VEX-side support for the V-bit tester.
- recognise the new "special instruction" for all architectures
  (ARM needs implementation work; x86 and ARM are untested)
- inject IR into the superblock
- type definition for the IR injection control block


git-svn-id: svn://svn.valgrind.org/vex/trunk@2490 8f6e269a-dfd6-0310-a8e1-e2731360e62c
diff --git a/priv/guest_amd64_toIR.c b/priv/guest_amd64_toIR.c
index fc24a35..4cf7e6d 100644
--- a/priv/guest_amd64_toIR.c
+++ b/priv/guest_amd64_toIR.c
@@ -117,6 +117,7 @@
       4887DB (xchgq %rbx,%rbx)   %RDX = client_request ( %RAX )
       4887C9 (xchgq %rcx,%rcx)   %RAX = guest_NRADDR
       4887D2 (xchgq %rdx,%rdx)   call-noredir *%RAX
+      4887F6 (xchgq %rdi,%rdi)   IR injection
 
    Any other bytes following the 16-byte preamble are illegal and
    constitute a failure in instruction decoding.  This all assumes
@@ -26548,6 +26549,27 @@
             vassert(dres.whatNext == Dis_StopHere);
             goto decode_success;
          }
+         else
+         if (code[16] == 0x48 && code[17] == 0x87
+                              && code[18] == 0xff /* xchgq %rdi,%rdi */) {
+           /* IR injection */
+            DIP("IR injection\n");
+            vex_inject_ir(irsb, Iend_LE);
+
+            // Invalidate the current insn. The reason is that the IRop we're
+            // injecting here can change. In which case the translation has to
+            // be redone. For ease of handling, we simply invalidate all the
+            // time.
+            stmt(IRStmt_Put(OFFB_TISTART, mkU64(guest_RIP_curr_instr)));
+            stmt(IRStmt_Put(OFFB_TILEN,   mkU64(19)));
+   
+            delta += 19;
+
+            stmt( IRStmt_Put( OFFB_RIP, mkU64(guest_RIP_bbstart + delta) ) );
+            dres.whatNext    = Dis_StopHere;
+            dres.jk_StopHere = Ijk_TInval;
+            goto decode_success;
+         }
          /* We don't know what it is. */
          goto decode_failure;
          /*NOTREACHED*/
diff --git a/priv/guest_arm_toIR.c b/priv/guest_arm_toIR.c
index 553dee1..29e3651 100644
--- a/priv/guest_arm_toIR.c
+++ b/priv/guest_arm_toIR.c
@@ -91,6 +91,7 @@
       E18AA00A (orr r10,r10,r10)   R3 = client_request ( R4 )
       E18BB00B (orr r11,r11,r11)   R3 = guest_NRADDR
       E18CC00C (orr r12,r12,r12)   branch-and-link-to-noredir R4
+      E18DD00D (orr r13,r13,r13)   IR injection
 
    Any other bytes following the 16-byte preamble are illegal and
    constitute a failure in instruction decoding.  This all assumes
@@ -12476,6 +12477,30 @@
             dres.whatNext    = Dis_StopHere;
             goto decode_success;
          }
+         else
+         if (getUIntLittleEndianly(code+16) == 0xE18DD00D
+                                               /* orr r13,r13,r13 */) {
+            /* IR injection */
+            DIP("IR injection\n");
+
+            vex_inject_ir(irsb, Iend_LE);
+
+            // Invalidate the current insn. The reason is that the IRop we're
+            // injecting here can change. In which case the translation has to
+            // be redone. For ease of handling, we simply invalidate all the
+            // time.
+#if 0
+            // FIXME: needs to be fixed
+            stmt(IRStmt_Put(OFFB_TISTART, mkU32(guest_R15_curr_instr_notENC)));
+            stmt(IRStmt_Put(OFFB_TILEN,   mkU32(20)));
+   
+            llPutIReg(15, mkU32( guest_R15_curr_instr_notENC + 20 ));
+
+            dres.whatNext    = Dis_StopHere;
+            dres.jk_StopHere = Ijk_TInval;
+            goto decode_success;
+#endif
+         }
          /* We don't know what it is.  Set opc1/opc2 so decode_failure
             can print the insn following the Special-insn preamble. */
          insn = getUIntLittleEndianly(code+16);
