]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
arm64 front end: ufbm/sfbm: handle plain shifts explicitly
authorJulian Seward <jseward@acm.org>
Thu, 17 Dec 2020 16:40:46 +0000 (17:40 +0100)
committerJulian Seward <jseward@acm.org>
Thu, 17 Dec 2020 16:40:46 +0000 (17:40 +0100)
The ufbm and sfbm instructions implement some kind of semi-magical rotate,
mask and sign/zero-extend functionality.  Boring old left and right shifts are
special cases of it.  The existing translation into IR is correct, but has the
disadvantage that the IR optimiser isn't clever enough to simplify the
resulting IR back into a single shift in the case where the instruction is
used simply to encode a shift.  This induces inefficiency and it also makes
the resulting disassembly pretty difficult to read, if you're into that kind
of thing.

This commit does the obvious thing: detects cases where the required behaviour
is just a single shift, and emits IR and disassembly-printing accordingly.
All other cases fall through to the existing general-case handling and so are
unchanged.

VEX/priv/guest_arm64_toIR.c

index d242d43c07af33a591890dfc880523d8ea054921..14cbd1ea3dfaf07a1707dd141e0a063a9a8f5d12 100644 (file)
@@ -2694,6 +2694,65 @@ Bool dis_ARM64_data_processing_immediate(/*MB_OUT*/DisResult* dres,
       Bool   is64 = sf == 1;
       IRType ty   = is64 ? Ity_I64 : Ity_I32;
 
+      // Handle plain shifts explicitly.  These are functionally identical to
+      // the general case below, but iropt isn't clever enough to reduce those
+      // sequences to plain shifts.  So give it a hand.
+      if (is64 && immS == 63 && immR >= 1 && immR <= 63) {
+         if (opc == BITS2(0,0)) {
+            // 64-bit signed shift right
+            putIReg64orZR(dd, binop(Iop_Sar64, getIReg64orZR(nn), mkU8(immR)));
+            DIP("asr %s, %s, #%u\n",
+                nameIRegOrZR(is64, dd), nameIRegOrZR(is64, nn), immR);
+            return True;
+         }
+         if (opc == BITS2(1,0)) {
+            // 64-bit unsigned shift right
+            putIReg64orZR(dd, binop(Iop_Shr64, getIReg64orZR(nn), mkU8(immR)));
+            DIP("lsr %s, %s, #%u\n",
+                nameIRegOrZR(is64, dd), nameIRegOrZR(is64, nn), immR);
+            return True;
+         }
+      }
+
+      if (!is64 && immS == 31 && immR >= 1 && immR <= 31) {
+         if (opc == BITS2(0,0)) {
+            // 32-bit signed shift right
+            putIReg32orZR(dd, binop(Iop_Sar32, getIReg32orZR(nn), mkU8(immR)));
+            DIP("asr %s, %s, #%u\n",
+                nameIRegOrZR(is64, dd), nameIRegOrZR(is64, nn), immR);
+            return True;
+         }
+         if (opc == BITS2(1,0)) {
+            // 32-bit unsigned shift right
+            putIReg32orZR(dd, binop(Iop_Shr32, getIReg32orZR(nn), mkU8(immR)));
+            DIP("lsr %s, %s, #%u\n",
+                nameIRegOrZR(is64, dd), nameIRegOrZR(is64, nn), immR);
+            return True;
+         }
+      }
+
+      if (is64 && immS >= 0 && immS <= 62
+          && immR == immS + 1 && opc == BITS2(1,0)) {
+         // 64-bit shift left
+         UInt shift = 64 - immR;
+         vassert(shift >= 1 && shift <= 63);
+         putIReg64orZR(dd, binop(Iop_Shl64, getIReg64orZR(nn), mkU8(shift)));
+         DIP("lsl %s, %s, #%u\n",
+             nameIRegOrZR(is64, dd), nameIRegOrZR(is64, nn), shift);
+         return True;
+      }
+      if (!is64 && immS >= 0 && immS <= 30
+          && immR == immS + 1 && opc == BITS2(1,0)) {
+         // 32-bit shift left
+         UInt shift = 32 - immR;
+         vassert(shift >= 1 && shift <= 31);
+         putIReg32orZR(dd, binop(Iop_Shl32, getIReg32orZR(nn), mkU8(shift)));
+         DIP("lsl %s, %s, #%u\n",
+             nameIRegOrZR(is64, dd), nameIRegOrZR(is64, nn), shift);
+         return True;
+      }
+
+      // No luck.  We have to use the (slow) general case.
       IRTemp dst = newTemp(ty);
       IRTemp src = newTemp(ty);
       IRTemp bot = newTemp(ty);