]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Support ADC/ADCS/SBC/SBCS. Fixes #335496. (dimitry@google.com)
authorJulian Seward <jseward@acm.org>
Wed, 4 Jun 2014 13:09:44 +0000 (13:09 +0000)
committerJulian Seward <jseward@acm.org>
Wed, 4 Jun 2014 13:09:44 +0000 (13:09 +0000)
git-svn-id: svn://svn.valgrind.org/vex/trunk@2868

VEX/priv/guest_arm64_defs.h
VEX/priv/guest_arm64_helpers.c
VEX/priv/guest_arm64_toIR.c

index ab7665fd57545927e425f14ad2e5467747b2012d..342312e834871cc879c77297df125d9e3e850dee 100644 (file)
@@ -83,12 +83,12 @@ extern
 ULong arm64g_calculate_flags_nzcv ( ULong cc_op, ULong cc_dep1,
                                     ULong cc_dep2, ULong cc_dep3 );
 
-//ZZ /* Calculate the C flag from the thunk components, in the lowest bit
-//ZZ    of the word (bit 0). */
-//ZZ extern 
-//ZZ UInt armg_calculate_flag_c ( UInt cc_op, UInt cc_dep1,
-//ZZ                              UInt cc_dep2, UInt cc_dep3 );
-//ZZ 
+/* Calculate the C flag from the thunk components, in the lowest bit
+   of the word (bit 0). */
+extern
+ULong arm64g_calculate_flag_c ( ULong cc_op, ULong cc_dep1,
+                                ULong cc_dep2, ULong cc_dep3 );
+
 //ZZ /* Calculate the V flag from the thunk components, in the lowest bit
 //ZZ    of the word (bit 0). */
 //ZZ extern 
