diff --git a/src/compiler/arm64/code-generator-arm64.cc b/src/compiler/arm64/code-generator-arm64.cc index fa821f58ca..b494090c47 100644 --- a/src/compiler/arm64/code-generator-arm64.cc +++ b/src/compiler/arm64/code-generator-arm64.cc @@ -71,6 +71,10 @@ class Arm64OperandConverter FINAL : public InstructionOperandConverter { return Operand(InputRegister32(index), ASR, InputInt5(index + 1)); case kMode_Operand2_R_ROR_I: return Operand(InputRegister32(index), ROR, InputInt5(index + 1)); + case kMode_Operand2_R_UXTB: + return Operand(InputRegister32(index), UXTB); + case kMode_Operand2_R_UXTH: + return Operand(InputRegister32(index), UXTH); case kMode_MRI: case kMode_MRR: break; @@ -91,6 +95,10 @@ class Arm64OperandConverter FINAL : public InstructionOperandConverter { return Operand(InputRegister64(index), ASR, InputInt6(index + 1)); case kMode_Operand2_R_ROR_I: return Operand(InputRegister64(index), ROR, InputInt6(index + 1)); + case kMode_Operand2_R_UXTB: + return Operand(InputRegister64(index), UXTB); + case kMode_Operand2_R_UXTH: + return Operand(InputRegister64(index), UXTH); case kMode_MRI: case kMode_MRR: break; @@ -107,6 +115,8 @@ class Arm64OperandConverter FINAL : public InstructionOperandConverter { case kMode_Operand2_R_LSR_I: case kMode_Operand2_R_ASR_I: case kMode_Operand2_R_ROR_I: + case kMode_Operand2_R_UXTB: + case kMode_Operand2_R_UXTH: break; case kMode_MRI: *first_index += 2; diff --git a/src/compiler/arm64/instruction-codes-arm64.h b/src/compiler/arm64/instruction-codes-arm64.h index 242d25d009..dfd90da44a 100644 --- a/src/compiler/arm64/instruction-codes-arm64.h +++ b/src/compiler/arm64/instruction-codes-arm64.h @@ -130,13 +130,15 @@ namespace compiler { // I = immediate (handle, external, int32) // MRI = [register + immediate] // MRR = [register + register] -#define TARGET_ADDRESSING_MODE_LIST(V) \ - V(MRI) /* [%r0 + K] */ \ - V(MRR) /* [%r0 + %r1] */ \ - V(Operand2_R_LSL_I) /* %r0 LSL K */ \ - V(Operand2_R_LSR_I) /* %r0 LSR K */ \ - V(Operand2_R_ASR_I) /* %r0 ASR K */ \ - V(Operand2_R_ROR_I) /* %r0 ROR K */ +#define TARGET_ADDRESSING_MODE_LIST(V) \ + V(MRI) /* [%r0 + K] */ \ + V(MRR) /* [%r0 + %r1] */ \ + V(Operand2_R_LSL_I) /* %r0 LSL K */ \ + V(Operand2_R_LSR_I) /* %r0 LSR K */ \ + V(Operand2_R_ASR_I) /* %r0 ASR K */ \ + V(Operand2_R_ROR_I) /* %r0 ROR K */ \ + V(Operand2_R_UXTB) /* %r0 UXTB (unsigned extend byte) */ \ + V(Operand2_R_UXTH) /* %r0 UXTH (unsigned extend halfword) */ } // namespace internal } // namespace compiler diff --git a/src/compiler/arm64/instruction-selector-arm64.cc b/src/compiler/arm64/instruction-selector-arm64.cc index fedda248d8..edef43837b 100644 --- a/src/compiler/arm64/instruction-selector-arm64.cc +++ b/src/compiler/arm64/instruction-selector-arm64.cc @@ -167,6 +167,25 @@ static bool TryMatchAnyShift(InstructionSelector* selector, Node* node, } +static bool TryMatchAnyExtend(InstructionSelector* selector, Node* node, + InstructionCode* opcode) { + NodeMatcher nm(node); + if (nm.IsWord32And()) { + Int32BinopMatcher m(node); + if (m.right().HasValue()) { + if (m.right().Value() == 0xff) { + *opcode |= AddressingModeField::encode(kMode_Operand2_R_UXTB); + return true; + } else if (m.right().Value() == 0xffff) { + *opcode |= AddressingModeField::encode(kMode_Operand2_R_UXTH); + return true; + } + } + } + return false; +} + + // Shared routine for multiple binary operations. template static void VisitBinop(InstructionSelector* selector, Node* node, @@ -178,28 +197,38 @@ static void VisitBinop(InstructionSelector* selector, Node* node, size_t input_count = 0; InstructionOperand outputs[2]; size_t output_count = 0; - bool try_ror_operand = true; + bool is_add_sub = false; if (m.IsInt32Add() || m.IsInt64Add() || m.IsInt32Sub() || m.IsInt64Sub()) { - try_ror_operand = false; + is_add_sub = true; } if (g.CanBeImmediate(m.right().node(), operand_mode)) { inputs[input_count++] = g.UseRegister(m.left().node()); inputs[input_count++] = g.UseImmediate(m.right().node()); } else if (TryMatchAnyShift(selector, m.right().node(), &opcode, - try_ror_operand)) { + !is_add_sub)) { Matcher m_shift(m.right().node()); inputs[input_count++] = g.UseRegister(m.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, m.left().node(), &opcode, - try_ror_operand)) { + !is_add_sub)) { Matcher m_shift(m.left().node()); inputs[input_count++] = g.UseRegister(m.right().node()); inputs[input_count++] = g.UseRegister(m_shift.left().node()); inputs[input_count++] = g.UseImmediate(m_shift.right().node()); + } else if (is_add_sub && + TryMatchAnyExtend(selector, m.right().node(), &opcode)) { + Matcher mright(m.right().node()); + inputs[input_count++] = g.UseRegister(m.left().node()); + inputs[input_count++] = g.UseRegister(mright.left().node()); + } else if (is_add_sub && m.HasProperty(Operator::kCommutative) && + TryMatchAnyExtend(selector, m.left().node(), &opcode)) { + Matcher mleft(m.left().node()); + inputs[input_count++] = g.UseRegister(m.right().node()); + inputs[input_count++] = g.UseRegister(mleft.left().node()); } else { inputs[input_count++] = g.UseRegister(m.left().node()); inputs[input_count++] = g.UseRegister(m.right().node()); diff --git a/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc b/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc index aea626201c..163175bcb8 100644 --- a/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc +++ b/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc @@ -472,6 +472,36 @@ TEST_P(InstructionSelectorAddSubTest, ShiftByImmediateOnRight) { } +TEST_P(InstructionSelectorAddSubTest, ExtendByte) { + const AddSub dpi = GetParam(); + const MachineType type = dpi.mi.machine_type; + StreamBuilder m(this, type, type, type); + m.Return((m.*dpi.mi.constructor)( + m.Parameter(0), m.Word32And(m.Parameter(1), m.Int32Constant(0xff)))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(dpi.mi.arch_opcode, s[0]->arch_opcode()); + EXPECT_EQ(kMode_Operand2_R_UXTB, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + ASSERT_EQ(1U, s[0]->OutputCount()); +} + + +TEST_P(InstructionSelectorAddSubTest, ExtendHalfword) { + const AddSub dpi = GetParam(); + const MachineType type = dpi.mi.machine_type; + StreamBuilder m(this, type, type, type); + m.Return((m.*dpi.mi.constructor)( + m.Parameter(0), m.Word32And(m.Parameter(1), m.Int32Constant(0xffff)))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(dpi.mi.arch_opcode, s[0]->arch_opcode()); + EXPECT_EQ(kMode_Operand2_R_UXTH, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + ASSERT_EQ(1U, s[0]->OutputCount()); +} + + INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, InstructionSelectorAddSubTest, ::testing::ValuesIn(kAddSubInstructions)); @@ -616,6 +646,58 @@ TEST_F(InstructionSelectorTest, AddShiftByImmediateOnLeft) { } +TEST_F(InstructionSelectorTest, AddExtendByteOnLeft) { + { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + m.Return(m.Int32Add(m.Word32And(m.Parameter(0), m.Int32Constant(0xff)), + m.Parameter(1))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Add32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_Operand2_R_UXTB, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + ASSERT_EQ(1U, s[0]->OutputCount()); + } + { + StreamBuilder m(this, kMachInt64, kMachInt32, kMachInt64); + m.Return(m.Int64Add(m.Word32And(m.Parameter(0), m.Int32Constant(0xff)), + m.Parameter(1))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Add, s[0]->arch_opcode()); + EXPECT_EQ(kMode_Operand2_R_UXTB, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + ASSERT_EQ(1U, s[0]->OutputCount()); + } +} + + +TEST_F(InstructionSelectorTest, AddExtendHalfwordOnLeft) { + { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + m.Return(m.Int32Add(m.Word32And(m.Parameter(0), m.Int32Constant(0xffff)), + m.Parameter(1))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Add32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_Operand2_R_UXTH, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + ASSERT_EQ(1U, s[0]->OutputCount()); + } + { + StreamBuilder m(this, kMachInt64, kMachInt32, kMachInt64); + m.Return(m.Int64Add(m.Word32And(m.Parameter(0), m.Int32Constant(0xffff)), + m.Parameter(1))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Add, s[0]->arch_opcode()); + EXPECT_EQ(kMode_Operand2_R_UXTH, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + ASSERT_EQ(1U, s[0]->OutputCount()); + } +} + + // ----------------------------------------------------------------------------- // Data processing controlled branches.