diff --git a/priv/guest_mips_toIR.c b/priv/guest_mips_toIR.c
index 0fcb07c..3c72619 100644
--- a/priv/guest_mips_toIR.c
+++ b/priv/guest_mips_toIR.c
@@ -1292,6 +1292,25 @@
             dres.jk_StopHere = Ijk_NoRedir;
             dres.whatNext    = Dis_StopHere;
             goto decode_success;
+         } else if (getUInt(code + 16) == 0x016b5825/* or t3,t3,t3 */ ) {
+           /* IR injection */
+            DIP("IR injection\n");
+#if defined (_MIPSEL)
+            vex_inject_ir(irsb, Iend_LE);
+#elif defined (_MIPSEB)
+            vex_inject_ir(irsb, Iend_BE);
+#endif
+            stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_TISTART),
+                            mkU32(guest_PC_curr_instr)));
+            stmt(IRStmt_Put(offsetof(VexGuestMIPS32State, guest_TILEN),
+                            mkU32(20)));
+
+            putPC(mkU32(guest_PC_curr_instr + 20));
+            dres.whatNext    = Dis_StopHere;
+            dres.jk_StopHere = Ijk_TInval;
+            dres.len = 20;
+            delta += 20;
+            goto decode_success;
          }
 
          /* We don't know what it is.  Set opc1/opc2 so decode_failure
diff --git a/priv/guest_ppc_toIR.c b/priv/guest_ppc_toIR.c
index cffdad1..6439e79 100644
--- a/priv/guest_ppc_toIR.c
+++ b/priv/guest_ppc_toIR.c
@@ -93,6 +93,7 @@
       7C421378 (or 2,2,2)   %R3 = guest_NRADDR
       7C631B78 (or 3,3,3)   branch-and-link-to-noredir %R11
       7C842378 (or 4,4,4)   %R3 = guest_NRADDR_GPR2
+      7CA52B78 (or 5,5,5)   IR injection
 
    Any other bytes following the 16-byte preamble are illegal and
    constitute a failure in instruction decoding.  This all assumes
@@ -16589,6 +16590,27 @@
             putIReg(3, IRExpr_Get( OFFB_NRADDR_GPR2, ty ));
             goto decode_success;
          }
+         else
+         if (getUIntBigendianly(code+16) == 0x7CA52B78 /* or 5,5,5 */) {
+            DIP("IR injection\n");
+
+            vex_inject_ir(irsb, Iend_BE);
+
+            delta += 20;
+            dres.len = 20;
+
+            // Invalidate the current insn. The reason is that the IRop we're
+            // injecting here can change. In which case the translation has to
+            // be redone. For ease of handling, we simply invalidate all the
+            // time.
+            stmt(IRStmt_Put(OFFB_TISTART, mkU64(guest_CIA_curr_instr)));
+            stmt(IRStmt_Put(OFFB_TILEN,   mkU64(20)));
+   
+            putGST( PPC_GST_CIA, mkSzImm( ty, guest_CIA_bbstart + delta ));
+            dres.whatNext    = Dis_StopHere;
+            dres.jk_StopHere = Ijk_TInval;
+            goto decode_success;
+         }
          /* We don't know what it is.  Set opc1/opc2 so decode_failure
             can print the insn following the Special-insn preamble. */
          theInstr = getUIntBigendianly(code+16);
diff --git a/priv/guest_s390_toIR.c b/priv/guest_s390_toIR.c
index da1c92f..d7e18dd 100644
--- a/priv/guest_s390_toIR.c
+++ b/priv/guest_s390_toIR.c
@@ -14129,6 +14129,23 @@
       s390_irgen_guest_NRADDR();
    } else if (bytes[0] == 0x18 && bytes[1] == 0x44 /* lr %r4, %r4 */) {
       s390_irgen_call_noredir();
+   } else if (bytes[0] == 0x18 && bytes[1] == 0x55 /* lr %r5, %r5 */) {
+      vex_inject_ir(irsb, Iend_BE);
+
+      /* Invalidate the current insn. The reason is that the IRop we're
+         injecting here can change. In which case the translation has to
+         be redone. For ease of handling, we simply invalidate all the
+         time. */
+      stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_TISTART),
+                      mkU64(guest_IA_curr_instr)));
+      stmt(IRStmt_Put(S390X_GUEST_OFFSET(guest_TILEN),
+                      mkU64(guest_IA_next_instr - guest_IA_curr_instr)));
+      vassert(guest_IA_next_instr - guest_IA_curr_instr ==
+              S390_SPECIAL_OP_PREAMBLE_SIZE + S390_SPECIAL_OP_SIZE);
+
+      put_IA(mkaddr_expr(guest_IA_next_instr));
+      dis_res->whatNext    = Dis_StopHere;
+      dis_res->jk_StopHere = Ijk_TInval;
    } else {
       /* We don't know what it is. */
       return S390_DECODE_UNKNOWN_SPECIAL_INSN;
diff --git a/priv/guest_x86_toIR.c b/priv/guest_x86_toIR.c
index 5dc58b5..e98762d 100644
--- a/priv/guest_x86_toIR.c
+++ b/priv/guest_x86_toIR.c
@@ -148,6 +148,7 @@
       87DB (xchgl %ebx,%ebx)   %EDX = client_request ( %EAX )
       87C9 (xchgl %ecx,%ecx)   %EAX = guest_NRADDR
       87D2 (xchgl %edx,%edx)   call-noredir *%EAX
+      87FF (xchgl %edi,%edi)   IR injection
 
    Any other bytes following the 12-byte preamble are illegal and
    constitute a failure in instruction decoding.  This all assumes
@@ -8017,6 +8018,26 @@
             vassert(dres.whatNext == Dis_StopHere);
             goto decode_success;
          }
