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.