]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Support "compare double ansd swap" insns: CDS, CDSY, and CDSG
authorFlorian Krohm <florian@eich-krohm.de>
Wed, 6 Jun 2012 02:26:01 +0000 (02:26 +0000)
committerFlorian Krohm <florian@eich-krohm.de>
Wed, 6 Jun 2012 02:26:01 +0000 (02:26 +0000)
VEX bits for fixing bugzilla #291865

git-svn-id: svn://svn.valgrind.org/vex/trunk@2372

VEX/priv/guest_s390_toIR.c
VEX/priv/host_s390_defs.c
VEX/priv/host_s390_defs.h
VEX/priv/host_s390_isel.c

index 17d31b3ad280494a7fea64a03e7976070a29b2d9..96d632e497d91d24968c55588da585f3049ed10e 100644 (file)
@@ -9903,6 +9903,116 @@ s390_irgen_CSG(UChar r1, UChar r3, IRTemp op2addr)
    return "csg";
 }
 
+/* Implementation for 32-bit compare-double-and-swap */
+static void
+s390_irgen_cdas_32(UChar r1, UChar r3, IRTemp op2addr)
+{
+   IRCAS *cas;
+   IRTemp op1_high = newTemp(Ity_I32);
+   IRTemp op1_low  = newTemp(Ity_I32);
+   IRTemp old_mem_high = newTemp(Ity_I32);
+   IRTemp old_mem_low  = newTemp(Ity_I32);
+   IRTemp op3_high = newTemp(Ity_I32);
+   IRTemp op3_low  = newTemp(Ity_I32);
+   IRTemp result = newTemp(Ity_I32);
+   IRTemp nequal = newTemp(Ity_I1);
+
+   assign(op1_high, get_gpr_w1(r1));
+   assign(op1_low,  get_gpr_w1(r1+1));
+   assign(op3_high, get_gpr_w1(r3));
+   assign(op3_low,  get_gpr_w1(r3+1));
+
+   /* The first and second operands are compared. If they are equal,
+      the third operand is stored at the second-operand location. */
+   cas = mkIRCAS(old_mem_high, old_mem_low,
+                 Iend_BE, mkexpr(op2addr),
+                 mkexpr(op1_high), mkexpr(op1_low), /* expected value */
+                 mkexpr(op3_high), mkexpr(op3_low)  /* new value */);
+   stmt(IRStmt_CAS(cas));
+
+   /* Set CC. Operands compared equal -> 0, else 1. */
+   assign(result, unop(Iop_1Uto32,
+          binop(Iop_CmpNE32,
+                binop(Iop_Or32,
+                      binop(Iop_Xor32, mkexpr(op1_high), mkexpr(old_mem_high)),
+                      binop(Iop_Xor32, mkexpr(op1_low), mkexpr(old_mem_low))),
+                mkU32(0))));
+
+   s390_cc_thunk_put1(S390_CC_OP_BITWISE, result, False);
+
+   /* If operands were equal (cc == 0) just store the old value op1 in r1.
+      Otherwise, store the old_value from memory in r1 and yield. */
+   assign(nequal, binop(Iop_CmpNE32, s390_call_calculate_cc(), mkU32(0)));
+   put_gpr_w1(r1,   mkite(mkexpr(nequal), mkexpr(old_mem_high), mkexpr(op1_high)));
+   put_gpr_w1(r1+1, mkite(mkexpr(nequal), mkexpr(old_mem_low),  mkexpr(op1_low)));
+   stmt(IRStmt_Exit(mkexpr(nequal), Ijk_Yield,
+                    IRConst_U64(guest_IA_next_instr),
+                    S390X_GUEST_OFFSET(guest_IA)));
+}
+
+static HChar *
+s390_irgen_CDS(UChar r1, UChar r3, IRTemp op2addr)
+{
+   s390_irgen_cdas_32(r1, r3, op2addr);
+
+   return "cds";
+}
+
+static HChar *
+s390_irgen_CDSY(UChar r1, UChar r3, IRTemp op2addr)
+{
+   s390_irgen_cdas_32(r1, r3, op2addr);
+
+   return "cdsy";
+}
+
+static HChar *
+s390_irgen_CDSG(UChar r1, UChar r3, IRTemp op2addr)
+{
+   IRCAS *cas;
+   IRTemp op1_high = newTemp(Ity_I64);
+   IRTemp op1_low  = newTemp(Ity_I64);
+   IRTemp old_mem_high = newTemp(Ity_I64);
+   IRTemp old_mem_low  = newTemp(Ity_I64);
+   IRTemp op3_high = newTemp(Ity_I64);
+   IRTemp op3_low  = newTemp(Ity_I64);
+   IRTemp result = newTemp(Ity_I64);
+   IRTemp nequal = newTemp(Ity_I1);
+
+   assign(op1_high, get_gpr_dw0(r1));
+   assign(op1_low,  get_gpr_dw0(r1+1));
+   assign(op3_high, get_gpr_dw0(r3));
+   assign(op3_low,  get_gpr_dw0(r3+1));
+
+   /* The first and second operands are compared. If they are equal,
+      the third operand is stored at the second-operand location. */
+   cas = mkIRCAS(old_mem_high, old_mem_low,
+                 Iend_BE, mkexpr(op2addr),
+                 mkexpr(op1_high), mkexpr(op1_low), /* expected value */
+                 mkexpr(op3_high), mkexpr(op3_low)  /* new value */);
+   stmt(IRStmt_CAS(cas));
+
+   /* Set CC. Operands compared equal -> 0, else 1. */
+   assign(result, unop(Iop_1Uto64,
+          binop(Iop_CmpNE64,
+                binop(Iop_Or64,
+                      binop(Iop_Xor64, mkexpr(op1_high), mkexpr(old_mem_high)),
+                      binop(Iop_Xor64, mkexpr(op1_low), mkexpr(old_mem_low))),
+                mkU64(0))));
+
+   s390_cc_thunk_put1(S390_CC_OP_BITWISE, result, False);
+
+   /* If operands were equal (cc == 0) just store the old value op1 in r1.
+      Otherwise, store the old_value from memory in r1 and yield. */
+   assign(nequal, binop(Iop_CmpNE32, s390_call_calculate_cc(), mkU32(0)));
+   put_gpr_dw0(r1,   mkite(mkexpr(nequal), mkexpr(old_mem_high), mkexpr(op1_high)));
+   put_gpr_dw0(r1+1, mkite(mkexpr(nequal), mkexpr(old_mem_low),  mkexpr(op1_low)));
+   stmt(IRStmt_Exit(mkexpr(nequal), Ijk_Yield,
+                    IRConst_U64(guest_IA_next_instr),
+                    S390X_GUEST_OFFSET(guest_IA)));
+   return "cdsg";
+}
+
 
 /* Binary floating point */
 
