Support conditional load and store for s390x (VEX side).
Fixes #269209.  (Christian Borntraeger, borntraeger@de.ibm.com)


git-svn-id: svn://svn.valgrind.org/vex/trunk@2121 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/guest_s390_toIR.c b/priv/guest_s390_toIR.c
index fdd2cf4..4537fc5 100644
--- a/priv/guest_s390_toIR.c
+++ b/priv/guest_s390_toIR.c
@@ -1564,6 +1564,16 @@
 }
 
 static void
+s390_format_RRF_U0RR(HChar *(*irgen)(UChar m3, UChar r1, UChar r2),
+                     UChar m3, UChar r1, UChar r2, Int xmnm_kind)
+{
+   irgen(m3, r1, r2);
+
+   if (unlikely(vex_traceflags & VEX_TRACE_FE))
+      s390_disasm(ENC3(XMNM, GPR, GPR), xmnm_kind, m3, r1, r2);
+}
+
+static void
 s390_format_RRF_U0RF(HChar *(*irgen)(UChar r3, UChar r1, UChar r2),
                      UChar r3, UChar r1, UChar r2)
 {
@@ -1739,6 +1749,26 @@
 }
 
 static void
+s390_format_RSY_RDRM(HChar *(*irgen)(UChar r1, IRTemp op2addr),
+                     UChar r1, UChar m3, UChar b2, UShort dl2, UChar dh2,
+                     Int xmnm_kind)
+{
+   IRTemp op2addr = newTemp(Ity_I64);
+   IRTemp d2 = newTemp(Ity_I64);
+
+   if_condition_goto(binop(Iop_CmpEQ32, s390_call_calculate_cond(m3), mkU32(0)),
+                     guest_IA_next_instr);
+   assign(d2, mkU64(((ULong)(Long)(Char)dh2 << 12) | ((ULong)dl2)));
+   assign(op2addr, binop(Iop_Add64, mkexpr(d2), b2 != 0 ? get_gpr_dw0(b2) :
+          mkU64(0)));
+
+   irgen(r1, op2addr);
+
+   if (unlikely(vex_traceflags & VEX_TRACE_FE))
+      s390_disasm(ENC3(XMNM, GPR, SDXB), xmnm_kind, m3, r1, dh2, dl2, 0, b2);
+}
+
+static void
 s390_format_RX(HChar *(*irgen)(UChar r1, UChar x2, UChar b2, UShort d2,
                IRTemp op2addr),
                UChar r1, UChar x2, UChar b2, UShort d2)
@@ -5755,6 +5785,44 @@
 }
 
 static HChar *
+s390_irgen_LOCR(UChar m3, UChar r1, UChar r2)
+{
+   if_condition_goto(binop(Iop_CmpEQ32, s390_call_calculate_cond(m3), mkU32(0)),
+                     guest_IA_next_instr);
+   put_gpr_w1(r1, get_gpr_w1(r2));
+
+   return "locr";
+}
+
+static HChar *
+s390_irgen_LOCGR(UChar m3, UChar r1, UChar r2)
+{
+   if_condition_goto(binop(Iop_CmpEQ32, s390_call_calculate_cond(m3), mkU32(0)),
+                     guest_IA_next_instr);
+   put_gpr_dw0(r1, get_gpr_dw0(r2));
+
+   return "locgr";
+}
+
+static HChar *
+s390_irgen_LOC(UChar r1, IRTemp op2addr)
+{
+   /* condition is checked in format handler */
+   put_gpr_w1(r1, load(Ity_I32, mkexpr(op2addr)));
+
+   return "loc";
+}
+
+static HChar *
+s390_irgen_LOCG(UChar r1, IRTemp op2addr)
+{
+   /* condition is checked in format handler */
+   put_gpr_dw0(r1, load(Ity_I64, mkexpr(op2addr)));
+
+   return "locg";
+}
+
+static HChar *
 s390_irgen_LPQ(UChar r1, IRTemp op2addr)
 {
    put_gpr_dw0(r1, load(Ity_I64, mkexpr(op2addr)));
@@ -7164,6 +7232,24 @@
 }
 
 static HChar *
