Fix implementation of LAA for atomic update.
Patch by Divya Vyas (divyvyas@linux.vnet.ibm.com).
Part of fixing BZ #306035.
git-svn-id: svn://svn.valgrind.org/vex/trunk@2576 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/guest_s390_toIR.c b/priv/guest_s390_toIR.c
index 4d94281..566ad88 100644
--- a/priv/guest_s390_toIR.c
+++ b/priv/guest_s390_toIR.c
@@ -5425,9 +5425,16 @@
return "larl";
}
+/* The IR representation of LAA and friends is an approximation of what
+ happens natively. Essentially a loop containing a compare-and-swap is
+ constructed which will iterate until the CAS succeeds. As a consequence,
+ instrumenters may see more memory accesses than happen natively. See also
+ discussion here: https://bugs.kde.org/show_bug.cgi?id=306035 */
static const HChar *
s390_irgen_LAA(UChar r1, UChar r3, IRTemp op2addr)
{
+ IRCAS *cas;
+ IRTemp old_mem = newTemp(Ity_I32);
IRTemp op2 = newTemp(Ity_I32);
IRTemp op3 = newTemp(Ity_I32);
IRTemp result = newTemp(Ity_I32);
@@ -5435,9 +5442,22 @@
assign(op2, load(Ity_I32, mkexpr(op2addr)));
assign(op3, get_gpr_w1(r3));
assign(result, binop(Iop_Add32, mkexpr(op2), mkexpr(op3)));
+
+ /* Place the addition of second operand and third operand at the
+ second-operand location everytime */
+ cas = mkIRCAS(IRTemp_INVALID, old_mem,
+ Iend_BE, mkexpr(op2addr),
+ NULL, mkexpr(op2), /* expected value */
+ NULL, mkexpr(result) /* new value */);
+ stmt(IRStmt_CAS(cas));
+
+ /* Set CC according to 32-bit signed addition */
s390_cc_thunk_putSS(S390_CC_OP_SIGNED_ADD_32, op2, op3);
- store(mkexpr(op2addr), mkexpr(result));
- put_gpr_w1(r1, mkexpr(op2));
+
+ /* If old_mem contains the expected value, then the CAS succeeded.
+ Otherwise, it did not */
+ yield_if(binop(Iop_CmpNE32, mkexpr(old_mem), mkexpr(op2)));
+ put_gpr_w1(r1, mkexpr(old_mem));
return "laa";
}