@@ -12189,7 +12299,8 @@ s390_decode_4byte_and_irgen(UChar *bytes)
    case 0xb7: /* LCTL */ goto unimplemented;
    case 0xba: s390_format_RS_RRRD(s390_irgen_CS, ovl.fmt.RS.r1, ovl.fmt.RS.r3,
                                   ovl.fmt.RS.b2, ovl.fmt.RS.d2);  goto ok;
-   case 0xbb: /* CDS */ goto unimplemented;
+   case 0xbb: s390_format_RS_RRRD(s390_irgen_CDS, ovl.fmt.RS.r1, ovl.fmt.RS.r3,
+                                  ovl.fmt.RS.b2, ovl.fmt.RS.d2);  goto ok;
    case 0xbd: s390_format_RS_RURD(s390_irgen_CLM, ovl.fmt.RS.r1, ovl.fmt.RS.r3,
                                   ovl.fmt.RS.b2, ovl.fmt.RS.d2);  goto ok;
    case 0xbe: s390_format_RS_RURD(s390_irgen_STCM, ovl.fmt.RS.r1, ovl.fmt.RS.r3,
@@ -12818,8 +12929,14 @@ s390_decode_6byte_and_irgen(UChar *bytes)
                                                 ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
                                                 ovl.fmt.RSY.dl2,
                                                 ovl.fmt.RSY.dh2);  goto ok;