+s390_irgen_STOC(UChar r1, IRTemp op2addr)
+{
+   /* condition is checked in format handler */
+   store(mkexpr(op2addr), get_gpr_w1(r1));
+
+   return "stoc";
+}
+
+static HChar *
+s390_irgen_STOCG(UChar r1, IRTemp op2addr)
+{
+   /* condition is checked in format handler */
+   store(mkexpr(op2addr), get_gpr_dw0(r1));
+
+   return "stocg";
+}
+
+static HChar *
 s390_irgen_STPQ(UChar r1, IRTemp op2addr)
 {
    store(mkexpr(op2addr), get_gpr_dw0(r1));
@@ -11276,7 +11362,9 @@
    case 0xb9df: s390_format_RRE_RR(s390_irgen_CLHLR, ovl.fmt.RRE.r1,
                                    ovl.fmt.RRE.r2);  goto ok;
    case 0xb9e1: /* POPCNT */ goto unimplemented;
-   case 0xb9e2: /* LOCGR */ goto unimplemented;
+   case 0xb9e2: s390_format_RRF_U0RR(s390_irgen_LOCGR, ovl.fmt.RRF3.r3,
+                                     ovl.fmt.RRF3.r1, ovl.fmt.RRF3.r2,
+                                     S390_XMNM_LOCGR);  goto ok;
    case 0xb9e4: s390_format_RRF_R0RR2(s390_irgen_NGRK, ovl.fmt.RRF4.r3,
                                       ovl.fmt.RRF4.r1, ovl.fmt.RRF4.r2);
                                       goto ok;
@@ -11298,7 +11386,9 @@
    case 0xb9eb: s390_format_RRF_R0RR2(s390_irgen_SLGRK, ovl.fmt.RRF4.r3,
                                       ovl.fmt.RRF4.r1, ovl.fmt.RRF4.r2);
                                       goto ok;
-   case 0xb9f2: /* LOCR */ goto unimplemented;
+   case 0xb9f2: s390_format_RRF_U0RR(s390_irgen_LOCR, ovl.fmt.RRF3.r3,
+                                     ovl.fmt.RRF3.r1, ovl.fmt.RRF3.r2,
+                                     S390_XMNM_LOCR);  goto ok;
    case 0xb9f4: s390_format_RRF_R0RR2(s390_irgen_NRK, ovl.fmt.RRF4.r3,
                                       ovl.fmt.RRF4.r1, ovl.fmt.RRF4.r2);
                                       goto ok;
@@ -12185,8 +12275,16 @@
                                                 ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
                                                 ovl.fmt.RSY.dl2,
                                                 ovl.fmt.RSY.dh2);  goto ok;
-   case 0xeb00000000e2ULL: /* LOCG */ goto unimplemented;
-   case 0xeb00000000e3ULL: /* STOCG */ goto unimplemented;
+   case 0xeb00000000e2ULL: s390_format_RSY_RDRM(s390_irgen_LOCG, ovl.fmt.RSY.r1,
+                                                ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
+                                                ovl.fmt.RSY.dl2,
+                                                ovl.fmt.RSY.dh2,
+                                                S390_XMNM_LOCG);  goto ok;
+   case 0xeb00000000e3ULL: s390_format_RSY_RDRM(s390_irgen_STOCG,
+                                                ovl.fmt.RSY.r1, ovl.fmt.RSY.r3,
+                                                ovl.fmt.RSY.b2, ovl.fmt.RSY.dl2,
+                                                ovl.fmt.RSY.dh2,
+                                                S390_XMNM_STOCG);  goto ok;
    case 0xeb00000000e4ULL: s390_format_RSY_RRRD(s390_irgen_LANG, ovl.fmt.RSY.r1,
                                                 ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
                                                 ovl.fmt.RSY.dl2,
@@ -12207,8 +12305,16 @@
                                                 ovl.fmt.RSY.r1, ovl.fmt.RSY.r3,
                                                 ovl.fmt.RSY.b2, ovl.fmt.RSY.dl2,
                                                 ovl.fmt.RSY.dh2);  goto ok;
