Support ARM and Thumb "CLREX" instructions since Dalvik generates
them.  Mucho hassle for something that is used considerably less often
than once in a blue moon.



git-svn-id: svn://svn.valgrind.org/vex/trunk@2209 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/guest_arm_toIR.c b/priv/guest_arm_toIR.c
index 675e1b3..6b93da5 100644
--- a/priv/guest_arm_toIR.c
+++ b/priv/guest_arm_toIR.c
@@ -11991,6 +11991,16 @@
          break;
    }
 
+   /* ------------------- CLREX ------------------ */
+   if (insn == 0xF57FF01F) {
+      /* AFAICS, this simply cancels a (all?) reservations made by a
+         (any?) preceding LDREX(es).  Arrange to hand it through to
+         the back end. */
+      stmt( IRStmt_MBE(Imbe_CancelReservation) );
+      DIP("clrex\n");
+      return True;
+   }
+
    /* ------------------- NEON ------------------- */
    if (archinfo->hwcaps & VEX_HWCAPS_ARM_NEON) {
       Bool ok_neon = decode_NEON_instruction(
@@ -17987,6 +17997,7 @@
    }
    /* -------------- v7 barrier insns -------------- */
    if (INSN0(15,0) == 0xF3BF && (INSN1(15,0) & 0xFF00) == 0x8F00) {
+      /* FIXME: should this be unconditional? */
       /* XXX this isn't really right, is it?  The generated IR does
          them unconditionally.  I guess it doesn't matter since it
          doesn't do any harm to do them even when the guarding
@@ -18025,6 +18036,7 @@
 
    /* ---------------------- PLD{,W} ---------------------- */
    if ((INSN0(15,4) & 0xFFD) == 0xF89 && INSN1(15,12) == 0xF) {
+      /* FIXME: should this be unconditional? */
       /* PLD/PLDW immediate, encoding T1 */
       UInt rN    = INSN0(3,0);
       UInt bW    = INSN0(5,5);
@@ -18034,6 +18046,7 @@
    }
 
    if ((INSN0(15,4) & 0xFFD) == 0xF81 && INSN1(15,8) == 0xFC) {
+      /* FIXME: should this be unconditional? */
       /* PLD/PLDW immediate, encoding T2 */
       UInt rN    = INSN0(3,0);
       UInt bW    = INSN0(5,5);
@@ -18043,6 +18056,7 @@
    }
 
    if ((INSN0(15,4) & 0xFFD) == 0xF81 && INSN1(15,6) == 0x3C0) {
+      /* FIXME: should this be unconditional? */
       /* PLD/PLDW register, encoding T1 */
       UInt rN   = INSN0(3,0);
       UInt rM   = INSN1(3,0);
@@ -18062,8 +18076,8 @@
    /* I don't know whether this is really v7-only.  But anyway, we
       have to support it since arm-linux uses TPIDRURO as a thread
       state register. */
-   
    if ((INSN0(15,0) == 0xEE1D) && (INSN1(11,0) == 0x0F70)) {
+      /* FIXME: should this be unconditional? */
       UInt rD = INSN1(15,12);
       if (!isBadRegT(rD)) {
          putIRegT(rD, IRExpr_Get(OFFB_TPIDRURO, Ity_I32), IRTemp_INVALID);
@@ -18073,6 +18087,17 @@
       /* fall through */
    }
 
+   /* ------------------- CLREX ------------------ */
+   if (INSN0(15,0) == 0xF3BF && INSN1(15,0) == 0x8F2F) {
+      /* AFAICS, this simply cancels a (all?) reservations made by a
+         (any?) preceding LDREX(es).  Arrange to hand it through to
+         the back end. */
+      mk_skip_over_T32_if_cond_is_false( condT );
+      stmt( IRStmt_MBE(Imbe_CancelReservation) );
+      DIP("clrex\n");
+      goto decode_success;
+   }
+
    /* ------------------- NOP ------------------ */
    if (INSN0(15,0) == 0xF3AF && INSN1(15,0) == 0x8000) {
       DIP("nop\n");
diff --git a/priv/host_arm_defs.c b/priv/host_arm_defs.c
index 9523607..6749f49 100644
--- a/priv/host_arm_defs.c
+++ b/priv/host_arm_defs.c
@@ -1338,6 +1338,11 @@
    i->tag      = ARMin_MFence;
    return i;
 }
+ARMInstr* ARMInstr_CLREX( void ) {
+   ARMInstr* i = LibVEX_Alloc(sizeof(ARMInstr));
+   i->tag      = ARMin_CLREX;
+   return i;
+}
 
 ARMInstr* ARMInstr_NLdStQ ( Bool isLoad, HReg dQ, ARMAModeN *amode ) {
    ARMInstr* i = LibVEX_Alloc(sizeof(ARMInstr));
@@ -1759,6 +1764,9 @@
          vex_printf("mfence (mcr 15,0,r0,c7,c10,4; 15,0,r0,c7,c10,5; "
                     "15,0,r0,c7,c5,4)");
          return;
+      case ARMin_CLREX:
+         vex_printf("clrex");
+         return;
       case ARMin_NLdStQ:
          if (i->ARMin.NLdStQ.isLoad)
             vex_printf("vld1.32 {");
@@ -2097,6 +2105,8 @@
          return;
       case ARMin_MFence:
          return;
+      case ARMin_CLREX:
+         return;
       case ARMin_NLdStQ:
          if (i->ARMin.NLdStQ.isLoad)
             addHRegUse(u, HRmWrite, i->ARMin.NLdStQ.dQ);
@@ -2275,6 +2285,8 @@
          return;
       case ARMin_MFence:
          return;
+      case ARMin_CLREX:
+         return;
       case ARMin_NLdStQ:
          i->ARMin.NLdStQ.dQ = lookupHRegRemap(m, i->ARMin.NLdStQ.dQ);
          mapRegs_ARMAModeN(m, i->ARMin.NLdStQ.amode);
@@ -3286,6 +3298,11 @@
          *p++ = 0xEE070F95; /* mcr 15,0,r0,c7,c5,4  (ISB) */
          goto done;
       }
+      case ARMin_CLREX: {
+         *p++ = 0xF57FF01F; /* clrex */
+         goto done;
+      }
+
       case ARMin_NLdStQ: {
          UInt regD = qregNo(i->ARMin.NLdStQ.dQ) << 1;
          UInt regN, regM;
diff --git a/priv/host_arm_defs.h b/priv/host_arm_defs.h
index 92bdbe0..989f79e 100644
--- a/priv/host_arm_defs.h
+++ b/priv/host_arm_defs.h
@@ -1,4 +1,3 @@
-
 /*---------------------------------------------------------------*/
 /*--- begin                                   host_arm_defs.h ---*/
 /*---------------------------------------------------------------*/
@@ -587,6 +586,7 @@
       ARMin_VCvtID,
       ARMin_FPSCR,
       ARMin_MFence,
+      ARMin_CLREX,
       /* Neon */
       ARMin_NLdStQ,
       ARMin_NLdStD,
@@ -827,6 +827,9 @@
          */
          struct {
          } MFence;
+         /* A CLREX instruction. */
+         struct {
+         } CLREX;
          /* Neon data processing instruction: 3 registers of the same
             length */
          struct {
@@ -940,6 +943,7 @@
                                      HReg dst, HReg src );
 extern ARMInstr* ARMInstr_FPSCR    ( Bool toFPSCR, HReg iReg );
 extern ARMInstr* ARMInstr_MFence   ( void );
+extern ARMInstr* ARMInstr_CLREX    ( void );
 extern ARMInstr* ARMInstr_NLdStQ   ( Bool isLoad, HReg, ARMAModeN* );
 extern ARMInstr* ARMInstr_NLdStD   ( Bool isLoad, HReg, ARMAModeN* );
 extern ARMInstr* ARMInstr_NUnary   ( ARMNeonUnOp, HReg, HReg, UInt, Bool );
diff --git a/priv/host_arm_isel.c b/priv/host_arm_isel.c
index 35c4c65..f347eb0 100644
--- a/priv/host_arm_isel.c
+++ b/priv/host_arm_isel.c
@@ -5908,7 +5908,10 @@
    case Ist_MBE:
       switch (stmt->Ist.MBE.event) {
          case Imbe_Fence:
-            addInstr(env,ARMInstr_MFence());
+            addInstr(env, ARMInstr_MFence());
+            return;
+         case Imbe_CancelReservation:
+            addInstr(env, ARMInstr_CLREX());
             return;
          default:
             break;
diff --git a/priv/ir_defs.c b/priv/ir_defs.c
index e586d17..c604ad2 100644
--- a/priv/ir_defs.c
+++ b/priv/ir_defs.c
@@ -1139,8 +1139,12 @@
 void ppIRMBusEvent ( IRMBusEvent event )
 {
    switch (event) {
-      case Imbe_Fence: vex_printf("Fence"); break;
-      default:         vpanic("ppIRMBusEvent");
+      case Imbe_Fence:
+         vex_printf("Fence"); break;
+      case Imbe_CancelReservation:
+         vex_printf("CancelReservation"); break;
+      default:
+         vpanic("ppIRMBusEvent");
    }
 }
 
@@ -3408,7 +3412,7 @@
          break;
       case Ist_MBE:
          switch (stmt->Ist.MBE.event) {
-            case Imbe_Fence:
+            case Imbe_Fence: case Imbe_CancelReservation:
                break;
             default: sanityCheckFail(bb,stmt,"IRStmt.MBE.event: unknown");
                break;
diff --git a/pub/libvex_ir.h b/pub/libvex_ir.h
index 3cd6618..435090f 100644
--- a/pub/libvex_ir.h
+++ b/pub/libvex_ir.h
@@ -1772,6 +1772,10 @@
 typedef
    enum { 
       Imbe_Fence=0x18000, 
+      /* Needed only on ARM.  It cancels a reservation made by a
+         preceding Linked-Load, and needs to be handed through to the
+         back end, just as LL and SC themselves are. */
+      Imbe_CancelReservation
    }
    IRMBusEvent;