-   case 0xeb0000000031ULL: /* CDSY */ goto unimplemented;
-   case 0xeb000000003eULL: /* CDSG */ goto unimplemented;
+   case 0xeb0000000031ULL: s390_format_RSY_RRRD(s390_irgen_CDSY, ovl.fmt.RSY.r1,
+                                                ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
+                                                ovl.fmt.RSY.dl2,
+                                                ovl.fmt.RSY.dh2);  goto ok;
+   case 0xeb000000003eULL: s390_format_RSY_RRRD(s390_irgen_CDSG, ovl.fmt.RSY.r1,
+                                                ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
+                                                ovl.fmt.RSY.dl2,
+                                                ovl.fmt.RSY.dh2);  goto ok;
    case 0xeb0000000044ULL: s390_format_RSY_RRRD(s390_irgen_BXHG, ovl.fmt.RSY.r1,
                                                 ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
                                                 ovl.fmt.RSY.dl2,
index 99f5f34cd040a4c2f4a4a1d088e1733c32185694..e1b4614168aa29734a9ea71b1a022aee8836189f 100644 (file)
@@ -610,6 +610,17 @@ s390_insn_get_reg_usage(HRegUsage *u, const s390_insn *insn)
       addHRegUse(u, HRmWrite,  insn->variant.cas.old_mem);
       break;
 
+   case S390_INSN_CDAS:
+      addHRegUse(u, HRmRead,  insn->variant.cdas.op1_high);
+      addHRegUse(u, HRmRead,  insn->variant.cdas.op1_low);
+      s390_amode_get_reg_usage(u, insn->variant.cas.op2);
+      addHRegUse(u, HRmRead,  insn->variant.cdas.op3_high);
+      addHRegUse(u, HRmRead,  insn->variant.cdas.op3_low);
+      addHRegUse(u, HRmWrite, insn->variant.cdas.old_mem_high);
+      addHRegUse(u, HRmWrite, insn->variant.cdas.old_mem_low);
+      addHRegUse(u, HRmWrite, insn->variant.cdas.scratch);
+      break;
+
    case S390_INSN_COMPARE:
       addHRegUse(u, HRmRead, insn->variant.compare.src1);
       s390_opnd_RMI_get_reg_usage(u, insn->variant.compare.src2);
@@ -839,6 +850,17 @@ s390_insn_map_regs(HRegRemap *m, s390_insn *insn)
       insn->variant.cas.old_mem = lookupHRegRemap(m, insn->variant.cas.old_mem);
       break;
 
+   case S390_INSN_CDAS:
+      insn->variant.cdas.op1_high = lookupHRegRemap(m, insn->variant.cdas.op1_high);
+      insn->variant.cdas.op1_low  = lookupHRegRemap(m, insn->variant.cdas.op1_low);
+      s390_amode_map_regs(m, insn->variant.cdas.op2);
+      insn->variant.cdas.op3_high = lookupHRegRemap(m, insn->variant.cdas.op3_high);
+      insn->variant.cdas.op3_low  = lookupHRegRemap(m, insn->variant.cdas.op3_low);
+      insn->variant.cdas.old_mem_high = lookupHRegRemap(m, insn->variant.cdas.old_mem_high);
+      insn->variant.cdas.old_mem_low  = lookupHRegRemap(m, insn->variant.cdas.old_mem_low);
+      insn->variant.cdas.scratch  = lookupHRegRemap(m, insn->variant.cdas.scratch);
+      break;
+
    case S390_INSN_COMPARE:
       insn->variant.compare.src1 = lookupHRegRemap(m, insn->variant.compare.src1);
       s390_opnd_RMI_map_regs(m, &insn->variant.compare.src2);
@@ -1573,6 +1595,40 @@ s390_emit_CSG(UChar *p, UChar r1, UChar r3, UChar b2, UShort dl2, UChar dh2)
 }
 
 
