Support "compare double ansd swap" insns: CDS, CDSY, and CDSG
VEX bits for fixing bugzilla #291865


git-svn-id: svn://svn.valgrind.org/vex/trunk@2372 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/host_s390_isel.c b/priv/host_s390_isel.c
index e0ec92b..ffae34d 100644
--- a/priv/host_s390_isel.c
+++ b/priv/host_s390_isel.c
@@ -2469,7 +2469,45 @@
          }
          return;
       } else {
-         vpanic("compare double and swap not implemented\n");
+         IRCAS *cas = stmt->Ist.CAS.details;
+         s390_amode *op2 = s390_isel_amode(env,  cas->addr);
+         HReg r8, r9, r10, r11, r1;
+         HReg op3_high = s390_isel_int_expr(env, cas->dataHi);  /* new value */
+         HReg op3_low  = s390_isel_int_expr(env, cas->dataLo);  /* new value */
+         HReg op1_high = s390_isel_int_expr(env, cas->expdHi);  /* expected value */
+         HReg op1_low  = s390_isel_int_expr(env, cas->expdLo);  /* expected value */
+         HReg old_low  = lookupIRTemp(env, cas->oldLo);
+         HReg old_high = lookupIRTemp(env, cas->oldHi);
+
+         /* Use non-virtual registers r8 and r9 as pair for op1
+            and move op1 there */
+         r8 = make_gpr(8);
+         r9 = make_gpr(9);
+         addInstr(env, s390_insn_move(8, r8, op1_high));
+         addInstr(env, s390_insn_move(8, r9, op1_low));
+
+         /* Use non-virtual registers r10 and r11 as pair for op3
+            and move op3 there */
+         r10 = make_gpr(10);
+         r11 = make_gpr(11);
+         addInstr(env, s390_insn_move(8, r10, op3_high));
+         addInstr(env, s390_insn_move(8, r11, op3_low));
+
+         /* Register r1 is used as a scratch register */
+         r1 = make_gpr(1);
+
+         if (typeOfIRTemp(env->type_env, cas->oldLo) == Ity_I32) {
+            addInstr(env, s390_insn_cdas(4, r8, r9, op2, r10, r11,
+                                         old_high, old_low, r1));
+         } else {
+            addInstr(env, s390_insn_cdas(8, r8, r9, op2, r10, r11,
+                                         old_high, old_low, r1));
+         }
+         addInstr(env, s390_insn_move(8, op1_high, r8));
+         addInstr(env, s390_insn_move(8, op1_low,  r9));
+         addInstr(env, s390_insn_move(8, op3_high, r10));
+         addInstr(env, s390_insn_move(8, op3_low,  r11));
+         return;
       }
       break;