+         else
+         if (code[12] == 0x87 && code[13] == 0xFF /* xchgl %edi,%edi */) {
+            /* IR injection */
+            DIP("IR injection\n");
+            vex_inject_ir(irsb, Iend_LE);
+
+            // Invalidate the current insn. The reason is that the IRop we're
+            // injecting here can change. In which case the translation has to
+            // be redone. For ease of handling, we simply invalidate all the
+            // time.
+            stmt(IRStmt_Put(OFFB_TISTART, mkU32(guest_EIP_curr_instr)));
+            stmt(IRStmt_Put(OFFB_TILEN,   mkU32(14)));
+   
+            delta += 14;
+
+            stmt( IRStmt_Put( OFFB_EIP, mkU32(guest_EIP_bbstart + delta) ) );
+            dres.whatNext    = Dis_StopHere;
+            dres.jk_StopHere = Ijk_TInval;
+            goto decode_success;
+         }
          /* We don't know what it is. */
          goto decode_failure;
          /*NOTREACHED*/
diff --git a/priv/ir_inject.c b/priv/ir_inject.c
new file mode 100644
index 0000000..53addbb
--- /dev/null
+++ b/priv/ir_inject.c
@@ -0,0 +1,253 @@
+/* -*- mode: C; c-basic-offset: 3; -*- */
+
+/*---------------------------------------------------------------*/
+/*--- begin                                       ir_inject.c ---*/
+/*---------------------------------------------------------------*/
+
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2012-2012  Florian Krohm   (britzel@acm.org)
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "libvex_basictypes.h"
+#include "libvex_ir.h"
+#include "libvex.h"
+#include "main_util.h"
+
+/* Convenience macros for readibility */
+#define mkU32(v)  IRExpr_Const(IRConst_U32(v))
+#define mkU64(v)  IRExpr_Const(IRConst_U64(v))
+#define unop(kind, a)  IRExpr_Unop(kind, a)
+#define binop(kind, a1, a2)  IRExpr_Binop(kind, a1, a2)
+#define triop(kind, a1, a2, a3)  IRExpr_Triop(kind, a1, a2, a3)
+#define qop(kind, a1, a2, a3, a4)  IRExpr_Qop(kind, a1, a2, a3, a4)
+#define stmt(irsb, st)  addStmtToIRSB(irsb, st)
+
+
+/* The IR Injection Control Block. vex_inject_ir will query its contents
+   to construct IR statements for testing purposes. */
+static IRICB iricb;
+
+
+void
+LibVEX_InitIRI(const IRICB *iricb_in)
+{
+   iricb = *iricb_in;  // copy in
+}
+
+
+static IRExpr *
+load_aux(IREndness endian, IRType type, IRExpr *addr)
+{
+   if (type == Ity_D64) {
+      /* The insn selectors do not support loading a DFP value from memory.
+         So we need to fix it here by loading an integer value and
+         reinterpreting it as DFP. */
+      return unop(Iop_ReinterpI64asD64,
+                  IRExpr_Load(endian, Ity_I64, addr));
+   }
+   if (type == Ity_I1) {
+      /* A Boolean value is stored as a 32-bit entity (see store_aux). */
+      return unop(Iop_32to1, IRExpr_Load(endian, Ity_I32, addr));
+   }
+
+   return IRExpr_Load(endian, type, addr);
+}
+
+
+/* Load a value from memory. Loads of more than 8 byte are split into
+   a series of 8-byte loads and combined using appropriate IROps. */
+static IRExpr *
+load(IREndness endian, IRType type, HWord haddr)
+{
+   IROp concat;
+   IRExpr *addr, *next_addr;
+
+   if (VEX_HOST_WORDSIZE == 8) {
+      addr = mkU64(haddr);
+      next_addr = binop(Iop_Add64, addr, mkU64(8));
+   } else if (VEX_HOST_WORDSIZE == 4) {
+      addr = mkU32(haddr);
+      next_addr = binop(Iop_Add32, addr, mkU32(4));
+   } else {
+      vpanic("invalid #bytes for address");
+   }
+
+   switch (type) {
+   case Ity_I128: concat = Iop_64HLto128;   type = Ity_I64; goto load128;
+   case Ity_F128: concat = Iop_F64HLtoF128; type = Ity_F64; goto load128;
+   case Ity_D128: concat = Iop_D64HLtoD128; type = Ity_D64; goto load128;
+
+   load128:
+     /* Two loads of 64 bit each. */
+      if (endian == Iend_BE) {
+         /* The more significant bits are at the lower address. */
+         return binop(concat,
+                      load_aux(endian, type, addr),
+                      load_aux(endian, type, next_addr));
+      } else {
+         /* The more significant bits are at the higher address. */
+         return binop(concat,
+                      load_aux(endian, type, next_addr),
+                      load_aux(endian, type, addr));
+      }
+
+   default:
+      return load_aux(endian, type, addr);
+   }
+}
+
+
+static void
+store_aux(IRSB *irsb, IREndness endian, IRExpr *addr, IRExpr *data)
+{
+   if (typeOfIRExpr(irsb->tyenv, data) == Ity_D64) {
+      /* The insn selectors do not support writing a DFP value to memory.
+         So we need to fix it here by reinterpreting the DFP value as an
+         integer and storing that. */
+      data = unop(Iop_ReinterpD64asI64, data);
+   }
+   if (typeOfIRExpr(irsb->tyenv, data) == Ity_I1) {
+      /* We cannot store a single bit. So we store it in a 32-bit container.
+         See also load_aux. */
+      data = unop(Iop_1Uto32, data);
+   }
+   stmt(irsb, IRStmt_Store(endian, addr, data));
+}
+
+
+/* Store a value to memory. If a value requires more than 8 bytes a series
+   of 8-byte loads will be generated. */
+static void __inline__
+store(IRSB *irsb, IREndness endian, HWord haddr, IRExpr *data)
+{
+   IROp high, low;
+   IRExpr *addr, *next_addr;
+
+   if (VEX_HOST_WORDSIZE == 8) {
+      addr = mkU64(haddr);
+      next_addr = binop(Iop_Add64, addr, mkU64(8));
+   } else if (VEX_HOST_WORDSIZE == 4) {
+      addr = mkU32(haddr);
+      next_addr = binop(Iop_Add32, addr, mkU32(4));
+   } else {
+      vpanic("invalid #bytes for address");
+   }
+
+   switch (typeOfIRExpr(irsb->tyenv, data)) {
+   case Ity_I128: high = Iop_128HIto64;   low = Iop_128to64;     goto store128;
+   case Ity_F128: high = Iop_F128HItoF64; low = Iop_F128LOtoF64; goto store128;
+   case Ity_D128: high = Iop_D128HItoD64; low = Iop_D128LOtoD64; goto store128;
+
+   store128:
+     /* Two stores of 64 bit each. */
+      if (endian == Iend_BE) {
+         /* The more significant bits are at the lower address. */
+         store_aux(irsb, endian, addr, unop(high, data));
+         store_aux(irsb, endian, next_addr, unop(low, data));
+      } else {
+         /* The more significant bits are at the higher address. */
+         store_aux(irsb, endian, addr, unop(low, data));
+         store_aux(irsb, endian, next_addr, unop(high, data));
+      }
+      return;
+
+   default:
+      store_aux(irsb, endian, addr, data);
+      return;
+   }
+}
+
+
+/* Inject IR stmts depending on the data provided in the control
+   block iricb. */
+void
+vex_inject_ir(IRSB *irsb, IREndness endian)
+{
+   IRExpr *data, *rounding_mode, *opnd1, *opnd2, *opnd3, *opnd4;
+
+   rounding_mode = NULL;
+   if (iricb.rounding_mode != NO_ROUNDING_MODE) {
+      rounding_mode = mkU32(iricb.rounding_mode);
+   }
+
+   switch (iricb.num_operands) {
+   case 1:
+      opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1);
+      if (rounding_mode)
+         data = binop(iricb.op, rounding_mode, opnd1);
+      else
+         data = unop(iricb.op, opnd1);
+      break;
+
+   case 2:
+      opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1);
+      opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2);
+      if (rounding_mode)
+         data = triop(iricb.op, rounding_mode, opnd1, opnd2);
+      else
+         data = binop(iricb.op, opnd1, opnd2);
+      break;
+
+   case 3:
+      opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1);
+      opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2);
+      opnd3 = load(endian, iricb.t_opnd3, iricb.opnd3);
+      if (rounding_mode)
+         data = qop(iricb.op, rounding_mode, opnd1, opnd2, opnd3);
+      else
+         data = triop(iricb.op, opnd1, opnd2, opnd3);
+      break;
+
+   case 4:
+      vassert(rounding_mode == NULL);
+      opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1);
+      opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2);
+      opnd3 = load(endian, iricb.t_opnd3, iricb.opnd3);
+      opnd4 = load(endian, iricb.t_opnd4, iricb.opnd4);
+      data = qop(iricb.op, opnd1, opnd2, opnd3, opnd4);
+      break;
+
+   default:
+      vpanic("unsupported operator");
+   }
+
+   store(irsb, endian, iricb.result, data);
+
+   if (0) {
+      vex_printf("BEGIN inject\n");
+      if (sizeofIRType(iricb.t_result) <= 8) {
+         ppIRStmt(irsb->stmts[irsb->stmts_used - 1]);
+      } else if (sizeofIRType(iricb.t_result) == 16) {
+         ppIRStmt(irsb->stmts[irsb->stmts_used - 2]);
+         vex_printf("\n");
+         ppIRStmt(irsb->stmts[irsb->stmts_used - 1]);
+         vex_printf("\n");
+      }
+      vex_printf("END inject\n");
+   }
+}
+
+/*---------------------------------------------------------------*/
+/*--- end                                         ir_inject.c ---*/
+/*---------------------------------------------------------------*/
diff --git a/pub/libvex.h b/pub/libvex.h
index 95551a2..d175f73 100644
--- a/pub/libvex.h
+++ b/pub/libvex.h
@@ -750,6 +750,33 @@
 
 extern void LibVEX_ShowStats ( void );
 