-   case 0xeb00000000f2ULL: /* LOC */ goto unimplemented;
-   case 0xeb00000000f3ULL: /* STOC */ goto unimplemented;
+   case 0xeb00000000f2ULL: s390_format_RSY_RDRM(s390_irgen_LOC, ovl.fmt.RSY.r1,
+                                                ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
+                                                ovl.fmt.RSY.dl2,
+                                                ovl.fmt.RSY.dh2, S390_XMNM_LOC);
+                                                goto ok;
+   case 0xeb00000000f3ULL: s390_format_RSY_RDRM(s390_irgen_STOC, ovl.fmt.RSY.r1,
+                                                ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
+                                                ovl.fmt.RSY.dl2,
+                                                ovl.fmt.RSY.dh2,
+                                                S390_XMNM_STOC);  goto ok;
    case 0xeb00000000f4ULL: s390_format_RSY_RRRD(s390_irgen_LAN, ovl.fmt.RSY.r1,
                                                 ovl.fmt.RSY.r3, ovl.fmt.RSY.b2,
                                                 ovl.fmt.RSY.dl2,
diff --git a/priv/host_s390_disasm.c b/priv/host_s390_disasm.c
index 62de0c3..35f1010 100644
--- a/priv/host_s390_disasm.c
+++ b/priv/host_s390_disasm.c
@@ -121,31 +121,52 @@
    return buf;
 }
 
+/* Common function used to construct a mnemonic based on a condition code
+   mask. */
+static const HChar *
+construct_mnemonic(const HChar *prefix, const HChar *suffix, UInt mask)
+{
+   HChar *to;
+   const HChar *from;
+
+   static HChar buf[10];
+
+   static HChar mask_id[16][4] = {
+      "", /* 0 -> unused */
+      "o", "h", "nle", "l", "nhe", "lh", "ne",
+      "e", "nlh", "he", "nl", "le", "nh", "no",
+      ""  /* 15 -> unused */
+   };
+
+   /* Guard against buffer overflow */
+   vassert(vex_strlen(prefix) + vex_strlen(suffix) + sizeof mask_id[0] <= sizeof buf);
+
+   /* strcpy(buf, prefix); */
+   for (from = prefix, to = buf; *from; ++from, ++to) {
+      *to = *from;
+   }
+   /* strcat(buf, mask_id); */
+   for (from = mask_id[mask]; *from; ++from, ++to) {
+      *to = *from;
+   }
+   /* strcat(buf, suffix); */
+   for (from = suffix; *from; ++from, ++to) {
+      *to = *from;
+   }
+   *to = '\0';
+
+   return buf;
+}
+
 
 /* Return the special mnemonic for the BCR opcode */
 static const HChar *
 bcr_operand(UInt m1)
 {
-   static const HChar mnemonic[16][6] = {
-      /* 0 */ "nopr",  /* no operation */
-      /* 1 */ "bor",   /* branch on overflow / if ones */
-      /* 2 */ "bhr",   /* branch on high */
-      /* 3 */ "bnler", /* branch on not low or equal */
-      /* 4 */ "blr",   /* branch on low */
-      /* 5 */ "bnher", /* branch on not high or equal */
-      /* 6 */ "blhr",  /* branch on low or high */
-      /* 7 */ "bner",  /* branch on not equal */
-      /* 8 */ "ber",   /* branch on equal */
-      /* 9 */ "bnlhr", /* branch on not low or high */
-      /* a */ "bher",  /* branch on high or equal */
-      /* b */ "bnlr",  /* branch on not low */
-      /* c */ "bler",  /* brach on low or equal */
-      /* d */ "bnhr",  /* branch on not high */
-      /* e */ "bnor",  /* branch on not overflow / if not ones */
-      /* f */ "br",    /* unconditional branch */
-   };
+   if (m1 ==  0) return "nopr";
+   if (m1 == 15) return "br";
 
-   return mnemonic[m1];
+   return construct_mnemonic("b", "r", m1);
 }
 
 
