https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70168

            Bug ID: 70168
           Summary: [5 Regression] Wrong code generation in
                    __sync_val_compare_and_swap on PowerPC
           Product: gcc
           Version: 5.4.0
            Status: UNCONFIRMED
          Severity: major
          Priority: P3
         Component: rtl-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: uweigand at gcc dot gnu.org
                CC: amodra at gcc dot gnu.org, dje at gcc dot gnu.org
  Target Milestone: ---
            Target: powerpc64le-linux

Building the following test case on powerpc64le-linux with -O2 using the
current GCC 5 branch:

unsigned long atomicAND (volatile unsigned long *memRef, unsigned long mask)
{
  unsigned long oldValue, newValue, prevValue;

  for (oldValue = *memRef; ; oldValue = prevValue)
    {
      newValue = oldValue & mask;
      if (newValue == oldValue)
        break;

      prevValue = __sync_val_compare_and_swap (memRef, oldValue, newValue);
      if (prevValue == oldValue)
        break;
    }

  return oldValue;
}

results in this assembler output:

atomicAND:
        ld 9,0(3)
        b .L6
        .p2align 4,,15
.L10:
        sync
.L3:
        ldarx 10,0,3
        cmpd 0,10,9
        bne- 0,.L4
        stdcx. 9,0,3
        bne- 0,.L3
.L4:
        isync
        cmpld 7,9,10
        beq 7,.L7
        mr 9,10
.L6:
        and 10,9,4
        cmpld 7,9,10
        bne 7,.L10
.L7:
        mr 3,10
        blr

Note how the stdcx. stores r9, which holds the original value, not the masked
value.  Therefore, this will usually succeed without updating the memory
location.

Debugging this problem, it turns out that rs6000_expand_atomic_compare_and_swap
is called with retval (operands[1]) equal to newval (operands[4]), and the
expander then proceeds to clobber newval before using it.

There is code to detect overlap of retval with oldval (operands[3]), but not
with newval.  Adding the equivalent detection code fixes the problem for me.

For some reason, I can reproduce this problem only with GCC 5; the same problem
should still be latently present in mainline since there's still no overlap
check, but I seem unable to construct a test case that would actually cause
retval == newval with mainline.

Reply via email to