+/*-------------------------------------------------------*/
+/*-- IR injection                                      --*/
+/*-------------------------------------------------------*/
+
+/* IR Injection Control Block */
+
+#define NO_ROUNDING_MODE (~0u)
+
+typedef 
+   struct {
+      IROp  op;        // the operation to perform
+      HWord result;    // address of the result
+      HWord opnd1;     // address of 1st operand
+      HWord opnd2;     // address of 2nd operand
+      HWord opnd3;     // address of 3rd operand
+      HWord opnd4;     // address of 4th operand
+      IRType t_result; // type of result
+      IRType t_opnd1;  // type of 1st operand
+      IRType t_opnd2;  // type of 2nd operand
+      IRType t_opnd3;  // type of 3rd operand
+      IRType t_opnd4;  // type of 4th operand
+      UInt  rounding_mode;
+      UInt  num_operands; // excluding rounding mode, if any
+   }
+   IRICB;
+
+extern void LibVEX_InitIRI ( const IRICB * );
 
 /*-------------------------------------------------------*/
 /*--- Notes                                           ---*/
diff --git a/pub/libvex_ir.h b/pub/libvex_ir.h
index 24d428d..6886087 100644
--- a/pub/libvex_ir.h
+++ b/pub/libvex_ir.h
@@ -1461,7 +1461,8 @@
       Iop_Recip32Fx8,
 
       Iop_Max32Fx8, Iop_Min32Fx8,
-      Iop_Max64Fx4, Iop_Min64Fx4
+      Iop_Max64Fx4, Iop_Min64Fx4,
+      Iop_LAST      /* must be the last enumerator */
    }
    IROp;
 
@@ -2496,6 +2497,13 @@
 /* Is this any value actually in the enumeration 'IRType' ? */
 extern Bool isPlausibleIRType ( IRType ty );
 
+
+/*---------------------------------------------------------------*/
+/*--- IR injection                                            ---*/
+/*---------------------------------------------------------------*/
+void vex_inject_ir(IRSB *, IREndness);
+
+
 #endif /* ndef __LIBVEX_IR_H */