@@ -153,26 +174,10 @@
 static const HChar *
 bc_operand(UInt m1)
 {
-   static const HChar mnemonic[16][5] = {
-      /* 0 */ "nop",  // no operation
-      /* 1 */ "bo",   // branch on overflow / if ones
-      /* 2 */ "bh",   // branch on high
-      /* 3 */ "bnle", // branch on not low or equal
-      /* 4 */ "bl",   // branch on low
-      /* 5 */ "bnhe", // branch on not high or equal
-      /* 6 */ "blh",  // branch on low or high
-      /* 7 */ "bne",  // branch on not equal
-      /* 8 */ "be",   // branch on equal
-      /* 9 */ "bnlh", // branch on not low or high
-      /* a */ "bhe",  // branch on high or equal
-      /* b */ "bnl",  // branch on not low
-      /* c */ "ble",  // branch on low or equal
-      /* d */ "bnh",  // branch on not high
-      /* e */ "bno",  // branch on not overflow / if not ones
-      /* f */ "b"     // unconditional branch
-   };
+   if (m1 ==  0) return "nop";
+   if (m1 == 15) return "b";
 
-   return mnemonic[m1];
+   return construct_mnemonic("b", "", m1);
 }
 
 
@@ -180,26 +185,10 @@
 static const HChar *
 brc_operand(UInt m1)
 {
-   static const HChar mnemonic[16][5] = {
-      /* 0 */ "brc",  /* no special mnemonic */
-      /* 1 */ "jo",   /* jump on overflow / if ones */
-      /* 2 */ "jh",   /* jump on A high */
-      /* 3 */ "jnle", /* jump on not low or equal */
-      /* 4 */ "jl",   /* jump on A low */
-      /* 5 */ "jnhe", /* jump on not high or equal */
-      /* 6 */ "jlh",  /* jump on low or high */
-      /* 7 */ "jne",  /* jump on A not equal B */
-      /* 8 */ "je",   /* jump on A equal B */
-      /* 9 */ "jnlh", /* jump on not low or high */
-      /* a */ "jhe",  /* jump on high or equal */
-      /* b */ "jnl",  /* jump on A not low */
-      /* c */ "jle",  /* jump on low or equal */
-      /* d */ "jnh",  /* jump on A not high */
-      /* e */ "jno",  /* jump on not overflow / if not ones */
-      /* f */ "j",    /* jump */
-   };
+   if (m1 == 0)  return "brc";
+   if (m1 == 15) return "j";
 
-   return mnemonic[m1];
+   return construct_mnemonic("j", "", m1);
 }
 
 
@@ -207,26 +196,31 @@
 static const HChar *
 brcl_operand(UInt m1)
 {
-   static const HChar mnemonic[16][6] = {
-      /* 0 */ "brcl",  /* no special mnemonic */
-      /* 1 */ "jgo",   /* jump long on overflow / if ones */
-      /* 2 */ "jgh",   /* jump long on high */
-      /* 3 */ "jgnle", /* jump long on not low or equal */
-      /* 4 */ "jgl",   /* jump long on low */
-      /* 5 */ "jgnhe", /* jump long on not high or equal */
-      /* 6 */ "jglh",  /* jump long on low or high */
-      /* 7 */ "jgne",  /* jump long on not equal */
-      /* 8 */ "jge",   /* jump long on equal */
-      /* 9 */ "jgnlh", /* jump long on not low or high */
-      /* a */ "jghe",  /* jump long on high or equal */
-      /* b */ "jgnl",  /* jump long on not low */
-      /* c */ "jgle",  /* jump long on low or equal */
-      /* d */ "jgnh",  /* jump long on not high */
-      /* e */ "jgno",  /* jump long on not overflow / if not ones */
-      /* f */ "jg",    /* jump long */
-   };
+   if (m1 == 0)  return "brcl";
+   if (m1 == 15) return "jg";
 
-   return mnemonic[m1];
+   return construct_mnemonic("jg", "", m1);
+}
+
+
+/* Return the special mnemonic for a conditional load/store  opcode */
+static const HChar *
+cls_operand(Int kind, UInt mask)
+{
+   HChar *prefix;
+
+   switch (kind) {
+   case S390_XMNM_LOCR:   prefix = "locr";  break;
+   case S390_XMNM_LOCGR:  prefix = "locgr"; break;
+   case S390_XMNM_LOC:    prefix = "loc";   break;
+   case S390_XMNM_LOCG:   prefix = "locg";  break;
+   case S390_XMNM_STOC:   prefix = "stoc";  break;
+   case S390_XMNM_STOCG:  prefix = "stocg"; break;
+   default:
+      vpanic("cls_operand");
+   }
+
+   return construct_mnemonic(prefix, "", mask);
 }
 
 
