Commit 1297a51e authored by martyn.capewell's avatar martyn.capewell Committed by Commit bot

[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}
parent 70502d16
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment