[turbofan] Support cmp with shift/extend on ARM64.
Support 32-bit cmp with shift/extend by reusing the existing add/sub shift and extend code. Review URL: https://codereview.chromium.org/1218103005 Cr-Commit-Position: refs/heads/master@{#29435}
This commit is contained in:
parent
70502d166f
commit
1297a51e30
@ -711,7 +711,7 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
|
||||
__ Cmp(i.InputRegister(0), i.InputOperand(1));
|
||||
break;
|
||||
case kArm64Cmp32:
|
||||
__ Cmp(i.InputRegister32(0), i.InputOperand32(1));
|
||||
__ Cmp(i.InputRegister32(0), i.InputOperand2_32(1));
|
||||
break;
|
||||
case kArm64Cmn:
|
||||
__ Cmn(i.InputRegister(0), i.InputOperand(1));
|
||||
|
@ -132,13 +132,14 @@ void VisitRRO(InstructionSelector* selector, ArchOpcode opcode, Node* node,
|
||||
|
||||
|
||||
bool TryMatchAnyShift(InstructionSelector* selector, Node* node,
|
||||
InstructionCode* opcode, bool try_ror) {
|
||||
Node* input_node, InstructionCode* opcode, bool try_ror) {
|
||||
Arm64OperandGenerator g(selector);
|
||||
|
||||
if (node->InputCount() != 2) return false;
|
||||
if (!g.IsIntegerConstant(node->InputAt(1))) return false;
|
||||
if (!selector->CanCover(node, input_node)) return false;
|
||||
if (input_node->InputCount() != 2) return false;
|
||||
if (!g.IsIntegerConstant(input_node->InputAt(1))) return false;
|
||||
|
||||
switch (node->opcode()) {
|
||||
switch (input_node->opcode()) {
|
||||
case IrOpcode::kWord32Shl:
|
||||
case IrOpcode::kWord64Shl:
|
||||
*opcode |= AddressingModeField::encode(kMode_Operand2_R_LSL_I);
|
||||
@ -165,9 +166,11 @@ bool TryMatchAnyShift(InstructionSelector* selector, Node* node,
|
||||
|
||||
|
||||
bool TryMatchAnyExtend(Arm64OperandGenerator* g, InstructionSelector* selector,
|
||||
Node* left_node, Node* right_node,
|
||||
Node* node, Node* left_node, Node* right_node,
|
||||
InstructionOperand* left_op,
|
||||
InstructionOperand* right_op, InstructionCode* opcode) {
|
||||
if (!selector->CanCover(node, right_node)) return false;
|
||||
|
||||
NodeMatcher nm(right_node);
|
||||
|
||||
if (nm.IsWord32And()) {
|
||||
@ -207,15 +210,20 @@ void VisitBinop(InstructionSelector* selector, Node* node,
|
||||
FlagsContinuation* cont) {
|
||||
Arm64OperandGenerator g(selector);
|
||||
Matcher m(node);
|
||||
InstructionOperand inputs[4];
|
||||
InstructionOperand inputs[5];
|
||||
size_t input_count = 0;
|
||||
InstructionOperand outputs[2];
|
||||
size_t output_count = 0;
|
||||
bool is_add_sub = false;
|
||||
bool is_cmp = opcode == kArm64Cmp32;
|
||||
|
||||
if (m.IsInt32Add() || m.IsInt64Add() || m.IsInt32Sub() || m.IsInt64Sub()) {
|
||||
is_add_sub = true;
|
||||
}
|
||||
// We can commute cmp by switching the inputs and commuting the flags
|
||||
// continuation.
|
||||
bool can_commute = m.HasProperty(Operator::kCommutative) || is_cmp;
|
||||
|
||||
// The cmp instruction is encoded as sub with zero output register, and
|
||||
// therefore supports the same operand modes.
|
||||
bool is_add_sub = m.IsInt32Add() || m.IsInt64Add() || m.IsInt32Sub() ||
|
||||
m.IsInt64Sub() || is_cmp;
|
||||
|
||||
Node* left_node = m.left().node();
|
||||
Node* right_node = m.right().node();
|
||||
@ -223,21 +231,28 @@ void VisitBinop(InstructionSelector* selector, Node* node,
|
||||
if (g.CanBeImmediate(right_node, operand_mode)) {
|
||||
inputs[input_count++] = g.UseRegister(left_node);
|
||||
inputs[input_count++] = g.UseImmediate(right_node);
|
||||
} else if (is_cmp && g.CanBeImmediate(left_node, operand_mode)) {
|
||||
cont->Commute();
|
||||
inputs[input_count++] = g.UseRegister(right_node);
|
||||
inputs[input_count++] = g.UseImmediate(left_node);
|
||||
} else if (is_add_sub &&
|
||||
TryMatchAnyExtend(&g, selector, left_node, right_node, &inputs[0],
|
||||
&inputs[1], &opcode)) {
|
||||
TryMatchAnyExtend(&g, selector, node, left_node, right_node,
|
||||
&inputs[0], &inputs[1], &opcode)) {
|
||||
input_count += 2;
|
||||
} else if (is_add_sub && m.HasProperty(Operator::kCommutative) &&
|
||||
TryMatchAnyExtend(&g, selector, right_node, left_node, &inputs[0],
|
||||
&inputs[1], &opcode)) {
|
||||
} else if (is_add_sub && can_commute &&
|
||||
TryMatchAnyExtend(&g, selector, node, right_node, left_node,
|
||||
&inputs[0], &inputs[1], &opcode)) {
|
||||
if (is_cmp) cont->Commute();
|
||||
input_count += 2;
|
||||
} else if (TryMatchAnyShift(selector, right_node, &opcode, !is_add_sub)) {
|
||||
} else if (TryMatchAnyShift(selector, node, right_node, &opcode,
|
||||
!is_add_sub)) {
|
||||
Matcher m_shift(right_node);
|
||||
inputs[input_count++] = g.UseRegister(left_node);
|
||||
inputs[input_count++] = g.UseRegister(m_shift.left().node());
|
||||
inputs[input_count++] = g.UseImmediate(m_shift.right().node());
|
||||
} else if (m.HasProperty(Operator::kCommutative) &&
|
||||
TryMatchAnyShift(selector, left_node, &opcode, !is_add_sub)) {
|
||||
} else if (can_commute && TryMatchAnyShift(selector, node, left_node, &opcode,
|
||||
!is_add_sub)) {
|
||||
if (is_cmp) cont->Commute();
|
||||
Matcher m_shift(left_node);
|
||||
inputs[input_count++] = g.UseRegister(right_node);
|
||||
inputs[input_count++] = g.UseRegister(m_shift.left().node());
|
||||
@ -252,13 +267,16 @@ void VisitBinop(InstructionSelector* selector, Node* node,
|
||||
inputs[input_count++] = g.Label(cont->false_block());
|
||||
}
|
||||
|
||||
outputs[output_count++] = g.DefineAsRegister(node);
|
||||
if (!is_cmp) {
|
||||
outputs[output_count++] = g.DefineAsRegister(node);
|
||||
}
|
||||
|
||||
if (cont->IsSet()) {
|
||||
outputs[output_count++] = g.DefineAsRegister(cont->result());
|
||||
}
|
||||
|
||||
DCHECK_NE(0u, input_count);
|
||||
DCHECK_NE(0u, output_count);
|
||||
DCHECK((output_count != 0) || is_cmp);
|
||||
DCHECK_GE(arraysize(inputs), input_count);
|
||||
DCHECK_GE(arraysize(outputs), output_count);
|
||||
|
||||
@ -1597,7 +1615,8 @@ void VisitWordCompare(InstructionSelector* selector, Node* node,
|
||||
|
||||
void VisitWord32Compare(InstructionSelector* selector, Node* node,
|
||||
FlagsContinuation* cont) {
|
||||
VisitWordCompare(selector, node, kArm64Cmp32, cont, false, kArithmeticImm);
|
||||
VisitBinop<Int32BinopMatcher>(selector, node, kArm64Cmp32, kArithmeticImm,
|
||||
cont);
|
||||
}
|
||||
|
||||
|
||||
@ -1756,8 +1775,7 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
|
||||
return VisitWordCompare(this, value, kArm64Cmn32, &cont, true,
|
||||
kArithmeticImm);
|
||||
case IrOpcode::kInt32Sub:
|
||||
return VisitWordCompare(this, value, kArm64Cmp32, &cont, false,
|
||||
kArithmeticImm);
|
||||
return VisitWord32Compare(this, value, &cont);
|
||||
case IrOpcode::kWord32And: {
|
||||
Int32BinopMatcher m(value);
|
||||
if (m.right().HasValue() &&
|
||||
|
@ -2237,6 +2237,190 @@ TEST_F(InstructionSelectorTest, Word64EqualWithZero) {
|
||||
}
|
||||
|
||||
|
||||
TEST_F(InstructionSelectorTest, Word32EqualWithWord32Shift) {
|
||||
TRACED_FOREACH(Shift, shift, kShiftInstructions) {
|
||||
// Skip non 32-bit shifts or ror operations.
|
||||
if (shift.mi.machine_type != kMachInt32 ||
|
||||
shift.mi.arch_opcode == kArm64Ror32) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TRACED_FORRANGE(int32_t, imm, -32, 63) {
|
||||
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* r = (m.*shift.mi.constructor)(p1, m.Int32Constant(imm));
|
||||
m.Return(m.Word32Equal(p0, r));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArm64Cmp32, s[0]->arch_opcode());
|
||||
EXPECT_EQ(shift.mode, s[0]->addressing_mode());
|
||||
ASSERT_EQ(3U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
EXPECT_EQ(imm, s.ToInt32(s[0]->InputAt(2)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
TRACED_FORRANGE(int32_t, imm, -32, 63) {
|
||||
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* r = (m.*shift.mi.constructor)(p1, m.Int32Constant(imm));
|
||||
m.Return(m.Word32Equal(r, p0));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArm64Cmp32, s[0]->arch_opcode());
|
||||
EXPECT_EQ(shift.mode, s[0]->addressing_mode());
|
||||
ASSERT_EQ(3U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
EXPECT_EQ(imm, s.ToInt32(s[0]->InputAt(2)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(InstructionSelectorTest, Word32EqualWithUnsignedExtendByte) {
|
||||
{
|
||||
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* r = m.Word32And(p1, m.Int32Constant(0xff));
|
||||
m.Return(m.Word32Equal(p0, r));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArm64Cmp32, s[0]->arch_opcode());
|
||||
EXPECT_EQ(kMode_Operand2_R_UXTB, s[0]->addressing_mode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
{
|
||||
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* r = m.Word32And(p1, m.Int32Constant(0xff));
|
||||
m.Return(m.Word32Equal(r, p0));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArm64Cmp32, s[0]->arch_opcode());
|
||||
EXPECT_EQ(kMode_Operand2_R_UXTB, s[0]->addressing_mode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(InstructionSelectorTest, Word32EqualWithUnsignedExtendHalfword) {
|
||||
{
|
||||
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* r = m.Word32And(p1, m.Int32Constant(0xffff));
|
||||
m.Return(m.Word32Equal(p0, r));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArm64Cmp32, s[0]->arch_opcode());
|
||||
EXPECT_EQ(kMode_Operand2_R_UXTH, s[0]->addressing_mode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
{
|
||||
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* r = m.Word32And(p1, m.Int32Constant(0xffff));
|
||||
m.Return(m.Word32Equal(r, p0));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArm64Cmp32, s[0]->arch_opcode());
|
||||
EXPECT_EQ(kMode_Operand2_R_UXTH, s[0]->addressing_mode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(InstructionSelectorTest, Word32EqualWithSignedExtendByte) {
|
||||
{
|
||||
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* r =
|
||||
m.Word32Sar(m.Word32Shl(p1, m.Int32Constant(24)), m.Int32Constant(24));
|
||||
m.Return(m.Word32Equal(p0, r));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArm64Cmp32, s[0]->arch_opcode());
|
||||
EXPECT_EQ(kMode_Operand2_R_SXTB, s[0]->addressing_mode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
{
|
||||
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* r =
|
||||
m.Word32Sar(m.Word32Shl(p1, m.Int32Constant(24)), m.Int32Constant(24));
|
||||
m.Return(m.Word32Equal(r, p0));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArm64Cmp32, s[0]->arch_opcode());
|
||||
EXPECT_EQ(kMode_Operand2_R_SXTB, s[0]->addressing_mode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_F(InstructionSelectorTest, Word32EqualWithSignedExtendHalfword) {
|
||||
{
|
||||
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* r =
|
||||
m.Word32Sar(m.Word32Shl(p1, m.Int32Constant(16)), m.Int32Constant(16));
|
||||
m.Return(m.Word32Equal(p0, r));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArm64Cmp32, s[0]->arch_opcode());
|
||||
EXPECT_EQ(kMode_Operand2_R_SXTH, s[0]->addressing_mode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
{
|
||||
StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32);
|
||||
Node* const p0 = m.Parameter(0);
|
||||
Node* const p1 = m.Parameter(1);
|
||||
Node* r =
|
||||
m.Word32Sar(m.Word32Shl(p1, m.Int32Constant(16)), m.Int32Constant(16));
|
||||
m.Return(m.Word32Equal(r, p0));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArm64Cmp32, s[0]->arch_opcode());
|
||||
EXPECT_EQ(kMode_Operand2_R_SXTH, s[0]->addressing_mode());
|
||||
ASSERT_EQ(2U, s[0]->InputCount());
|
||||
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
|
||||
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
|
||||
ASSERT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Miscellaneous
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user