0

I know this is hard to believe, but I am 100% serious about this.

When I compile the code below with Visual Studio 2008 Express Edition in Release mode on my MacBook Pro (Core 2 Duo P8600) with Windows XP Professional 32 bit SP3 running natively, run the executable, the printf is hit sporadically as soon as I touch the touchpad (no joke) - which should definitely never happen.

Can anybody reproduce the same problem on his MacBook Pro (or any other laptop)? Can anybody see in the assembly listing what might be the problem?

My guess is that the touchpad driver somehow manages to manipulate a register that is responsible for the floating point comparison. With integers, the problem does not occur.

Any idea what is going on here would be very welcome.

#include <stdio.h>

int main()
{
    while (true)
    {
        float x = 1.0f;

        for (int i = 0; i < 50; i++)
        {
            if (0.0f < x)
                x = 0.0f;
        }

        if (x == 1.0f)
            printf("bad: %.2f\n", x);
    }

    return 0;
}

Here is the assembly listing for the code above, produced by Visual Studio 2008 Express Edition:

; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00.30729.01 

    TITLE   c:\Dokumente und Einstellungen\azad\Desktop\WeirdProblem\main.cpp
    .686P
    .XMM
    include listing.inc
    .model  flat

INCLUDELIB MSVCRT
INCLUDELIB OLDNAMES

PUBLIC  ??_C@_0L@LNNNMCPH@bad?3?5?$CF?42f?6?$AA@    ; `string'
PUBLIC  __real@00000000
PUBLIC  __real@0000000000000000
PUBLIC  __real@3f800000
PUBLIC  _main
EXTRN   __imp__printf:PROC
EXTRN   __fltused:DWORD
;   COMDAT ??_C@_0L@LNNNMCPH@bad?3?5?$CF?42f?6?$AA@
; File c:\dokumente und einstellungen\azad\desktop\weirdproblem\main.cpp
CONST   SEGMENT
??_C@_0L@LNNNMCPH@bad?3?5?$CF?42f?6?$AA@ DB 'bad: %.2f', 0aH, 00H ; `string'
CONST   ENDS
;   COMDAT __real@00000000
CONST   SEGMENT
__real@00000000 DD 000000000r           ; 0
CONST   ENDS
;   COMDAT __real@0000000000000000
CONST   SEGMENT
__real@0000000000000000 DQ 00000000000000000r   ; 0
CONST   ENDS
;   COMDAT __real@3f800000
CONST   SEGMENT
__real@3f800000 DD 03f800000r           ; 1
; Function compile flags: /Ogtpy
CONST   ENDS
;   COMDAT _main
_TEXT   SEGMENT
_x$3834 = -4                        ; size = 4
_main   PROC                        ; COMDAT