@@ -159,8 +159,10 @@ ULong arm64g_calculate_condition ( /* ARM64Condcode << 4 | cc_op */
    OP_ADD64          argL              argR              unused
    OP_SUB32          argL              argR              unused
    OP_SUB64          argL              argR              unused
-//ZZ    OP_ADC            argL              argR              31x0:old_C
-//ZZ    OP_SBB            argL              argR              31x0:old_C
+   OP_ADC32          argL              argR              63x0:old_C
+   OP_ADC64          argL              argR              63x0:old_C
+   OP_SBC32          argL              argR              63x0:old_C
+   OP_SBC64          argL              argR              63x0:old_C
    OP_LOGIC32        result            unused            unused
    OP_LOGIC64        result            unused            unused
 //ZZ    OP_MUL            result            unused            30x0:old_C:old_V
@@ -183,11 +185,17 @@ enum {
    ARM64G_CC_OP_SUB64,    /* DEP1 = argL (Rn), DEP2 = argR (shifter_op),
                              DEP3 = 0 */
 
-//ZZ    ARMG_CC_OP_ADC,     /* DEP1 = argL (Rn), DEP2 = arg2 (shifter_op),
-//ZZ                           DEP3 = oldC (in LSB) */
-//ZZ 
-//ZZ    ARMG_CC_OP_SBB,     /* DEP1 = argL (Rn), DEP2 = arg2 (shifter_op),
-//ZZ                           DEP3 = oldC (in LSB) */
+   ARM64G_CC_OP_ADC32,    /* DEP1 = argL (Rn), DEP2 = arg2 (shifter_op),
+                             DEP3 = oldC (in LSB) */
+
+   ARM64G_CC_OP_ADC64,    /* DEP1 = argL (Rn), DEP2 = arg2 (shifter_op),
+                             DEP3 = oldC (in LSB) */
+
+   ARM64G_CC_OP_SBC32,    /* DEP1 = argL (Rn), DEP2 = arg2 (shifter_op),
+                             DEP3 = oldC (in LSB) */
+
+   ARM64G_CC_OP_SBC64,    /* DEP1 = argL (Rn), DEP2 = arg2 (shifter_op),
+                             DEP3 = oldC (in LSB) */
 
    ARM64G_CC_OP_LOGIC32,  /* DEP1 = result, DEP2 = 0, DEP3 = 0 */
    ARM64G_CC_OP_LOGIC64,  /* DEP1 = result, DEP2 = 0, DEP3 = 0 */
index 0ff080383defe38073a2a21f66e3c10ebcdc07c1..3196d424b49c7a778c66335c43cf28319b4197ae 100644 (file)
@@ -153,26 +153,46 @@ ULong arm64g_calculate_flag_n ( ULong cc_op, ULong cc_dep1,
          ULong nf   = res >> 63;
          return nf;
       }
-//ZZ       case ARMG_CC_OP_ADC: {
-//ZZ          /* (argL, argR, oldC) */
-//ZZ          UInt argL = cc_dep1;
-//ZZ          UInt argR = cc_dep2;
-//ZZ          UInt oldC = cc_dep3;
-//ZZ          vassert((oldC & ~1) == 0);
-//ZZ          UInt res  = argL + argR + oldC;
-//ZZ          UInt nf   = res >> 31;
-//ZZ          return nf;
-//ZZ       }
-//ZZ       case ARMG_CC_OP_SBB: {
-//ZZ          /* (argL, argR, oldC) */
-//ZZ          UInt argL = cc_dep1;
-//ZZ          UInt argR = cc_dep2;
-//ZZ          UInt oldC = cc_dep3;
-//ZZ          vassert((oldC & ~1) == 0);
-//ZZ          UInt res  = argL - argR - (oldC ^ 1);
-//ZZ          UInt nf   = res >> 31;
-//ZZ          return nf;
-//ZZ       }
+      case ARM64G_CC_OP_ADC32: {
+         /* (argL, argR, oldC) */
+         UInt  argL = cc_dep1;
+         UInt  argR = cc_dep2;
+         UInt  oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         UInt  res  = argL + argR + oldC;
+         ULong nf   = (ULong)(res >> 31);
+         return nf;
+      }
+      case ARM64G_CC_OP_ADC64: {
+         /* (argL, argR, oldC) */
+         ULong argL = cc_dep1;
+         ULong argR = cc_dep2;
+         ULong oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         ULong res  = argL + argR + oldC;
+         ULong nf   = res >> 63;
+         return nf;
+      }
+      case ARM64G_CC_OP_SBC32: {
+         /* (argL, argR, oldC) */
+         UInt  argL = cc_dep1;
+         UInt  argR = cc_dep2;
+         UInt  oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         UInt  res  = argL - argR - (oldC ^ 1);
+         ULong nf   = (ULong)(res >> 31);
+         return nf;
+      }
+      case ARM64G_CC_OP_SBC64: {
+         /* (argL, argR, oldC) */
+         ULong argL = cc_dep1;
+         ULong argR = cc_dep2;
+         ULong oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         ULong res  = argL - argR - (oldC ^ 1);
+         ULong nf   = res >> 63;
+         return nf;
+      }
       case ARM64G_CC_OP_LOGIC32: {
          /* (res, unused, unused) */
          UInt  res = (UInt)cc_dep1;
@@ -251,26 +271,46 @@ ULong arm64g_calculate_flag_z ( ULong cc_op, ULong cc_dep1,
          ULong zf   = res == 0;
          return zf;
       }
-//ZZ       case ARMG_CC_OP_ADC: {
-//ZZ          /* (argL, argR, oldC) */
-//ZZ          UInt argL = cc_dep1;
-//ZZ          UInt argR = cc_dep2;
-//ZZ          UInt oldC = cc_dep3;
-//ZZ          vassert((oldC & ~1) == 0);
-//ZZ          UInt res  = argL + argR + oldC;
-//ZZ          UInt zf   = res == 0;
-//ZZ          return zf;
-//ZZ       }
-//ZZ       case ARMG_CC_OP_SBB: {
-//ZZ          /* (argL, argR, oldC) */
-//ZZ          UInt argL = cc_dep1;
-//ZZ          UInt argR = cc_dep2;
-//ZZ          UInt oldC = cc_dep3;
-//ZZ          vassert((oldC & ~1) == 0);
-//ZZ          UInt res  = argL - argR - (oldC ^ 1);
-//ZZ          UInt zf   = res == 0;
-//ZZ          return zf;
-//ZZ       }
+      case ARM64G_CC_OP_ADC32: {
+         /* (argL, argR, oldC) */
+         UInt  argL = cc_dep1;
+         UInt  argR = cc_dep2;
+         UInt  oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         UInt  res  = argL + argR + oldC;
+         ULong zf   = res == 0;
+         return zf;
+      }
+      case ARM64G_CC_OP_ADC64: {
+         /* (argL, argR, oldC) */
+         ULong argL = cc_dep1;
+         ULong argR = cc_dep2;
+         ULong oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         ULong res  = argL + argR + oldC;
+         ULong zf   = res == 0;
+         return zf;
+      }
+      case ARM64G_CC_OP_SBC32: {
+         /* (argL, argR, oldC) */
+         UInt  argL = cc_dep1;
+         UInt  argR = cc_dep2;
+         UInt  oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         UInt  res  = argL - argR - (oldC ^ 1);
+         ULong zf   = res == 0;
+         return zf;
+      }
+      case ARM64G_CC_OP_SBC64: {
+         /* (argL, argR, oldC) */
+         ULong argL = cc_dep1;
+         ULong argR = cc_dep2;
+         ULong oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         ULong res  = argL - argR - (oldC ^ 1);
+         ULong zf   = res == 0;
+         return zf;
+      }
       case ARM64G_CC_OP_LOGIC32: {
          /* (res, unused, unused) */
          UInt  res  = (UInt)cc_dep1;
@@ -309,7 +349,6 @@ ULong arm64g_calculate_flag_z ( ULong cc_op, ULong cc_dep1,
 /* CALLED FROM GENERATED CODE: CLEAN HELPER */
 /* Calculate the C flag from the supplied thunk components, in the
    least significant bit of the word.  Returned bits 63:1 are zero. */
-static
 ULong arm64g_calculate_flag_c ( ULong cc_op, ULong cc_dep1,
                                 ULong cc_dep2, ULong cc_dep3 )
 {
@@ -349,25 +388,44 @@ ULong arm64g_calculate_flag_c ( ULong cc_op, ULong cc_dep1,
          ULong cf   = argL >= argR;
          return cf;
       }
-//ZZ       case ARMG_CC_OP_ADC: {
-//ZZ          /* (argL, argR, oldC) */
-//ZZ          UInt argL = cc_dep1;
-//ZZ          UInt argR = cc_dep2;
-//ZZ          UInt oldC = cc_dep3;
-//ZZ          vassert((oldC & ~1) == 0);
-//ZZ          UInt res  = argL + argR + oldC;
-//ZZ          UInt cf   = oldC ? (res <= argL) : (res < argL);
-//ZZ          return cf;
-//ZZ       }
-//ZZ       case ARMG_CC_OP_SBB: {
-//ZZ          /* (argL, argR, oldC) */
-//ZZ          UInt argL = cc_dep1;
-//ZZ          UInt argR = cc_dep2;
-//ZZ          UInt oldC = cc_dep3;
-//ZZ          vassert((oldC & ~1) == 0);
-//ZZ          UInt cf   = oldC ? (argL >= argR) : (argL > argR);
-//ZZ          return cf;
-//ZZ       }
+      case ARM64G_CC_OP_ADC32: {
+         /* (argL, argR, oldC) */
+         UInt  argL = cc_dep1;
+         UInt  argR = cc_dep2;
+         UInt  oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         UInt  res  = argL + argR + oldC;
+         ULong cf   = oldC ? (res <= argL) : (res < argL);
+         return cf;
+      }
+      case ARM64G_CC_OP_ADC64: {
+         /* (argL, argR, oldC) */
+         ULong argL = cc_dep1;
+         ULong argR = cc_dep2;
+         ULong oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         ULong res  = argL + argR + oldC;
+         ULong cf   = oldC ? (res <= argL) : (res < argL);
+         return cf;
+      }
+      case ARM64G_CC_OP_SBC32: {
+         /* (argL, argR, oldC) */
+         UInt  argL = cc_dep1;
+         UInt  argR = cc_dep2;
+         UInt  oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         ULong cf   = oldC ? (argL >= argR) : (argL > argR);
+         return cf;
+      }
+      case ARM64G_CC_OP_SBC64: {
+         /* (argL, argR, oldC) */
+         ULong argL = cc_dep1;
+         ULong argR = cc_dep2;
+         ULong oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         ULong cf   = oldC ? (argL >= argR) : (argL > argR);
+         return cf;
+      }
       case ARM64G_CC_OP_LOGIC32:
       case ARM64G_CC_OP_LOGIC64: {
          /* (res, unused, unused) */
@@ -442,26 +500,46 @@ ULong arm64g_calculate_flag_v ( ULong cc_op, ULong cc_dep1,
          ULong vf   = (((argL ^ argR) & (argL ^ res))) >> 63;
          return vf;
       }
-//ZZ       case ARMG_CC_OP_ADC: {
-//ZZ          /* (argL, argR, oldC) */
-//ZZ          UInt argL = cc_dep1;
-//ZZ          UInt argR = cc_dep2;
-//ZZ          UInt oldC = cc_dep3;
-//ZZ          vassert((oldC & ~1) == 0);
-//ZZ          UInt res  = argL + argR + oldC;
-//ZZ          UInt vf   = ((res ^ argL) & (res ^ argR)) >> 31;
-//ZZ          return vf;
-//ZZ       }
-//ZZ       case ARMG_CC_OP_SBB: {
-//ZZ          /* (argL, argR, oldC) */
-//ZZ          UInt argL = cc_dep1;
-//ZZ          UInt argR = cc_dep2;
-//ZZ          UInt oldC = cc_dep3;
-//ZZ          vassert((oldC & ~1) == 0);
-//ZZ          UInt res  = argL - argR - (oldC ^ 1);
-//ZZ          UInt vf   = ((argL ^ argR) & (argL ^ res)) >> 31;
-//ZZ          return vf;
-//ZZ       }
+      case ARM64G_CC_OP_ADC32: {
+         /* (argL, argR, oldC) */
+         UInt  argL = cc_dep1;
+         UInt  argR = cc_dep2;
+         UInt  oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         UInt  res  = argL + argR + oldC;
+         ULong vf   = (ULong)(((res ^ argL) & (res ^ argR)) >> 31);
+         return vf;
+      }
+      case ARM64G_CC_OP_ADC64: {
+         /* (argL, argR, oldC) */
+         ULong argL = cc_dep1;
+         ULong argR = cc_dep2;
+         ULong oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         ULong res  = argL + argR + oldC;
+         ULong vf   = ((res ^ argL) & (res ^ argR)) >> 63;
+         return vf;
+      }
+      case ARM64G_CC_OP_SBC32: {
+         /* (argL, argR, oldC) */
+         UInt  argL = cc_dep1;
+         UInt  argR = cc_dep2;
+         UInt  oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         UInt  res  = argL - argR - (oldC ^ 1);
+         ULong vf   = (ULong)(((argL ^ argR) & (argL ^ res)) >> 31);
+         return vf;
+      }
+      case ARM64G_CC_OP_SBC64: {
+         /* (argL, argR, oldC) */
+         ULong argL = cc_dep1;
+         ULong argR = cc_dep2;
+         ULong oldC = cc_dep3;
+         vassert((oldC & ~1) == 0);
+         ULong res  = argL - argR - (oldC ^ 1);
+         ULong vf   = ((argL ^ argR) & (argL ^ res)) >> 63;
+         return vf;
+      }
       case ARM64G_CC_OP_LOGIC32:
       case ARM64G_CC_OP_LOGIC64: {
          /* (res, unused, unused) */
index d0953683d46b2f67e04e667b4e0883d3b024d5c4..72f73e5d5e795db2178b0b3fe5c50e002aa729ae 100644 (file)
@@ -1316,30 +1316,30 @@ static IRExpr* mk_arm64g_calculate_condition ( ARM64Condcode cond )
 }
 
 
-//ZZ /* Build IR to calculate just the carry flag from stored
-//ZZ    CC_OP/CC_DEP1/CC_DEP2/CC_NDEP.  Returns an expression ::
-//ZZ    Ity_I32. */
-//ZZ static IRExpr* mk_armg_calculate_flag_c ( void )
-//ZZ {
-//ZZ    IRExpr** args
-//ZZ       = mkIRExprVec_4( IRExpr_Get(OFFB_CC_OP,   Ity_I32),
-//ZZ                        IRExpr_Get(OFFB_CC_DEP1, Ity_I32),
-//ZZ                        IRExpr_Get(OFFB_CC_DEP2, Ity_I32),
-//ZZ                        IRExpr_Get(OFFB_CC_NDEP, Ity_I32) );
-//ZZ    IRExpr* call
-//ZZ       = mkIRExprCCall(
-//ZZ            Ity_I32,
-//ZZ            0/*regparm*/, 
-//ZZ            "armg_calculate_flag_c", &armg_calculate_flag_c,
-//ZZ            args
-//ZZ         );
-//ZZ    /* Exclude OP and NDEP from definedness checking.  We're only
-//ZZ       interested in DEP1 and DEP2. */
-//ZZ    call->Iex.CCall.cee->mcx_mask = (1<<0) | (1<<3);
-//ZZ    return call;
-//ZZ }
-//ZZ 
-//ZZ 
+/* Build IR to calculate just the carry flag from stored
+   CC_OP/CC_DEP1/CC_DEP2/CC_NDEP.  Returns an expression ::
+   Ity_I64. */
+static IRExpr* mk_arm64g_calculate_flag_c ( void )
+{
+   IRExpr** args
+      = mkIRExprVec_4( IRExpr_Get(OFFB_CC_OP,   Ity_I64),
+                       IRExpr_Get(OFFB_CC_DEP1, Ity_I64),
+                       IRExpr_Get(OFFB_CC_DEP2, Ity_I64),
+                       IRExpr_Get(OFFB_CC_NDEP, Ity_I64) );
+   IRExpr* call
+      = mkIRExprCCall(
+           Ity_I64,
+           0/*regparm*/,
+           "arm64g_calculate_flag_c", &arm64g_calculate_flag_c,
+           args
+        );
+   /* Exclude OP and NDEP from definedness checking.  We're only
+      interested in DEP1 and DEP2. */
+   call->Iex.CCall.cee->mcx_mask = (1<<0) | (1<<3);
+   return call;
+}
+
+
 //ZZ /* Build IR to calculate just the overflow flag from stored
 //ZZ    CC_OP/CC_DEP1/CC_DEP2/CC_NDEP.  Returns an expression ::
 //ZZ    Ity_I32. */
@@ -1428,6 +1428,35 @@ void setFlags_ADD_SUB ( Bool is64, Bool isSUB, IRTemp argL, IRTemp argR )
    setFlags_D1_D2_ND(cc_op, argL64, argR64, z64);
 }
 
+/* Build IR to set the flags thunk after ADC or SBC. */
+static
+void setFlags_ADC_SBC ( Bool is64, Bool isSBC,
+                        IRTemp argL, IRTemp argR, IRTemp oldC )
+{
+   IRTemp argL64 = IRTemp_INVALID;
+   IRTemp argR64 = IRTemp_INVALID;
+   IRTemp oldC64 = IRTemp_INVALID;
+   if (is64) {
+      argL64 = argL;
+      argR64 = argR;
+      oldC64 = oldC;
+   } else {
+      argL64 = newTemp(Ity_I64);
+      argR64 = newTemp(Ity_I64);
+      oldC64 = newTemp(Ity_I64);
+      assign(argL64, unop(Iop_32Uto64, mkexpr(argL)));
+      assign(argR64, unop(Iop_32Uto64, mkexpr(argR)));
+      assign(oldC64, unop(Iop_32Uto64, mkexpr(oldC)));
+   }
+   UInt cc_op = ARM64G_CC_OP_NUMBER;
+   /**/ if ( isSBC &&  is64) { cc_op = ARM64G_CC_OP_SBC64; }
+   else if ( isSBC && !is64) { cc_op = ARM64G_CC_OP_SBC32; }
+   else if (!isSBC &&  is64) { cc_op = ARM64G_CC_OP_ADC64; }
+   else if (!isSBC && !is64) { cc_op = ARM64G_CC_OP_ADC32; }
+   else                      { vassert(0); }
+   setFlags_D1_D2_ND(cc_op, argL64, argR64, oldC64);
+}
+
 /* Build IR to set the flags thunk after ADD or SUB, if the given
    condition evaluates to True at run time.  If not, the flags are set
    to the specified NZCV value. */
@@ -2308,6 +2337,68 @@ Bool dis_ARM64_data_processing_register(/*MB_OUT*/DisResult* dres,
       }
    }
 
+   /* ------------------- ADC/SBC(reg) ------------------- */
+   /* x==0 => 32 bit op      x==1 => 64 bit op
+
+      31 30 29 28    23 21 20 15     9  4
+      |  |  |  |     |  |  |  |      |  |
+      x  0  0  11010 00 0  Rm 000000 Rn Rd   ADC  Rd,Rn,Rm
+      x  0  1  11010 00 0  Rm 000000 Rn Rd   ADCS Rd,Rn,Rm
+      x  1  0  11010 00 0  Rm 000000 Rn Rd   SBC  Rd,Rn,Rm
+      x  1  1  11010 00 0  Rm 000000 Rn Rd   SBCS Rd,Rn,Rm
+   */
+
+   if (INSN(28,21) == BITS8(1,1,0,1,0,0,0,0) && INSN(15,10) == 0 ) {
+      UInt   bX    = INSN(31,31);
+      UInt   bOP   = INSN(30,30); /* 0: ADC, 1: SBC */
+      UInt   bS    = INSN(29,29); /* set flags */
+      UInt   rM    = INSN(20,16);
+      UInt   rN    = INSN(9,5);
+      UInt   rD    = INSN(4,0);
+
+      Bool   isSUB = bOP == 1;
+      Bool   is64  = bX == 1;
+      IRType ty    = is64 ? Ity_I64 : Ity_I32;
+
+      IRTemp oldC = newTemp(ty);
+      assign(oldC,
+             is64 ? mk_arm64g_calculate_flag_c()
+                  : unop(Iop_64to32, mk_arm64g_calculate_flag_c()) );
+
+      IRTemp argL = newTemp(ty);
+      assign(argL, getIRegOrZR(is64, rN));
+      IRTemp argR = newTemp(ty);
+      assign(argR, getIRegOrZR(is64, rM));
+
+      IROp   op   = isSUB ? mkSUB(ty) : mkADD(ty);
+      IRTemp res  = newTemp(ty);
+      if (isSUB) {
+         IRExpr* one = is64 ? mkU64(1) : mkU32(1);
+         IROp xorOp = is64 ? Iop_Xor64 : Iop_Xor32;
+         assign(res,
+                binop(op,
+                      binop(op, mkexpr(argL), mkexpr(argR)),
+                      binop(xorOp, mkexpr(oldC), one)));
+      } else {
+         assign(res,
+                binop(op,
+                      binop(op, mkexpr(argL), mkexpr(argR)),
+                      mkexpr(oldC)));
+      }
+
+      if (rD != 31) putIRegOrZR(is64, rD, mkexpr(res));
+
+      if (bS) {
+         setFlags_ADC_SBC(is64, isSUB, argL, argR, oldC);
+      }
+
+      DIP("%s%s %s, %s, %s\n",
+          bOP ? "sbc" : "adc", bS ? "s" : "",
+          nameIRegOrZR(is64, rD), nameIRegOrZR(is64, rN),
+          nameIRegOrZR(is64, rM));
+      return True;
+   }
+
    /* -------------------- LOGIC(reg) -------------------- */   
    /* x==0 => 32 bit op      x==1 => 64 bit op
       N==0 => inv? is no-op (no inversion)