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) {
&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:
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;
}
}
-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;
+ }
}
// 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.
// 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;
}
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;
}
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;
}
}
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)),
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)),
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());
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());
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());
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());
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());
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());
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());
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());