This bug appears in ALL version of gcc that we tested since gcc 4.1.2

If you see the assembly generated by the cpp source using the
-DNOT_WORKING
option

you will see:
main:
        leal    4(%esp), %ecx
        andl    $-16, %esp
        pushl   -4(%ecx)
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %esi
        pushl   %ebx
        pushl   %ecx
        subl    $92, %esp
        movl    -20(%ebp), %eax
        movl    $0, -44(%ebp)
/*
Here is the bug. The compiler generates code to read
*location -16(%ebp) before anything was written to it!
*/
        movl    -16(%ebp), %edx
        leal    -44(%ebp), %esi
        movl    $0, -40(%ebp)
---------------------------------------------------------
Attached are 3 files:
(1) The cpp source
(2) an associated assembly file needed to link. That code is not
   the source of the bug since the bug is in the calling procedure
(3) A shell script exercising the bug and a workaround that masks
   the bug.

This is the code that produces the bug:
------------------------------------------------------------------------------
bug.cpp File (1)
------------------------------------------------------------------------------
#include <stdio.h>
#include <sys/types.h>

extern "C" {
    extern int64_t __cmpxchg8bytes(volatile int64_t *destination, volatile
int64_t exchange, volatile int64_t compare);
}

#if     defined(WORKING)

// there is no error by using the union of data
class TwoPointers
{
public:
    TwoPointers() { u1.s1.First = NULL; u1.s1.Second = NULL; }

    void* GetFirst() const     { return  u1.s1.First; } 
    void* GetSecond() const    { return  u1.s1.Second; } 
    void SetFirst(void* ptr)   {  u1.s1.First = ptr; } 
    void SetSecond(void* ptr)  {  u1.s1.Second = ptr; }

    union unionData {
        struct structData1 {
            void* volatile First;
            void* volatile Second;
        } s1;
        long long longlongData;
    } u1;
};

__inline TwoPointers InterlockedExchange(TwoPointers* Target, TwoPointers
Value)
{
    long long retVal = __cmpxchg8bytes(( long long *)Target, *((long long *)
(&Value.u1.longlongData)), *(long long *) (&Target->u1.longlongData) ) ; 
    return ( *(TwoPointers *) &retVal ) ;
}

#else   // defined(WORKING)

// this generates wrong code
class TwoPointers
{
public:
    TwoPointers() { First = NULL; Second = NULL; }

    void* GetFirst() const     { return First; } 
    void* GetSecond() const    { return Second; } 
    void SetFirst(void* ptr)   { First = ptr; } 
    void SetSecond(void* ptr)  { Second = ptr; }

    void* volatile First;
    void* volatile Second;
};

__inline TwoPointers InterlockedExchange(TwoPointers* Target, TwoPointers
Value)
{
    long long retVal = __cmpxchg8bytes(( long long *)Target, *((long long *)
(&Value)), *(long long *) (&Target) ) ; 
    return ( *(TwoPointers *) &retVal ) ;
}

#endif  // defined(WORKING)

int main()
{
    unsigned long long i = 0xdeaddeaddeaddeadll ;
    unsigned long long j = 0xbeafbeafbeafbeafll ;
    TwoPointers a ;
    TwoPointers b ;

    a.SetFirst( (void *) 0xdeaddead ) ;
    a.SetSecond( ( void *) 0xdeaddead ) ;

    b.SetFirst( (void *) 0xbeafbeaf ) ;
    b.SetSecond( (void *) 0xbeafbeaf ) ;

    InterlockedExchange( &a, b ) ;

    if ( a.GetFirst() == b.GetFirst() && a.GetSecond() == b.GetSecond() ) {
        printf("Succeeded\n");
    } else {
        printf("Failed\n");
        printf("It should be 0x%016llx\n", *( (long long *)&b) ) ;
        printf("Real data is 0x%016llx\n", *( (long long *)&a) ) ;
    }
}
-------------------------------------------------------------------------
i386xadd.s File (2)
-------------------------------------------------------------------------
/* BEGIN */
.global __cmpxchg8bytes
        .type __cmpxchg8bytes,@function
__cmpxchg8bytes:
        pushl %ebp
        movl %esp, %ebp
/*
* stack frame layout
* =============
* old (8 byes)
* new (8 bytes)
* ptr (4 bytes)
* rip (4 bytes)
* old ebp
* ============ # ebp and esp point here
*/

/*
* Save ecx, ebx CMPXCHG8B uses them. Save esi, this code uses it.
*/
        pushl %ecx
        pushl %ebx
        pushl %esi

/* move ptr to esi */
        movl 8(%ebp), %esi

/* move new to ecx:ebx; low order word at bottom, high word at top */
        movl 16(%ebp), %ecx
        movl 12(%ebp), %ebx

/* move old to edx:eax; low order word at bottom, high word at top */
        movl 24(%ebp), %edx
        movl 20(%ebp), %eax

/* magic word */
        lock cmpxchg8b (%esi)

/* restore regs and stack */
        popl %esi
        popl %ebx
        popl %ecx
        leave
        ret

/* END */
--------------------------------------------------------------------
File 3: runit.sh
This script demonstrates the bug
--------------------------------------------------------------------
#!/bin/sh

program="bug"
cmd_as="/usr/local/gcc-4.1.2/bin/gcc -O2 -o i386xadd.o -c i386xadd.s"
echo $cmd_as
$cmd_as

compiler="/usr/local/gcc-4.1.2/bin/g++"

for flags in NOT_WORKING WORKING
do

    echo
    echo "********** Compile and run with flag $flags turned on  **********"

    options=" -march=i686 -O3 i386xadd.o -lgcc -lgcc_s -lstdc++"
    cmd="$compiler $options -D$flags $program.cpp -o $program"
    echo $cmd
    $cmd

    echo
    ./$program
    echo

done


-- 
           Summary: Incorrect code generation when compiling c++ source
           Product: gcc
           Version: 4.3.3
            Status: UNCONFIRMED
          Severity: major
          Priority: P3
         Component: c++
        AssignedTo: unassigned at gcc dot gnu dot org
        ReportedBy: jacob at jacob dot remcomp dot fr


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40698

Reply via email to