+static UChar *
+s390_emit_CDS(UChar *p, UChar r1, UChar r3, UChar b2, UShort d2)
+{
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM))
+      s390_disasm(ENC4(MNM, GPR, GPR, UDXB), "cds", r1, r3, d2, 0, b2);
+
+   return emit_RS(p, 0xbb000000, r1, r3, b2, d2);
+}
+
+
+static UChar *
+s390_emit_CDSY(UChar *p, UChar r1, UChar r3, UChar b2, UShort dl2, UChar dh2)
+{
+   vassert(s390_host_has_ldisp);
+
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM))
+      s390_disasm(ENC4(MNM, GPR, GPR, SDXB), "cdsy", r1, r3, dh2, dl2, 0, b2);
+
+   return emit_RSY(p, 0xeb0000000031ULL, r1, r3, b2, dl2, dh2);
+}
+
+
+static UChar *
+s390_emit_CDSG(UChar *p, UChar r1, UChar r3, UChar b2, UShort dl2, UChar dh2)
+{
+   vassert(s390_host_has_ldisp || dh2 == 0);
+
+   if (UNLIKELY(vex_traceflags & VEX_TRACE_ASM))
+      s390_disasm(ENC4(MNM, GPR, GPR, SDXB), "cdsg", r1, r3, dh2, dl2, 0, b2);
+
+   return emit_RSY(p, 0xeb000000003eULL, r1, r3, b2, dl2, dh2);
+}
+
+
 static UChar *
 s390_emit_CLR(UChar *p, UChar r1, UChar r2)
 {
@@ -4316,6 +4372,32 @@ s390_insn_cas(UChar size, HReg op1, s390_amode *op2, HReg op3, HReg old_mem)
 }
 
 
+s390_insn *
+s390_insn_cdas(UChar size, HReg op1_high, HReg op1_low, s390_amode *op2,
+               HReg op3_high, HReg op3_low, HReg old_mem_high, HReg old_mem_low,
+               HReg scratch)
+{
+   s390_insn *insn = LibVEX_Alloc(sizeof(s390_insn));
+
+   vassert(size == 4 || size == 8);
+   vassert(op2->x == 0);
+   vassert(hregNumber(scratch) == 1);  /* r0,r1 used as scratch reg pair */
+
+   insn->tag  = S390_INSN_CDAS;
+   insn->size = size;
+   insn->variant.cdas.op1_high = op1_high;
+   insn->variant.cdas.op1_low  = op1_low;
+   insn->variant.cdas.op2 = op2;
+   insn->variant.cdas.op3_high = op3_high;
+   insn->variant.cdas.op3_low  = op3_low;
+   insn->variant.cdas.old_mem_high = old_mem_high;
+   insn->variant.cdas.old_mem_low  = old_mem_low;
+   insn->variant.cdas.scratch = scratch;
+
+   return insn;
+}
+
+
 s390_insn *
 s390_insn_compare(UChar size, HReg src1, s390_opnd_RMI src2,
                   Bool signed_comparison)
@@ -4933,6 +5015,14 @@ s390_insn_as_string(const s390_insn *insn)
                    insn->variant.cas.old_mem);
       break;
 
+   case S390_INSN_CDAS:
+      s390_sprintf(buf, "%M %R,%R,%A,%R,%R,%R,%R", "v-cdas",
+                   insn->variant.cdas.op1_high, insn->variant.cdas.op1_low,
+                   insn->variant.cdas.op2, insn->variant.cdas.op3_high,
+                   insn->variant.cdas.op3_low, insn->variant.cdas.old_mem_high,
+                   insn->variant.cdas.old_mem_low);
+      break;
+
    case S390_INSN_COMPARE:
       if (insn->variant.compare.signed_comparison) {
          op = "v-cmps";
@@ -6261,6 +6351,66 @@ s390_insn_cas_emit(UChar *buf, const s390_insn *insn)
 }
 
 
