Reland [arm64][turbofan]: Handle any immediate shift.
authorjacob.bramley <jacob.bramley@arm.com>
Fri, 12 Jun 2015 05:03:01 +0000 (22:03 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 12 Jun 2015 05:03:18 +0000 (05:03 +0000)
With this patch, we can generate simple immediate-shift instructions for
immediates outside the range "0 <= imm < width". Several related
instruction selectors have also been updated accordingly.

Example of generated code:

    ---- Before ---         ---- After ----
    movz w0, #33            lsr w0, w1, #1
    lsr  w0, w1, w0

BUG=

Review URL: https://codereview.chromium.org/1179893003

Cr-Commit-Position: refs/heads/master@{#28977}

src/compiler/arm64/code-generator-arm64.cc
src/compiler/arm64/instruction-selector-arm64.cc
test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc

index 3f2f1c9..831627d 100644 (file)
@@ -331,16 +331,17 @@ Condition FlagsConditionToCondition(FlagsCondition condition) {
   } while (0)
 
 
-#define ASSEMBLE_SHIFT(asm_instr, width)                                       \
-  do {                                                                         \
-    if (instr->InputAt(1)->IsRegister()) {                                     \
-      __ asm_instr(i.OutputRegister##width(), i.InputRegister##width(0),       \
-                   i.InputRegister##width(1));                                 \
-    } else {                                                                   \
-      int imm =                                                                \
-          static_cast<int>(i.InputOperand##width(1).immediate().value());      \
-      __ asm_instr(i.OutputRegister##width(), i.InputRegister##width(0), imm); \
-    }                                                                          \
+#define ASSEMBLE_SHIFT(asm_instr, width)                                    \
+  do {                                                                      \
+    if (instr->InputAt(1)->IsRegister()) {                                  \
+      __ asm_instr(i.OutputRegister##width(), i.InputRegister##width(0),    \
+                   i.InputRegister##width(1));                              \
+    } else {                                                                \
+      uint32_t imm =                                                        \
+          static_cast<uint32_t>(i.InputOperand##width(1).ImmediateValue()); \
+      __ asm_instr(i.OutputRegister##width(), i.InputRegister##width(0),    \
+                   imm % (width));                                          \
+    }                                                                       \
   } while (0)
 
 
index 6fd9084..2d8c030 100644 (file)
@@ -37,15 +37,31 @@ class Arm64OperandGenerator final : public OperandGenerator {
     return UseRegister(node);
   }
 
+  // Use the provided node if it has the required value, or create a
+  // TempImmediate otherwise.
+  InstructionOperand UseImmediateOrTemp(Node* node, int32_t value) {
+    if (GetIntegerConstantValue(node) == value) {
+      return UseImmediate(node);
+    }
+    return TempImmediate(value);
+  }
+
+  bool IsIntegerConstant(Node* node) {
+    return (node->opcode() == IrOpcode::kInt32Constant) ||
+           (node->opcode() == IrOpcode::kInt64Constant);
+  }
+
+  int64_t GetIntegerConstantValue(Node* node) {
+    if (node->opcode() == IrOpcode::kInt32Constant) {
+      return OpParameter<int32_t>(node);
+    }
+    DCHECK(node->opcode() == IrOpcode::kInt64Constant);
+    return OpParameter<int64_t>(node);
+  }
+
   bool CanBeImmediate(Node* node, ImmediateMode mode) {
-    int64_t value;
-    if (node->opcode() == IrOpcode::kInt32Constant)
-      value = OpParameter<int32_t>(node);
-    else if (node->opcode() == IrOpcode::kInt64Constant)
-      value = OpParameter<int64_t>(node);
-    else
-      return false;
-    return CanBeImmediate(value, mode);
+    return IsIntegerConstant(node) &&
+           CanBeImmediate(GetIntegerConstantValue(node), mode);
   }
 
   bool CanBeImmediate(int64_t value, ImmediateMode mode) {
@@ -61,10 +77,6 @@ class Arm64OperandGenerator final : public OperandGenerator {
                                        &ignored, &ignored, &ignored);
       case kArithmeticImm:
         return Assembler::IsImmAddSub(value);
-      case kShift32Imm:
-        return 0 <= value && value < 32;
-      case kShift64Imm:
-        return 0 <= value && value < 64;
       case kLoadStoreImm8:
         return IsLoadStoreImmediate(value, LSByte);
       case kLoadStoreImm16:
@@ -75,6 +87,12 @@ class Arm64OperandGenerator final : public OperandGenerator {
         return IsLoadStoreImmediate(value, LSDoubleWord);
       case kNoImmediate:
         return false;
+      case kShift32Imm:  // Fall through.
+      case kShift64Imm:
+        // Shift operations only observe the bottom 5 or 6 bits of the value.
+        // All possible shifts can be encoded by discarding bits which have no
+        // effect.
+        return true;
     }
     return false;
   }
@@ -113,47 +131,36 @@ void VisitRRO(InstructionSelector* selector, ArchOpcode opcode, Node* node,
 }
 
 
-template <typename Matcher>
-bool TryMatchShift(InstructionSelector* selector, Node* node,
-                   InstructionCode* opcode, IrOpcode::Value shift_opcode,
-                   ImmediateMode imm_mode, AddressingMode addressing_mode) {
-  if (node->opcode() != shift_opcode) return false;
+bool TryMatchAnyShift(InstructionSelector* selector, Node* node,
+                      InstructionCode* opcode, bool try_ror) {
   Arm64OperandGenerator g(selector);
-  Matcher m(node);
-  if (g.CanBeImmediate(m.right().node(), imm_mode)) {
-    *opcode |= AddressingModeField::encode(addressing_mode);
-    return true;
-  }
-  return false;
-}
 
+  if (node->InputCount() != 2) return false;
+  if (!g.IsIntegerConstant(node->InputAt(1))) return false;
 
-bool TryMatchAnyShift(InstructionSelector* selector, Node* node,
-                      InstructionCode* opcode, bool try_ror) {
-  return TryMatchShift<Int32BinopMatcher>(selector, node, opcode,
-                                          IrOpcode::kWord32Shl, kShift32Imm,
-                                          kMode_Operand2_R_LSL_I) ||
-         TryMatchShift<Int32BinopMatcher>(selector, node, opcode,
-                                          IrOpcode::kWord32Shr, kShift32Imm,
-                                          kMode_Operand2_R_LSR_I) ||
-         TryMatchShift<Int32BinopMatcher>(selector, node, opcode,
-                                          IrOpcode::kWord32Sar, kShift32Imm,
-                                          kMode_Operand2_R_ASR_I) ||
-         (try_ror && TryMatchShift<Int32BinopMatcher>(
-                         selector, node, opcode, IrOpcode::kWord32Ror,
-                         kShift32Imm, kMode_Operand2_R_ROR_I)) ||
-         TryMatchShift<Int64BinopMatcher>(selector, node, opcode,
-                                          IrOpcode::kWord64Shl, kShift64Imm,
-                                          kMode_Operand2_R_LSL_I) ||
-         TryMatchShift<Int64BinopMatcher>(selector, node, opcode,
-                                          IrOpcode::kWord64Shr, kShift64Imm,
-                                          kMode_Operand2_R_LSR_I) ||
-         TryMatchShift<Int64BinopMatcher>(selector, node, opcode,
-                                          IrOpcode::kWord64Sar, kShift64Imm,
-                                          kMode_Operand2_R_ASR_I) ||
-         (try_ror && TryMatchShift<Int64BinopMatcher>(
-                         selector, node, opcode, IrOpcode::kWord64Ror,
-                         kShift64Imm, kMode_Operand2_R_ROR_I));
+  switch (node->opcode()) {
+    case IrOpcode::kWord32Shl:
+    case IrOpcode::kWord64Shl:
+      *opcode |= AddressingModeField::encode(kMode_Operand2_R_LSL_I);
+      return true;
+    case IrOpcode::kWord32Shr:
+    case IrOpcode::kWord64Shr:
+      *opcode |= AddressingModeField::encode(kMode_Operand2_R_LSR_I);
+      return true;
+    case IrOpcode::kWord32Sar:
+    case IrOpcode::kWord64Sar:
+      *opcode |= AddressingModeField::encode(kMode_Operand2_R_ASR_I);
+      return true;
+    case IrOpcode::kWord32Ror:
+    case IrOpcode::kWord64Ror:
+      if (try_ror) {
+        *opcode |= AddressingModeField::encode(kMode_Operand2_R_ROR_I);
+        return true;
+      }
+      return false;
+    default:
+      return false;
+  }
 }
 
 
@@ -564,17 +571,20 @@ void InstructionSelector::VisitWord32And(Node* node) {
       // Select Ubfx for And(Shr(x, imm), mask) where the mask is in the least
       // significant bits.
       Int32BinopMatcher mleft(m.left().node());
-      if (mleft.right().IsInRange(0, 31)) {
+      if (mleft.right().HasValue()) {
+        // Any shift value can match; int32 shifts use `value % 32`.
+        uint32_t lsb = mleft.right().Value() & 0x1f;
+
         // Ubfx cannot extract bits past the register size, however since
         // shifting the original value would have introduced some zeros we can
         // still use ubfx with a smaller mask and the remaining bits will be
         // zeros.
-        uint32_t lsb = mleft.right().Value();
         if (lsb + mask_width > 32) mask_width = 32 - lsb;
 
         Emit(kArm64Ubfx32, g.DefineAsRegister(node),
              g.UseRegister(mleft.left().node()),
-             g.UseImmediate(mleft.right().node()), g.TempImmediate(mask_width));
+             g.UseImmediateOrTemp(mleft.right().node(), lsb),
+             g.TempImmediate(mask_width));
         return;
       }
       // Other cases fall through to the normal And operation.
@@ -601,17 +611,19 @@ void InstructionSelector::VisitWord64And(Node* node) {
       // Select Ubfx for And(Shr(x, imm), mask) where the mask is in the least
       // significant bits.
       Int64BinopMatcher mleft(m.left().node());
-      if (mleft.right().IsInRange(0, 63)) {
+      if (mleft.right().HasValue()) {
+        // Any shift value can match; int64 shifts use `value % 64`.
+        uint32_t lsb = static_cast<uint32_t>(mleft.right().Value() & 0x3f);
+
         // Ubfx cannot extract bits past the register size, however since
         // shifting the original value would have introduced some zeros we can
         // still use ubfx with a smaller mask and the remaining bits will be
         // zeros.
-        uint64_t lsb = mleft.right().Value();
         if (lsb + mask_width > 64) mask_width = 64 - lsb;
 
         Emit(kArm64Ubfx, g.DefineAsRegister(node),
              g.UseRegister(mleft.left().node()),
-             g.UseImmediate(mleft.right().node()),
+             g.UseImmediateOrTemp(mleft.right().node(), lsb),
              g.TempImmediate(static_cast<int32_t>(mask_width)));
         return;
       }
@@ -740,20 +752,21 @@ bool TryEmitBitfieldExtract32(InstructionSelector* selector, Node* node) {
 
 void InstructionSelector::VisitWord32Shr(Node* node) {
   Int32BinopMatcher m(node);
-  if (m.left().IsWord32And() && m.right().IsInRange(0, 31)) {
-    uint32_t lsb = m.right().Value();
+  if (m.left().IsWord32And() && m.right().HasValue()) {
+    uint32_t lsb = m.right().Value() & 0x1f;
     Int32BinopMatcher mleft(m.left().node());
     if (mleft.right().HasValue()) {
-      uint32_t mask = (mleft.right().Value() >> lsb) << lsb;
-      uint32_t mask_width = base::bits::CountPopulation32(mask);
-      uint32_t mask_msb = base::bits::CountLeadingZeros32(mask);
       // Select Ubfx for Shr(And(x, mask), imm) where the result of the mask is
       // shifted into the least-significant bits.
+      uint32_t mask = (mleft.right().Value() >> lsb) << lsb;
+      unsigned mask_width = base::bits::CountPopulation32(mask);
+      unsigned mask_msb = base::bits::CountLeadingZeros32(mask);
       if ((mask_msb + mask_width + lsb) == 32) {
         Arm64OperandGenerator g(this);
         DCHECK_EQ(lsb, base::bits::CountTrailingZeros32(mask));
         Emit(kArm64Ubfx32, g.DefineAsRegister(node),
-             g.UseRegister(mleft.left().node()), g.TempImmediate(lsb),
+             g.UseRegister(mleft.left().node()),
+             g.UseImmediateOrTemp(m.right().node(), lsb),
              g.TempImmediate(mask_width));
         return;
       }
@@ -782,23 +795,23 @@ void InstructionSelector::VisitWord32Shr(Node* node) {
 
 
 void InstructionSelector::VisitWord64Shr(Node* node) {
-  Arm64OperandGenerator g(this);
   Int64BinopMatcher m(node);
-  if (m.left().IsWord64And() && m.right().IsInRange(0, 63)) {
-    uint64_t lsb = m.right().Value();
+  if (m.left().IsWord64And() && m.right().HasValue()) {
+    uint32_t lsb = m.right().Value() & 0x3f;
     Int64BinopMatcher mleft(m.left().node());
     if (mleft.right().HasValue()) {
       // Select Ubfx for Shr(And(x, mask), imm) where the result of the mask is
       // shifted into the least-significant bits.
       uint64_t mask = (mleft.right().Value() >> lsb) << lsb;
-      uint64_t mask_width = base::bits::CountPopulation64(mask);
-      uint64_t mask_msb = base::bits::CountLeadingZeros64(mask);
+      unsigned mask_width = base::bits::CountPopulation64(mask);
+      unsigned mask_msb = base::bits::CountLeadingZeros64(mask);
       if ((mask_msb + mask_width + lsb) == 64) {
+        Arm64OperandGenerator g(this);
         DCHECK_EQ(lsb, base::bits::CountTrailingZeros64(mask));
         Emit(kArm64Ubfx, g.DefineAsRegister(node),
              g.UseRegister(mleft.left().node()),
-             g.TempImmediate(static_cast<int32_t>(lsb)),
-             g.TempImmediate(static_cast<int32_t>(mask_width)));
+             g.UseImmediateOrTemp(m.right().node(), lsb),
+             g.TempImmediate(mask_width));
         return;
       }
     }
index e52d1d5..16aa151 100644 (file)
@@ -642,7 +642,9 @@ TEST_F(InstructionSelectorTest, AddShiftByImmediateOnLeft) {
     if (shift.mi.machine_type != kMachInt32) continue;
     if (shift.mi.arch_opcode == kArm64Ror32) continue;
 
-    TRACED_FORRANGE(int, imm, 0, 31) {
+    // The available shift operand range is `0 <= imm < 32`, but we also test
+    // that immediates outside this range are handled properly (modulo-32).
+    TRACED_FORRANGE(int, imm, -32, 63) {
       StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
       m.Return((m.Int32Add)(
           (m.*shift.mi.constructor)(m.Parameter(1), m.Int32Constant(imm)),
@@ -663,7 +665,9 @@ TEST_F(InstructionSelectorTest, AddShiftByImmediateOnLeft) {
     if (shift.mi.machine_type != kMachInt64) continue;
     if (shift.mi.arch_opcode == kArm64Ror) continue;
 
-    TRACED_FORRANGE(int, imm, 0, 63) {
+    // The available shift operand range is `0 <= imm < 64`, but we also test
+    // that immediates outside this range are handled properly (modulo-64).
+    TRACED_FORRANGE(int, imm, -64, 127) {
       StreamBuilder m(this, kMachInt64, kMachInt64, kMachInt64);
       m.Return((m.Int64Add)(
           (m.*shift.mi.constructor)(m.Parameter(1), m.Int64Constant(imm)),
@@ -2394,14 +2398,17 @@ TEST_F(InstructionSelectorTest, Word64XorMinusOneWithParameter) {
 
 
 TEST_F(InstructionSelectorTest, Word32ShrWithWord32AndWithImmediate) {
-  TRACED_FORRANGE(int32_t, lsb, 1, 31) {
+  // The available shift operand range is `0 <= imm < 32`, but we also test
+  // that immediates outside this range are handled properly (modulo-32).
+  TRACED_FORRANGE(int32_t, shift, -32, 63) {
+    int32_t lsb = shift & 0x1f;
     TRACED_FORRANGE(int32_t, width, 1, 32 - lsb) {
       uint32_t jnk = rng()->NextInt();
-      jnk >>= 32 - lsb;
+      jnk = (lsb > 0) ? (jnk >> (32 - lsb)) : 0;
       uint32_t msk = ((0xffffffffu >> (32 - width)) << lsb) | jnk;
       StreamBuilder m(this, kMachInt32, kMachInt32);
       m.Return(m.Word32Shr(m.Word32And(m.Parameter(0), m.Int32Constant(msk)),
-                           m.Int32Constant(lsb)));
+                           m.Int32Constant(shift)));
       Stream s = m.Build();
       ASSERT_EQ(1U, s.size());
       EXPECT_EQ(kArm64Ubfx32, s[0]->arch_opcode());
@@ -2410,14 +2417,15 @@ TEST_F(InstructionSelectorTest, Word32ShrWithWord32AndWithImmediate) {
       EXPECT_EQ(width, s.ToInt32(s[0]->InputAt(2)));
     }
   }
-  TRACED_FORRANGE(int32_t, lsb, 1, 31) {
+  TRACED_FORRANGE(int32_t, shift, -32, 63) {
+    int32_t lsb = shift & 0x1f;
     TRACED_FORRANGE(int32_t, width, 1, 32 - lsb) {
       uint32_t jnk = rng()->NextInt();
-      jnk >>= 32 - lsb;
+      jnk = (lsb > 0) ? (jnk >> (32 - lsb)) : 0;
       uint32_t msk = ((0xffffffffu >> (32 - width)) << lsb) | jnk;
       StreamBuilder m(this, kMachInt32, kMachInt32);
       m.Return(m.Word32Shr(m.Word32And(m.Int32Constant(msk), m.Parameter(0)),
-                           m.Int32Constant(lsb)));
+                           m.Int32Constant(shift)));
       Stream s = m.Build();
       ASSERT_EQ(1U, s.size());
       EXPECT_EQ(kArm64Ubfx32, s[0]->arch_opcode());
@@ -2430,15 +2438,18 @@ TEST_F(InstructionSelectorTest, Word32ShrWithWord32AndWithImmediate) {
 
 
 TEST_F(InstructionSelectorTest, Word64ShrWithWord64AndWithImmediate) {
-  TRACED_FORRANGE(int32_t, lsb, 1, 63) {
+  // The available shift operand range is `0 <= imm < 64`, but we also test
+  // that immediates outside this range are handled properly (modulo-64).
+  TRACED_FORRANGE(int32_t, shift, -64, 127) {
+    int32_t lsb = shift & 0x3f;
     TRACED_FORRANGE(int32_t, width, 1, 64 - lsb) {
       uint64_t jnk = rng()->NextInt64();
-      jnk >>= 64 - lsb;
+      jnk = (lsb > 0) ? (jnk >> (64 - lsb)) : 0;
       uint64_t msk =
           ((V8_UINT64_C(0xffffffffffffffff) >> (64 - width)) << lsb) | jnk;
       StreamBuilder m(this, kMachInt64, kMachInt64);
       m.Return(m.Word64Shr(m.Word64And(m.Parameter(0), m.Int64Constant(msk)),
-                           m.Int64Constant(lsb)));
+                           m.Int64Constant(shift)));
       Stream s = m.Build();
       ASSERT_EQ(1U, s.size());
       EXPECT_EQ(kArm64Ubfx, s[0]->arch_opcode());
@@ -2447,15 +2458,16 @@ TEST_F(InstructionSelectorTest, Word64ShrWithWord64AndWithImmediate) {
       EXPECT_EQ(width, s.ToInt64(s[0]->InputAt(2)));
     }
   }
-  TRACED_FORRANGE(int32_t, lsb, 1, 63) {
+  TRACED_FORRANGE(int32_t, shift, -64, 127) {
+    int32_t lsb = shift & 0x3f;
     TRACED_FORRANGE(int32_t, width, 1, 64 - lsb) {
       uint64_t jnk = rng()->NextInt64();
-      jnk >>= 64 - lsb;
+      jnk = (lsb > 0) ? (jnk >> (64 - lsb)) : 0;
       uint64_t msk =
           ((V8_UINT64_C(0xffffffffffffffff) >> (64 - width)) << lsb) | jnk;
       StreamBuilder m(this, kMachInt64, kMachInt64);
       m.Return(m.Word64Shr(m.Word64And(m.Int64Constant(msk), m.Parameter(0)),
-                           m.Int64Constant(lsb)));
+                           m.Int64Constant(shift)));
       Stream s = m.Build();
       ASSERT_EQ(1U, s.size());
       EXPECT_EQ(kArm64Ubfx, s[0]->arch_opcode());
@@ -2468,11 +2480,14 @@ TEST_F(InstructionSelectorTest, Word64ShrWithWord64AndWithImmediate) {
 
 
 TEST_F(InstructionSelectorTest, Word32AndWithImmediateWithWord32Shr) {
-  TRACED_FORRANGE(int32_t, lsb, 1, 31) {
+  // The available shift operand range is `0 <= imm < 32`, but we also test
+  // that immediates outside this range are handled properly (modulo-32).
+  TRACED_FORRANGE(int32_t, shift, -32, 63) {
+    int32_t lsb = shift & 0x1f;
     TRACED_FORRANGE(int32_t, width, 1, 31) {
       uint32_t msk = (1 << width) - 1;
       StreamBuilder m(this, kMachInt32, kMachInt32);
-      m.Return(m.Word32And(m.Word32Shr(m.Parameter(0), m.Int32Constant(lsb)),
+      m.Return(m.Word32And(m.Word32Shr(m.Parameter(0), m.Int32Constant(shift)),
                            m.Int32Constant(msk)));
       Stream s = m.Build();
       ASSERT_EQ(1U, s.size());
@@ -2483,12 +2498,14 @@ TEST_F(InstructionSelectorTest, Word32AndWithImmediateWithWord32Shr) {
       EXPECT_EQ(actual_width, s.ToInt32(s[0]->InputAt(2)));
     }
   }
-  TRACED_FORRANGE(int32_t, lsb, 1, 31) {
+  TRACED_FORRANGE(int32_t, shift, -32, 63) {
+    int32_t lsb = shift & 0x1f;
     TRACED_FORRANGE(int32_t, width, 1, 31) {
       uint32_t msk = (1 << width) - 1;
       StreamBuilder m(this, kMachInt32, kMachInt32);
-      m.Return(m.Word32And(m.Int32Constant(msk),
-                           m.Word32Shr(m.Parameter(0), m.Int32Constant(lsb))));
+      m.Return(
+          m.Word32And(m.Int32Constant(msk),
+                      m.Word32Shr(m.Parameter(0), m.Int32Constant(shift))));
       Stream s = m.Build();
       ASSERT_EQ(1U, s.size());
       EXPECT_EQ(kArm64Ubfx32, s[0]->arch_opcode());
@@ -2502,11 +2519,14 @@ TEST_F(InstructionSelectorTest, Word32AndWithImmediateWithWord32Shr) {
 
 
 TEST_F(InstructionSelectorTest, Word64AndWithImmediateWithWord64Shr) {
-  TRACED_FORRANGE(int64_t, lsb, 1, 63) {
+  // The available shift operand range is `0 <= imm < 64`, but we also test
+  // that immediates outside this range are handled properly (modulo-64).
+  TRACED_FORRANGE(int64_t, shift, -64, 127) {
+    int64_t lsb = shift & 0x3f;
     TRACED_FORRANGE(int64_t, width, 1, 63) {
       uint64_t msk = (V8_UINT64_C(1) << width) - 1;
       StreamBuilder m(this, kMachInt64, kMachInt64);
-      m.Return(m.Word64And(m.Word64Shr(m.Parameter(0), m.Int64Constant(lsb)),
+      m.Return(m.Word64And(m.Word64Shr(m.Parameter(0), m.Int64Constant(shift)),
                            m.Int64Constant(msk)));
       Stream s = m.Build();
       ASSERT_EQ(1U, s.size());
@@ -2517,12 +2537,14 @@ TEST_F(InstructionSelectorTest, Word64AndWithImmediateWithWord64Shr) {
       EXPECT_EQ(actual_width, s.ToInt64(s[0]->InputAt(2)));
     }
   }
-  TRACED_FORRANGE(int64_t, lsb, 1, 63) {
+  TRACED_FORRANGE(int64_t, shift, -64, 127) {
+    int64_t lsb = shift & 0x3f;
     TRACED_FORRANGE(int64_t, width, 1, 63) {
       uint64_t msk = (V8_UINT64_C(1) << width) - 1;
       StreamBuilder m(this, kMachInt64, kMachInt64);
-      m.Return(m.Word64And(m.Int64Constant(msk),
-                           m.Word64Shr(m.Parameter(0), m.Int64Constant(lsb))));
+      m.Return(
+          m.Word64And(m.Int64Constant(msk),
+                      m.Word64Shr(m.Parameter(0), m.Int64Constant(shift))));
       Stream s = m.Build();
       ASSERT_EQ(1U, s.size());
       EXPECT_EQ(kArm64Ubfx, s[0]->arch_opcode());