; 4    : {

    push    ebp
    mov ebp, esp
    and esp, -64                ; ffffffc0H
    fld1
    sub esp, 60                 ; 0000003cH
    fldz
    push    esi
    fldz
    mov esi, DWORD PTR __imp__printf
    jmp SHORT $LN7@main
$LN43@main:

; 10   :        {
; 11   :            if (0.0f < x)
; 12   :                x = 0.0f;
; 13   :        }
; 14   : 
; 15   :        if (x == 1.0f)

    fstp    ST(0)
    fxch    ST(2)
$LN7@main:
    fxch    ST(2)
    mov ecx, 10                 ; 0000000aH
    fst DWORD PTR _x$3834[esp+64]
    fld DWORD PTR _x$3834[esp+64]
$LN5@main:
    fcom    ST(2)
    fnstsw  ax
    test    ah, 65                  ; 00000041H
    jne SHORT $LN4@main
    fstp    ST(0)
    fxch    ST(2)
    fst DWORD PTR _x$3834[esp+64]
    fld DWORD PTR _x$3834[esp+64]
    fxch    ST(1)
    fxch    ST(3)
    fxch    ST(1)
$LN4@main:
    fcom    ST(2)
    fnstsw  ax
    test    ah, 65                  ; 00000041H
    jne SHORT $LN14@main
    fstp    ST(0)
    fxch    ST(2)
    fst DWORD PTR _x$3834[esp+64]
    fld DWORD PTR _x$3834[esp+64]
    fxch    ST(1)
    fxch    ST(3)
    fxch    ST(1)
$LN14@main:
    fcom    ST(2)
    fnstsw  ax
    test    ah, 65                  ; 00000041H
    jne SHORT $LN15@main
    fstp    ST(0)
    fxch    ST(2)
    fst DWORD PTR _x$3834[esp+64]
    fld DWORD PTR _x$3834[esp+64]
    fxch    ST(1)
    fxch    ST(3)
    fxch    ST(1)
$LN15@main:
    fcom    ST(2)
    fnstsw  ax
    test    ah, 65                  ; 00000041H
    jne SHORT $LN16@main
    fstp    ST(0)
    fxch    ST(2)
    fst DWORD PTR _x$3834[esp+64]
    fld DWORD PTR _x$3834[esp+64]
    fxch    ST(1)
    fxch    ST(3)
    fxch    ST(1)
$LN16@main:
    fcom    ST(2)
    fnstsw  ax
    test    ah, 65                  ; 00000041H
    jne SHORT $LN17@main
    fstp    ST(0)
    fxch    ST(2)
    fst DWORD PTR _x$3834[esp+64]
    fld DWORD PTR _x$3834[esp+64]
    fxch    ST(1)
    fxch    ST(3)
    fxch    ST(1)
$LN17@main:

; 5    :    while (true)
; 6    :    {
; 7    :        float x = 1.0f;
; 8    : 
; 9    :        for (int i = 0; i < 50; i++)

    sub ecx, 1
    jne $LN5@main

; 10   :        {
; 11   :            if (0.0f < x)
; 12   :                x = 0.0f;
; 13   :        }
; 14   : 
; 15   :        if (x == 1.0f)

    fld ST(1)
    fucomp  ST(1)
    fnstsw  ax
    test    ah, 68                  ; 00000044H
    jp  $LN43@main
    fstp    ST(2)

; 16   :            printf("bad: %.2f\n", x);

    sub esp, 8
    fstp    ST(2)
    fstp    ST(1)
    fstp    QWORD PTR [esp]
    push    OFFSET ??_C@_0L@LNNNMCPH@bad?3?5?$CF?42f?6?$AA@
    call    esi

; 17   :    }

    fld1
    fldz
    add esp, 12                 ; 0000000cH
    fldz
    jmp $LN7@main
_main   ENDP
_TEXT   ENDS
END
Pedro
  • 842
  • 6
  • 16
  • 1
    I wouldn't call it malicious behaviour as the program isn't actively trying to do something wrong. Anyhow, I tested it on Linux with GCC 4.5.2 and the printf was never called. – Kasper Jan 05 '11 at 14:08
  • It's probably Microsoft's way of reminding you that you're running the wrong O/S on the right hardware. FWIW: the code didn't show any untoward behaviour when run on MacOS X 10.6.5 (Core 2 Duo chip), compiled with GCC 4.5.2. – Jonathan Leffler Jan 05 '11 at 14:41
  • Have you tried flicking the switch to 'more magic'? – derkyjadex Jan 05 '11 at 15:11
  • I run Mac OS X on my MacBook almost all the time. That's why I figured out this issue rather late, after almost two years. On Mac OS X 10.5.8, compiled with GCC, the same code runs fine even on my laptop. – Pedro Jan 05 '11 at 15:39
  • More likely than not it has to do with the way visual studio optimizes the loops. Can you post the generated assembly? – Beanz Jan 05 '11 at 22:20
  • I added the generated assembly (see above). – Pedro Jan 06 '11 at 00:09
  • If I had to guess, seems like it might be an issue with Windows and/or the driver. I wonder if the trackpad kernel driver is doing calculations that influence the floating point register(s) or their status causing incorrect FPU data to appear in the userland program when they are restored. This would reasonably explain what you are seeing, but proving that is the case would be a different matter. – Michael Petch Sep 07 '17 at 00:18

2 Answers2

1

I don't see how the touchpad driver would be able to produce this kind of behaviour.

My feeling is that this is a hardware issue of some kind. Have you overclocked your MacBook, by any chance? The CPU can start doing all sorts of strange things once you overclock it (Eric Raymond has some war stories to tell of this). If you're not overclocking, maybe your CPU is just getting too hot? Might be a good idea to check the cooling vents. Or maybe it's just a flaky CPU.

If it is the CPU, why does it happen only when you touch the touchpad? Pure speculation, but maybe the power drain from the touchpad lowers the CPU voltage just enough to cause it to do silly things...

Martin B
  • 23,670
  • 6
  • 53
  • 72
  • I haven't overclocked my MacBook. I don't think the CPU is getting too hot, since this happens also right after booting. It also doesn't happen under Mac OS X, so I would assume the CPU is ok. What also confuses me that if I compile the same code with Visual Studio 6, it does not happen. Interesting idea with the power drain lowering the CPU voltage. I wouldn't doubt it is something like that. The question is: Why does it happen only with a specific OS and a specific compiler? – Pedro Jan 05 '11 at 15:27
  • It isn't really *that* surprising that the touchpad driver could cause something like this - the touchpad would be one of very few devices where the driver would find a need for floating point arithmetic. – caf Jan 06 '11 at 03:55
  • 1
    It is reasonable that the touchpad driver uses the FPU. But for my understanding it should be impossible that a driver corrupts the state of the FPU for other tasks (e.g. user applications). I would say it must be one of the following: (1) a bug of the OS, (2) a compiler bug, or (3) a hardware bug. – Pedro Jan 06 '11 at 11:08
  • @Pedro: a driver bug that corrupts x87 state would explain it, but you'd expect that to break all kinds of other programs too. – Peter Cordes Oct 23 '18 at 20:24
1

May have something to do with FPU and optimizations. Does it happen if you define x as volatile float or compile with /O0? If it does, maybe buggy driver changes the state of the FPU.

StasM
  • 10,593
  • 6
  • 56
  • 103
  • 'volatile' makes it happen less often and /Od (means for VC 2008 that all optimizations are deactivated) reduces the frequency of occurrences to a minimum. Can a buggy driver change the state of the FPU, without the OS restoring the state after switching to the user application? – Pedro Jan 05 '11 at 21:47
  • well, it should not happen, in theory, but what you are seeing looks very much like it's happening. I do not know enough about Windows drivers to really suggest why... – StasM Jan 05 '11 at 21:53