+/* Only 4-byte and 8-byte operands are handled. */
+static UChar *
+s390_insn_cdas_emit(UChar *buf, const s390_insn *insn)
+{
+   UChar r1, r1p1, r3, r3p1, b, old_high, old_low, scratch;
+   Int d;
+   s390_amode *am;
+
+   r1   = hregNumber(insn->variant.cdas.op1_high); /* expected value */
+   r1p1 = hregNumber(insn->variant.cdas.op1_low);  /* expected value */
+   r3   = hregNumber(insn->variant.cdas.op3_high);
+   r3p1 = hregNumber(insn->variant.cdas.op3_low);
+   old_high = hregNumber(insn->variant.cdas.old_mem_high);
+   old_low  = hregNumber(insn->variant.cdas.old_mem_low);
+   scratch  = hregNumber(insn->variant.cdas.scratch);
+   am = insn->variant.cdas.op2;
+   b  = hregNumber(am->b);
+   d  = am->d;
+
+   vassert(scratch == 1);
+
+   switch (insn->size) {
+   case 4:
+      /* r1, r1+1 must not be overwritten. So copy them to R0,scratch
+         and let CDS/CDSY clobber it */
+      buf = s390_emit_LR(buf, R0, r1);
+      buf = s390_emit_LR(buf, scratch, r1p1);
+
+      if (am->tag == S390_AMODE_B12)
+         buf = s390_emit_CDS(buf, R0, r3, b, d);
+      else
+         buf = s390_emit_CDSY(buf, R0, r3, b, DISP20(d));
+
+      /* Now copy R0,scratch which has the old memory value to OLD */
+      buf = s390_emit_LR(buf, old_high, R0);
+      buf = s390_emit_LR(buf, old_low,  scratch);
+      return buf;
+
+   case 8:
+      /* r1, r1+1 must not be overwritten. So copy them to R0,scratch
+         and let CDSG clobber it */
+      buf = s390_emit_LGR(buf, R0, r1);
+      buf = s390_emit_LGR(buf, scratch, r1p1);
+
+      buf = s390_emit_CDSG(buf, R0, r3, b, DISP20(d));
+
+      /* Now copy R0,scratch which has the old memory value to OLD */
+      buf = s390_emit_LGR(buf, old_high, R0);
+      buf = s390_emit_LGR(buf, old_low,  scratch);
+      return buf;
+
+   default:
+      goto fail;
+   }
+
+ fail:
+   vpanic("s390_insn_cas_emit");
+}
+
+
 /* Only 4-byte and 8-byte comparisons are handled. 1-byte and 2-byte
    comparisons will have been converted to 4-byte comparisons in
    s390_isel_cc and should not occur here. */
@@ -7765,6 +7915,10 @@ emit_S390Instr(Bool *is_profinc, UChar *buf, Int nbuf, s390_insn *insn,
       end = s390_insn_cas_emit(buf, insn);
       break;
 
+   case S390_INSN_CDAS:
+      end = s390_insn_cdas_emit(buf, insn);
+      break;
+
    case S390_INSN_COMPARE:
       end = s390_insn_compare_emit(buf, insn);
       break;
index daa90ce589a88ffe438fb58c70fdab8bf139cbf4..74bf0bbd2485157e56fa40fb573a42d90cd7cf13 100644 (file)
@@ -132,6 +132,7 @@ typedef enum {
    S390_INSN_COMPARE,
    S390_INSN_HELPER_CALL,
    S390_INSN_CAS,    /* compare and swap */
+   S390_INSN_CDAS,   /* compare double and swap */
    S390_INSN_BFP_BINOP, /* Binary floating point 32-bit / 64-bit */
    S390_INSN_BFP_UNOP,
    S390_INSN_BFP_TRIOP,
@@ -343,6 +344,16 @@ typedef struct {
          HReg        op3;
          HReg        old_mem;
       } cas;
+      struct {
+         HReg        op1_high;
+         HReg        op1_low;
+         s390_amode *op2;
+         HReg        op3_high;
+         HReg        op3_low;
+         HReg        old_mem_high;
+         HReg        old_mem_low;
+         HReg        scratch;
+      } cdas;
       /* Pseudo-insn for representing a helper call.
          TARGET is the absolute address of the helper function
          NUM_ARGS says how many arguments are being passed.
@@ -466,6 +477,9 @@ s390_insn *s390_insn_clz(UChar size, HReg num_bits, HReg clobber,
                          s390_opnd_RMI op);
 s390_insn *s390_insn_cas(UChar size, HReg op1, s390_amode *op2, HReg op3,
                          HReg old);
+s390_insn *s390_insn_cdas(UChar size, HReg op1_high, HReg op1_low,
+                          s390_amode *op2, HReg op3_high, HReg op3_low,
+                          HReg old_high, HReg old_low, HReg scratch);
 s390_insn *s390_insn_unop(UChar size, s390_unop_t tag, HReg dst,
                           s390_opnd_RMI opnd);
 s390_insn *s390_insn_cc2bool(HReg dst, s390_cc_t src);
index e0ec92b2f6d6899f617b71cf545938c264eaf352..ffae34d8127461bce458dfcd3cf957bf2857d289 100644 (file)
@@ -2469,7 +2469,45 @@ s390_isel_stmt(ISelEnv *env, IRStmt *stmt)
          }
          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;