@@ -291,6 +285,7 @@
    HChar buf[128];  /* holds the disassembled insn */
    HChar *p;
    HChar separator;
+   Int mask_suffix = -1;
 
    va_start(args, command);
 
@@ -350,6 +345,20 @@
             mask = va_arg(args, UInt);
             p  += vex_sprintf(p, s390_mnm_fmt, cab_operand(mnm, mask));
             break;
+
+         case S390_XMNM_LOCR:
+         case S390_XMNM_LOCGR:
+         case S390_XMNM_LOC:
+         case S390_XMNM_LOCG:
+         case S390_XMNM_STOC:
+         case S390_XMNM_STOCG:
+            mask = va_arg(args, UInt);
+            mnm = cls_operand(kind, mask);
+            p  += vex_sprintf(p, s390_mnm_fmt, mnm);
+            /* There are no special opcodes when mask == 0 or 15. In that case
+               the integer mask is appended as the final operand */
+            if (mask == 0 || mask == 15) mask_suffix = mask;
+            break;
          }
       }
       continue;
@@ -439,6 +448,8 @@
  done:
    va_end(args);
 
+   if (mask_suffix != -1)
+      p += vex_sprintf(p, ",%d", mask_suffix);
    *p = '\0';
 
    vassert(p < buf + sizeof buf);  /* detect buffer overwrite */
diff --git a/priv/host_s390_disasm.h b/priv/host_s390_disasm.h
index c7f75cc..c01aa34 100644
--- a/priv/host_s390_disasm.h
+++ b/priv/host_s390_disasm.h
@@ -75,7 +75,13 @@
    S390_XMNM_BCR = 1,
    S390_XMNM_BC = 2,
    S390_XMNM_BRC = 3,
-   S390_XMNM_BRCL = 4
+   S390_XMNM_BRCL = 4,
+   S390_XMNM_LOCR = 5,
+   S390_XMNM_LOCGR = 6,
+   S390_XMNM_LOC = 7,
+   S390_XMNM_LOCG = 8,
+   S390_XMNM_STOC = 9,
+   S390_XMNM_STOCG = 10
 };
 
 void s390_disasm(UInt command, ...);
diff --git a/priv/main_util.c b/priv/main_util.c
index d12380e..a4706cf 100644
--- a/priv/main_util.c
+++ b/priv/main_util.c
@@ -235,7 +235,7 @@
    New code for vex_util.c should go above this point. */
 #include <stdarg.h>
 
-static Int vex_strlen ( const HChar* str )
+Int vex_strlen ( const HChar* str )
 {
    Int i = 0;
    while (str[i] != 0) i++;
diff --git a/priv/main_util.h b/priv/main_util.h
index 1392b4b..ca63d8c 100644
--- a/priv/main_util.h
+++ b/priv/main_util.h
@@ -73,6 +73,7 @@
 /* String ops */
 
 extern Bool vex_streq ( const HChar* s1, const HChar* s2 );
+extern Int vex_strlen ( const HChar* str );
 
 
 /* Storage management: clear the area, and allocate from it. */