[arm64][turbofan]: Handle any immediate shift.
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/1179733004 Cr-Commit-Position: refs/heads/master@{#28943}
This commit is contained in:
parent
d9e009f900
commit
36d771bbfa
@ -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)
|
||||
|
||||
|
||||
|
@ -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;
|
||||
Arm64OperandGenerator g(selector);
|
||||
Matcher m(node);
|
||||
if (g.CanBeImmediate(m.right().node(), imm_mode)) {
|
||||
*opcode |= AddressingModeField::encode(addressing_mode);
|
||||
return true;
|
||||
}
|
||||
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));
|
||||
Arm64OperandGenerator g(selector);
|
||||
|
||||
if (node->InputCount() != 2) return false;
|
||||
if (!g.IsIntegerConstant(node->InputAt(1))) return false;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -548,17 +555,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.
|
||||
@ -585,17 +595,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;
|
||||
}
|
||||
@ -724,20 +736,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;
|
||||
}
|
||||
@ -750,23 +763,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;
|
||||
}
|
||||
}
|
||||
|
@ -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)),
|
||||
@ -2202,14 +2206,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;
|
||||
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());
|
||||
@ -2218,14 +2225,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;
|
||||
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());
|
||||
@ -2238,7 +2246,10 @@ 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;
|
||||
@ -2246,7 +2257,7 @@ TEST_F(InstructionSelectorTest, Word64ShrWithWord64AndWithImmediate) {
|
||||
((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());
|
||||
@ -2255,7 +2266,8 @@ 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;
|
||||
@ -2263,7 +2275,7 @@ TEST_F(InstructionSelectorTest, Word64ShrWithWord64AndWithImmediate) {
|
||||
((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());
|
||||
@ -2276,11 +2288,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());
|
||||
@ -2291,12 +2306,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());
|
||||
@ -2310,11 +2327,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());
|
||||
@ -2325,12 +2345,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());
|
||||
|
Loading…
Reference in New Issue
Block a user