[turbofan] ARM: Support shifted indexes in loads and stores
This patch is a follow up to https://codereview.chromium.org/1972103002/ adding support for the `Operand_R_LSL_I` addressing mode to loads and stores for ARM. Just as the ARM64 implementation, the shift + load/store pattern is only really relevant to the interpreter. For this reason, this patch does not add support for the other addressing modes (`R_LSR_I`, `R_ASR_I` and `R_ROR_I`) as I haven't seen those pattern being generated. Additionally, the optimization is restricted 32 bit loads and stores. kind = BYTECODE_HANDLER name = Star compiler = turbofan Instructions (size = 40) 0x22a5f860 0 e2851001 add r1, r5, #1 0x22a5f864 4 e19610d1 ldrsb r1, [r6, +r1] 0x22a5f868 8 e1a0200b mov r2, fp 0x22a5f86c 12 e7820101 str r0, [r2, +r1, lsl #2] ^^^^^^^^^^^^^^^^^^^^^^^^^ 0x22a5f870 16 e2855002 add r5, r5, #2 0x22a5f874 20 e7d61005 ldrb r1, [r6, +r5] 0x22a5f878 24 e7981101 ldr r1, [r8, +r1, lsl #2] ^^^^^^^^^^^^^^^^^^^^^^^^^ 0x22a5f87c 28 e12fff11 bx r1 BUG= Review-Url: https://codereview.chromium.org/1974263002 Cr-Commit-Position: refs/heads/master@{#36381}
This commit is contained in:
parent
4d0ea36c0d
commit
11b661f414
@ -125,13 +125,16 @@ class ArmOperandConverter final : public InstructionOperandConverter {
|
||||
case kMode_Operand2_R:
|
||||
case kMode_Operand2_R_ASR_I:
|
||||
case kMode_Operand2_R_ASR_R:
|
||||
case kMode_Operand2_R_LSL_I:
|
||||
case kMode_Operand2_R_LSL_R:
|
||||
case kMode_Operand2_R_LSR_I:
|
||||
case kMode_Operand2_R_LSR_R:
|
||||
case kMode_Operand2_R_ROR_I:
|
||||
case kMode_Operand2_R_ROR_R:
|
||||
break;
|
||||
case kMode_Operand2_R_LSL_I:
|
||||
*first_index += 3;
|
||||
return MemOperand(InputRegister(index + 0), InputRegister(index + 1),
|
||||
LSL, InputInt32(index + 2));
|
||||
case kMode_Offset_RI:
|
||||
*first_index += 2;
|
||||
return MemOperand(InputRegister(index + 0), InputInt32(index + 1));
|
||||
@ -1121,59 +1124,44 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
|
||||
__ ldrsb(i.OutputRegister(), i.InputOffset());
|
||||
DCHECK_EQ(LeaveCC, i.OutputSBit());
|
||||
break;
|
||||
case kArmStrb: {
|
||||
size_t index = 0;
|
||||
MemOperand operand = i.InputOffset(&index);
|
||||
__ strb(i.InputRegister(index), operand);
|
||||
case kArmStrb:
|
||||
__ strb(i.InputRegister(0), i.InputOffset(1));
|
||||
DCHECK_EQ(LeaveCC, i.OutputSBit());
|
||||
break;
|
||||
}
|
||||
case kArmLdrh:
|
||||
__ ldrh(i.OutputRegister(), i.InputOffset());
|
||||
break;
|
||||
case kArmLdrsh:
|
||||
__ ldrsh(i.OutputRegister(), i.InputOffset());
|
||||
break;
|
||||
case kArmStrh: {
|
||||
size_t index = 0;
|
||||
MemOperand operand = i.InputOffset(&index);
|
||||
__ strh(i.InputRegister(index), operand);
|
||||
case kArmStrh:
|
||||
__ strh(i.InputRegister(0), i.InputOffset(1));
|
||||
DCHECK_EQ(LeaveCC, i.OutputSBit());
|
||||
break;
|
||||
}
|
||||
case kArmLdr:
|
||||
__ ldr(i.OutputRegister(), i.InputOffset());
|
||||
break;
|
||||
case kArmStr: {
|
||||
size_t index = 0;
|
||||
MemOperand operand = i.InputOffset(&index);
|
||||
__ str(i.InputRegister(index), operand);
|
||||
case kArmStr:
|
||||
__ str(i.InputRegister(0), i.InputOffset(1));
|
||||
DCHECK_EQ(LeaveCC, i.OutputSBit());
|
||||
break;
|
||||
}
|
||||
case kArmVldrF32: {
|
||||
__ vldr(i.OutputFloat32Register(), i.InputOffset());
|
||||
DCHECK_EQ(LeaveCC, i.OutputSBit());
|
||||
break;
|
||||
}
|
||||
case kArmVstrF32: {
|
||||
size_t index = 0;
|
||||
MemOperand operand = i.InputOffset(&index);
|
||||
__ vstr(i.InputFloat32Register(index), operand);
|
||||
case kArmVstrF32:
|
||||
__ vstr(i.InputFloat32Register(0), i.InputOffset(1));
|
||||
DCHECK_EQ(LeaveCC, i.OutputSBit());
|
||||
break;
|
||||
}
|
||||
case kArmVldrF64:
|
||||
__ vldr(i.OutputFloat64Register(), i.InputOffset());
|
||||
DCHECK_EQ(LeaveCC, i.OutputSBit());
|
||||
break;
|
||||
case kArmVstrF64: {
|
||||
size_t index = 0;
|
||||
MemOperand operand = i.InputOffset(&index);
|
||||
__ vstr(i.InputFloat64Register(index), operand);
|
||||
case kArmVstrF64:
|
||||
__ vstr(i.InputFloat64Register(0), i.InputOffset(1));
|
||||
DCHECK_EQ(LeaveCC, i.OutputSBit());
|
||||
break;
|
||||
}
|
||||
case kArmFloat32Max: {
|
||||
CpuFeatureScope scope(masm(), ARMv8);
|
||||
// (b < a) ? a : b
|
||||
|
@ -115,6 +115,24 @@ bool TryMatchShift(InstructionSelector* selector,
|
||||
return false;
|
||||
}
|
||||
|
||||
template <IrOpcode::Value kOpcode, int kImmMin, int kImmMax,
|
||||
AddressingMode kImmMode>
|
||||
bool TryMatchShiftImmediate(InstructionSelector* selector,
|
||||
InstructionCode* opcode_return, Node* node,
|
||||
InstructionOperand* value_return,
|
||||
InstructionOperand* shift_return) {
|
||||
ArmOperandGenerator g(selector);
|
||||
if (node->opcode() == kOpcode) {
|
||||
Int32BinopMatcher m(node);
|
||||
if (m.right().IsInRange(kImmMin, kImmMax)) {
|
||||
*opcode_return |= AddressingModeField::encode(kImmMode);
|
||||
*value_return = g.UseRegister(m.left().node());
|
||||
*shift_return = g.UseImmediate(m.right().node());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryMatchROR(InstructionSelector* selector, InstructionCode* opcode_return,
|
||||
Node* node, InstructionOperand* value_return,
|
||||
@ -142,6 +160,14 @@ bool TryMatchLSL(InstructionSelector* selector, InstructionCode* opcode_return,
|
||||
value_return, shift_return);
|
||||
}
|
||||
|
||||
bool TryMatchLSLImmediate(InstructionSelector* selector,
|
||||
InstructionCode* opcode_return, Node* node,
|
||||
InstructionOperand* value_return,
|
||||
InstructionOperand* shift_return) {
|
||||
return TryMatchShiftImmediate<IrOpcode::kWord32Shl, 0, 31,
|
||||
kMode_Operand2_R_LSL_I>(
|
||||
selector, opcode_return, node, value_return, shift_return);
|
||||
}
|
||||
|
||||
bool TryMatchLSR(InstructionSelector* selector, InstructionCode* opcode_return,
|
||||
Node* node, InstructionOperand* value_return,
|
||||
@ -312,8 +338,11 @@ void InstructionSelector::VisitLoad(Node* node) {
|
||||
ArmOperandGenerator g(this);
|
||||
Node* base = node->InputAt(0);
|
||||
Node* index = node->InputAt(1);
|
||||
InstructionOperand inputs[3];
|
||||
size_t input_count = 0;
|
||||
InstructionOperand outputs[1];
|
||||
|
||||
ArchOpcode opcode = kArchNop;
|
||||
InstructionCode opcode = kArchNop;
|
||||
switch (load_rep.representation()) {
|
||||
case MachineRepresentation::kFloat32:
|
||||
opcode = kArmVldrF32;
|
||||
@ -339,13 +368,24 @@ void InstructionSelector::VisitLoad(Node* node) {
|
||||
return;
|
||||
}
|
||||
|
||||
outputs[0] = g.DefineAsRegister(node);
|
||||
inputs[0] = g.UseRegister(base);
|
||||
|
||||
if (g.CanBeImmediate(index, opcode)) {
|
||||
Emit(opcode | AddressingModeField::encode(kMode_Offset_RI),
|
||||
g.DefineAsRegister(node), g.UseRegister(base), g.UseImmediate(index));
|
||||
input_count = 2;
|
||||
inputs[1] = g.UseImmediate(index);
|
||||
opcode |= AddressingModeField::encode(kMode_Offset_RI);
|
||||
} else if ((opcode == kArmLdr) &&
|
||||
TryMatchLSLImmediate(this, &opcode, index, &inputs[1],
|
||||
&inputs[2])) {
|
||||
input_count = 3;
|
||||
} else {
|
||||
Emit(opcode | AddressingModeField::encode(kMode_Offset_RR),
|
||||
g.DefineAsRegister(node), g.UseRegister(base), g.UseRegister(index));
|
||||
input_count = 2;
|
||||
inputs[1] = g.UseRegister(index);
|
||||
opcode |= AddressingModeField::encode(kMode_Offset_RR);
|
||||
}
|
||||
|
||||
Emit(opcode, arraysize(outputs), outputs, input_count, inputs);
|
||||
}
|
||||
|
||||
|
||||
@ -397,7 +437,10 @@ void InstructionSelector::VisitStore(Node* node) {
|
||||
code |= MiscField::encode(static_cast<int>(record_write_mode));
|
||||
Emit(code, 0, nullptr, input_count, inputs, temp_count, temps);
|
||||
} else {
|
||||
ArchOpcode opcode = kArchNop;
|
||||
InstructionOperand inputs[4];
|
||||
size_t input_count = 0;
|
||||
|
||||
InstructionCode opcode = kArchNop;
|
||||
switch (rep) {
|
||||
case MachineRepresentation::kFloat32:
|
||||
opcode = kArmVstrF32;
|
||||
@ -423,13 +466,23 @@ void InstructionSelector::VisitStore(Node* node) {
|
||||
return;
|
||||
}
|
||||
|
||||
inputs[0] = g.UseRegister(value);
|
||||
inputs[1] = g.UseRegister(base);
|
||||
|
||||
if (g.CanBeImmediate(index, opcode)) {
|
||||
Emit(opcode | AddressingModeField::encode(kMode_Offset_RI), g.NoOutput(),
|
||||
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value));
|
||||
input_count = 3;
|
||||
inputs[2] = g.UseImmediate(index);
|
||||
opcode |= AddressingModeField::encode(kMode_Offset_RI);
|
||||
} else if ((opcode == kArmStr) &&
|
||||
TryMatchLSLImmediate(this, &opcode, index, &inputs[2],
|
||||
&inputs[3])) {
|
||||
input_count = 4;
|
||||
} else {
|
||||
Emit(opcode | AddressingModeField::encode(kMode_Offset_RR), g.NoOutput(),
|
||||
g.UseRegister(base), g.UseRegister(index), g.UseRegister(value));
|
||||
input_count = 3;
|
||||
inputs[2] = g.UseRegister(index);
|
||||
opcode |= AddressingModeField::encode(kMode_Offset_RR);
|
||||
}
|
||||
Emit(opcode, 0, nullptr, input_count, inputs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1392,8 +1392,8 @@ TEST_P(InstructionSelectorMemoryAccessTest, StoreWithImmediateIndex) {
|
||||
EXPECT_EQ(memacc.str_opcode, s[0]->arch_opcode());
|
||||
EXPECT_EQ(kMode_Offset_RI, s[0]->addressing_mode());
|
||||
ASSERT_EQ(3U, s[0]->InputCount());
|
||||
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
|
||||
EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1)));
|
||||
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(2)->kind());
|
||||
EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(2)));
|
||||
EXPECT_EQ(0U, s[0]->OutputCount());
|
||||
}
|
||||
}
|
||||
@ -1403,6 +1403,39 @@ INSTANTIATE_TEST_CASE_P(InstructionSelectorTest,
|
||||
InstructionSelectorMemoryAccessTest,
|
||||
::testing::ValuesIn(kMemoryAccesses));
|
||||
|
||||
TEST_F(InstructionSelectorMemoryAccessTest, LoadWithShiftedIndex) {
|
||||
TRACED_FORRANGE(int, immediate_shift, 1, 31) {
|
||||
StreamBuilder m(this, MachineType::Int32(), MachineType::Pointer(),
|
||||
MachineType::Int32());
|
||||
Node* const index =
|
||||
m.Word32Shl(m.Parameter(1), m.Int32Constant(immediate_shift));
|
||||
m.Return(m.Load(MachineType::Int32(), m.Parameter(0), index));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArmLdr, s[0]->arch_opcode());
|
||||
EXPECT_EQ(kMode_Operand2_R_LSL_I, s[0]->addressing_mode());
|
||||
EXPECT_EQ(3U, s[0]->InputCount());
|
||||
EXPECT_EQ(1U, s[0]->OutputCount());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(InstructionSelectorMemoryAccessTest, StoreWithShiftedIndex) {
|
||||
TRACED_FORRANGE(int, immediate_shift, 1, 31) {
|
||||
StreamBuilder m(this, MachineType::Int32(), MachineType::Pointer(),
|
||||
MachineType::Int32(), MachineType::Int32());
|
||||
Node* const index =
|
||||
m.Word32Shl(m.Parameter(1), m.Int32Constant(immediate_shift));
|
||||
m.Store(MachineRepresentation::kWord32, m.Parameter(0), index,
|
||||
m.Parameter(2), kNoWriteBarrier);
|
||||
m.Return(m.Int32Constant(0));
|
||||
Stream s = m.Build();
|
||||
ASSERT_EQ(1U, s.size());
|
||||
EXPECT_EQ(kArmStr, s[0]->arch_opcode());
|
||||
EXPECT_EQ(kMode_Operand2_R_LSL_I, s[0]->addressing_mode());
|
||||
EXPECT_EQ(4U, s[0]->InputCount());
|
||||
EXPECT_EQ(0U, s[0]->OutputCount());
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Conversions.
|
||||
|
Loading…
Reference in New Issue
Block a user