Commit d3a021bb authored by dcarney@chromium.org's avatar dcarney@chromium.org

[turbofan] add new x64 addressing modes

R=bmeurer@chromium.org, titzer@chromium.org

BUG=

Review URL: https://codereview.chromium.org/605693002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24267 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent ad24cdae
...@@ -107,10 +107,10 @@ typedef int32_t InstructionCode; ...@@ -107,10 +107,10 @@ typedef int32_t InstructionCode;
// continuation into a single InstructionCode which is stored as part of // continuation into a single InstructionCode which is stored as part of
// the instruction. // the instruction.
typedef BitField<ArchOpcode, 0, 7> ArchOpcodeField; typedef BitField<ArchOpcode, 0, 7> ArchOpcodeField;
typedef BitField<AddressingMode, 7, 4> AddressingModeField; typedef BitField<AddressingMode, 7, 5> AddressingModeField;
typedef BitField<FlagsMode, 11, 2> FlagsModeField; typedef BitField<FlagsMode, 12, 2> FlagsModeField;
typedef BitField<FlagsCondition, 13, 5> FlagsConditionField; typedef BitField<FlagsCondition, 14, 5> FlagsConditionField;
typedef BitField<int, 13, 19> MiscField; typedef BitField<int, 14, 18> MiscField;
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
......
...@@ -145,22 +145,79 @@ class X64OperandConverter : public InstructionOperandConverter { ...@@ -145,22 +145,79 @@ class X64OperandConverter : public InstructionOperandConverter {
return result; return result;
} }
Operand MemoryOperand(int* first_input) { static int NextOffset(int* offset) {
const int offset = *first_input; int i = *offset;
switch (AddressingModeField::decode(instr_->opcode())) { (*offset)++;
case kMode_MR1I: { return i;
*first_input += 2; }
Register index = InputRegister(offset + 1);
return Operand(InputRegister(offset + 0), index, times_1, static ScaleFactor ScaleFor(AddressingMode one, AddressingMode mode) {
0); // TODO(dcarney): K != 0 STATIC_ASSERT(0 == static_cast<int>(times_1));
} STATIC_ASSERT(1 == static_cast<int>(times_2));
case kMode_MRI: STATIC_ASSERT(2 == static_cast<int>(times_4));
*first_input += 2; STATIC_ASSERT(3 == static_cast<int>(times_8));
return Operand(InputRegister(offset + 0), InputInt32(offset + 1)); int scale = static_cast<int>(mode - one);
default: DCHECK(scale >= 0 && scale < 4);
return static_cast<ScaleFactor>(scale);
}
Operand MemoryOperand(int* offset) {
AddressingMode mode = AddressingModeField::decode(instr_->opcode());
switch (mode) {
case kMode_MR: {
Register base = InputRegister(NextOffset(offset));
int32_t disp = 0;
return Operand(base, disp);
}
case kMode_MRI: {
Register base = InputRegister(NextOffset(offset));
int32_t disp = InputInt32(NextOffset(offset));
return Operand(base, disp);
}
case kMode_MR1:
case kMode_MR2:
case kMode_MR4:
case kMode_MR8: {
Register base = InputRegister(NextOffset(offset));
Register index = InputRegister(NextOffset(offset));
ScaleFactor scale = ScaleFor(kMode_MR1, mode);
int32_t disp = 0;
return Operand(base, index, scale, disp);
}
case kMode_MR1I:
case kMode_MR2I:
case kMode_MR4I:
case kMode_MR8I: {
Register base = InputRegister(NextOffset(offset));
Register index = InputRegister(NextOffset(offset));
ScaleFactor scale = ScaleFor(kMode_MR1I, mode);
int32_t disp = InputInt32(NextOffset(offset));
return Operand(base, index, scale, disp);
}
case kMode_M1:
case kMode_M2:
case kMode_M4:
case kMode_M8: {
Register index = InputRegister(NextOffset(offset));
ScaleFactor scale = ScaleFor(kMode_M1, mode);
int32_t disp = 0;
return Operand(index, scale, disp);
}
case kMode_M1I:
case kMode_M2I:
case kMode_M4I:
case kMode_M8I: {
Register index = InputRegister(NextOffset(offset));
ScaleFactor scale = ScaleFor(kMode_M1I, mode);
int32_t disp = InputInt32(NextOffset(offset));
return Operand(index, scale, disp);
}
case kMode_None:
UNREACHABLE(); UNREACHABLE();
return Operand(no_reg, 0); return Operand(no_reg, 0);
} }
UNREACHABLE();
return Operand(no_reg, 0);
} }
Operand MemoryOperand() { Operand MemoryOperand() {
......
...@@ -79,22 +79,30 @@ namespace compiler { ...@@ -79,22 +79,30 @@ namespace compiler {
// //
// We use the following local notation for addressing modes: // We use the following local notation for addressing modes:
// //
// R = register // M = memory operand
// O = register or stack slot // R = base register
// D = double register // N = index register * N for N in {1, 2, 4, 8}
// I = immediate (handle, external, int32) // I = immediate displacement (int32_t)
// MR = [register]
// MI = [immediate]
// MRN = [register + register * N in {1, 2, 4, 8}]
// MRI = [register + immediate]
// MRNI = [register + register * N in {1, 2, 4, 8} + immediate]
#define TARGET_ADDRESSING_MODE_LIST(V) \ #define TARGET_ADDRESSING_MODE_LIST(V) \
V(MR) /* [%r1] */ \ V(MR) /* [%r1 ] */ \
V(MRI) /* [%r1 + K] */ \ V(MRI) /* [%r1 + K] */ \
V(MR1I) /* [%r1 + %r2 + K] */ \ V(MR1) /* [%r1 + %r2*1 ] */ \
V(MR2) /* [%r1 + %r2*2 ] */ \
V(MR4) /* [%r1 + %r2*4 ] */ \
V(MR8) /* [%r1 + %r2*8 ] */ \
V(MR1I) /* [%r1 + %r2*1 + K] */ \
V(MR2I) /* [%r1 + %r2*2 + K] */ \ V(MR2I) /* [%r1 + %r2*2 + K] */ \
V(MR4I) /* [%r1 + %r2*4 + K] */ \ V(MR4I) /* [%r1 + %r2*3 + K] */ \
V(MR8I) /* [%r1 + %r2*8 + K] */ V(MR8I) /* [%r1 + %r2*4 + K] */ \
V(M1) /* [ %r2*1 ] */ \
V(M2) /* [ %r2*2 ] */ \
V(M4) /* [ %r2*4 ] */ \
V(M8) /* [ %r2*8 ] */ \
V(M1I) /* [ %r2*1 + K] */ \
V(M2I) /* [ %r2*2 + K] */ \
V(M4I) /* [ %r2*4 + K] */ \
V(M8I) /* [ %r2*8 + K] */
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
......
...@@ -161,6 +161,134 @@ INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, ...@@ -161,6 +161,134 @@ INSTANTIATE_TEST_CASE_P(InstructionSelectorTest,
InstructionSelectorMemoryAccessTest, InstructionSelectorMemoryAccessTest,
::testing::ValuesIn(kMemoryAccesses)); ::testing::ValuesIn(kMemoryAccesses));
// -----------------------------------------------------------------------------
// AddressingMode for loads and stores.
class AddressingModeUnitTest : public InstructionSelectorTest {
public:
AddressingModeUnitTest() : m(NULL) { Reset(); }
~AddressingModeUnitTest() { delete m; }
void Run(Node* base, Node* index, AddressingMode mode) {
Node* load = m->Load(kMachInt32, base, index);
m->Store(kMachInt32, base, index, load);
m->Return(m->Int32Constant(0));
Stream s = m->Build();
ASSERT_EQ(2U, s.size());
EXPECT_EQ(mode, s[0]->addressing_mode());
EXPECT_EQ(mode, s[1]->addressing_mode());
}
Node* zero;
Node* null_ptr;
Node* non_zero;
Node* base_reg; // opaque value to generate base as register
Node* index_reg; // opaque value to generate index as register
Node* scales[4];
StreamBuilder* m;
void Reset() {
delete m;
m = new StreamBuilder(this, kMachInt32, kMachInt32, kMachInt32);
zero = m->Int32Constant(0);
null_ptr = m->Int64Constant(0);
non_zero = m->Int32Constant(127);
base_reg = m->Parameter(0);
index_reg = m->Parameter(0);
scales[0] = m->Int32Constant(1);
scales[1] = m->Int32Constant(2);
scales[2] = m->Int32Constant(4);
scales[3] = m->Int32Constant(8);
}
};
TEST_F(AddressingModeUnitTest, AddressingMode_MR) {
Node* base = base_reg;
Node* index = zero;
Run(base, index, kMode_MR);
}
TEST_F(AddressingModeUnitTest, AddressingMode_MRI) {
Node* base = base_reg;
Node* index = non_zero;
Run(base, index, kMode_MRI);
}
TEST_F(AddressingModeUnitTest, AddressingMode_MR1) {
Node* base = base_reg;
Node* index = index_reg;
Run(base, index, kMode_MR1);
}
TEST_F(AddressingModeUnitTest, AddressingMode_MRN) {
AddressingMode expected[] = {kMode_MR1, kMode_MR2, kMode_MR4, kMode_MR8};
for (size_t i = 0; i < arraysize(scales); ++i) {
Reset();
Node* base = base_reg;
Node* index = m->Int32Mul(index_reg, scales[i]);
Run(base, index, expected[i]);
}
}
TEST_F(AddressingModeUnitTest, AddressingMode_MR1I) {
Node* base = base_reg;
Node* index = m->Int32Add(index_reg, non_zero);
Run(base, index, kMode_MR1I);
}
TEST_F(AddressingModeUnitTest, AddressingMode_MRNI) {
AddressingMode expected[] = {kMode_MR1I, kMode_MR2I, kMode_MR4I, kMode_MR8I};
for (size_t i = 0; i < arraysize(scales); ++i) {
Reset();
Node* base = base_reg;
Node* index = m->Int32Add(m->Int32Mul(index_reg, scales[i]), non_zero);
Run(base, index, expected[i]);
}
}
TEST_F(AddressingModeUnitTest, AddressingMode_M1) {
Node* base = null_ptr;
Node* index = index_reg;
Run(base, index, kMode_M1);
}
TEST_F(AddressingModeUnitTest, AddressingMode_MN) {
AddressingMode expected[] = {kMode_M1, kMode_M2, kMode_M4, kMode_M8};
for (size_t i = 0; i < arraysize(scales); ++i) {
Reset();
Node* base = null_ptr;
Node* index = m->Int32Mul(index_reg, scales[i]);
Run(base, index, expected[i]);
}
}
TEST_F(AddressingModeUnitTest, AddressingMode_M1I) {
Node* base = null_ptr;
Node* index = m->Int32Add(index_reg, non_zero);
Run(base, index, kMode_M1I);
}
TEST_F(AddressingModeUnitTest, AddressingMode_MNI) {
AddressingMode expected[] = {kMode_M1I, kMode_M2I, kMode_M4I, kMode_M8I};
for (size_t i = 0; i < arraysize(scales); ++i) {
Reset();
Node* base = null_ptr;
Node* index = m->Int32Add(m->Int32Mul(index_reg, scales[i]), non_zero);
Run(base, index, expected[i]);
}
}
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -20,11 +20,6 @@ class X64OperandGenerator FINAL : public OperandGenerator { ...@@ -20,11 +20,6 @@ class X64OperandGenerator FINAL : public OperandGenerator {
Register::ToAllocationIndex(reg)); Register::ToAllocationIndex(reg));
} }
InstructionOperand* UseByteRegister(Node* node) {
// TODO(dcarney): relax constraint.
return UseFixed(node, rdx);
}
InstructionOperand* UseImmediate64(Node* node) { return UseImmediate(node); } InstructionOperand* UseImmediate64(Node* node) { return UseImmediate(node); }
bool CanBeImmediate(Node* node) { bool CanBeImmediate(Node* node) {
...@@ -59,10 +54,173 @@ class X64OperandGenerator FINAL : public OperandGenerator { ...@@ -59,10 +54,173 @@ class X64OperandGenerator FINAL : public OperandGenerator {
}; };
// Matches nodes of form [x * N] for N in {1,2,4,8}
class ScaleFactorMatcher : public NodeMatcher {
public:
explicit ScaleFactorMatcher(Node* node)
: NodeMatcher(node), left_(NULL), power_(0) {
Match();
}
bool Matches() { return left_ != NULL; }
int Power() {
DCHECK(Matches());
return power_;
}
Node* Left() {
DCHECK(Matches());
return left_;
}
private:
void Match() {
if (opcode() != IrOpcode::kInt32Mul) return;
Int32BinopMatcher m(node());
if (!m.right().HasValue()) return;
int32_t value = m.right().Value();
switch (value) {
case 8:
power_++; // Fall through.
case 4:
power_++; // Fall through.
case 2:
power_++; // Fall through.
case 1:
break;
default:
return;
}
left_ = m.left().node();
}
Node* left_;
int power_;
};
// Matches nodes of form:
// [x * N]
// [x * N + K]
// [x + K]
// [x] -- fallback case
// for N in {1,2,4,8} and K int32_t
class IndexAndDisplacementMatcher : public NodeMatcher {
public:
explicit IndexAndDisplacementMatcher(Node* node)
: NodeMatcher(node), index_node_(node), displacement_(0), power_(0) {
Match();
}
Node* index_node() { return index_node_; }
int displacement() { return displacement_; }
int power() { return power_; }
private:
void Match() {
if (opcode() == IrOpcode::kInt32Add) {
// Assume reduction has put constant on the right.
Int32BinopMatcher m(node());
if (m.right().HasValue()) {
displacement_ = m.right().Value();
index_node_ = m.left().node();
}
}
// Test scale factor.
ScaleFactorMatcher scale_matcher(index_node_);
if (scale_matcher.Matches()) {
index_node_ = scale_matcher.Left();
power_ = scale_matcher.Power();
}
}
Node* index_node_;
int displacement_;
int power_;
};
class AddressingModeMatcher {
public:
AddressingModeMatcher(X64OperandGenerator* g, Node* base, Node* index)
: base_operand_(NULL),
index_operand_(NULL),
displacement_operand_(NULL),
mode_(kMode_None) {
Int32Matcher index_imm(index);
if (index_imm.HasValue()) {
int32_t value = index_imm.Value();
if (value == 0) {
mode_ = kMode_MR;
} else {
mode_ = kMode_MRI;
index_operand_ = g->UseImmediate(index);
}
base_operand_ = g->UseRegister(base);
} else {
// Compute base operand.
Int64Matcher base_imm(base);
if (!base_imm.HasValue() || base_imm.Value() != 0) {
base_operand_ = g->UseRegister(base);
}
// Compute index and displacement.
IndexAndDisplacementMatcher matcher(index);
index_operand_ = g->UseRegister(matcher.index_node());
if (matcher.displacement() != 0) {
displacement_operand_ = g->TempImmediate(matcher.displacement());
}
// Compute mode with scale factor one.
if (base_operand_ == NULL) {
if (displacement_operand_ == NULL) {
mode_ = kMode_M1;
} else {
mode_ = kMode_M1I;
}
} else {
if (displacement_operand_ == NULL) {
mode_ = kMode_MR1;
} else {
mode_ = kMode_MR1I;
}
}
// Adjust mode to actual scale factor.
mode_ = GetMode(mode_, matcher.power());
}
DCHECK_NE(kMode_None, mode_);
}
AddressingMode GetMode(AddressingMode one, int power) {
return static_cast<AddressingMode>(static_cast<int>(one) + power);
}
size_t SetInputs(InstructionOperand** inputs) {
size_t input_count = 0;
// Compute inputs_ and input_count.
if (base_operand_ != NULL) {
inputs[input_count++] = base_operand_;
}
if (index_operand_ != NULL) {
inputs[input_count++] = index_operand_;
}
if (displacement_operand_ != NULL) {
// Pure displacement mode not supported by x64.
DCHECK_NE(input_count, 0);
inputs[input_count++] = displacement_operand_;
}
DCHECK_NE(input_count, 0);
return input_count;
}
static const int kMaxInputCount = 3;
InstructionOperand* base_operand_;
InstructionOperand* index_operand_;
InstructionOperand* displacement_operand_;
AddressingMode mode_;
};
void InstructionSelector::VisitLoad(Node* node) { void InstructionSelector::VisitLoad(Node* node) {
MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node)); MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node));
MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node)); MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node));
X64OperandGenerator g(this);
Node* base = node->InputAt(0); Node* base = node->InputAt(0);
Node* index = node->InputAt(1); Node* index = node->InputAt(1);
...@@ -93,18 +251,14 @@ void InstructionSelector::VisitLoad(Node* node) { ...@@ -93,18 +251,14 @@ void InstructionSelector::VisitLoad(Node* node) {
UNREACHABLE(); UNREACHABLE();
return; return;
} }
if (g.CanBeImmediate(base)) {
// load [#base + %index] X64OperandGenerator g(this);
Emit(opcode | AddressingModeField::encode(kMode_MRI), AddressingModeMatcher matcher(&g, base, index);
g.DefineAsRegister(node), g.UseRegister(index), g.UseImmediate(base)); InstructionCode code = opcode | AddressingModeField::encode(matcher.mode_);
} else if (g.CanBeImmediate(index)) { // load [%base + #index] InstructionOperand* outputs[] = {g.DefineAsRegister(node)};
Emit(opcode | AddressingModeField::encode(kMode_MRI), InstructionOperand* inputs[AddressingModeMatcher::kMaxInputCount];
g.DefineAsRegister(node), g.UseRegister(base), g.UseImmediate(index)); int input_count = matcher.SetInputs(inputs);
} else { // load [%base + %index + K] Emit(code, 1, outputs, input_count, inputs);
Emit(opcode | AddressingModeField::encode(kMode_MR1I),
g.DefineAsRegister(node), g.UseRegister(base), g.UseRegister(index));
}
// TODO(turbofan): addressing modes [r+r*{2,4,8}+K]
} }
...@@ -128,14 +282,6 @@ void InstructionSelector::VisitStore(Node* node) { ...@@ -128,14 +282,6 @@ void InstructionSelector::VisitStore(Node* node) {
return; return;
} }
DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind()); DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
InstructionOperand* val;
if (g.CanBeImmediate(value)) {
val = g.UseImmediate(value);
} else if (rep == kRepWord8 || rep == kRepBit) {
val = g.UseByteRegister(value);
} else {
val = g.UseRegister(value);
}
ArchOpcode opcode; ArchOpcode opcode;
switch (rep) { switch (rep) {
case kRepFloat32: case kRepFloat32:
...@@ -162,18 +308,20 @@ void InstructionSelector::VisitStore(Node* node) { ...@@ -162,18 +308,20 @@ void InstructionSelector::VisitStore(Node* node) {
UNREACHABLE(); UNREACHABLE();
return; return;
} }
if (g.CanBeImmediate(base)) {
// store [#base + %index], %|#value InstructionOperand* val;
Emit(opcode | AddressingModeField::encode(kMode_MRI), NULL, if (g.CanBeImmediate(value)) {
g.UseRegister(index), g.UseImmediate(base), val); val = g.UseImmediate(value);
} else if (g.CanBeImmediate(index)) { // store [%base + #index], %|#value } else {
Emit(opcode | AddressingModeField::encode(kMode_MRI), NULL, val = g.UseRegister(value);
g.UseRegister(base), g.UseImmediate(index), val);
} else { // store [%base + %index], %|#value
Emit(opcode | AddressingModeField::encode(kMode_MR1I), NULL,
g.UseRegister(base), g.UseRegister(index), val);
} }
// TODO(turbofan): addressing modes [r+r*{2,4,8}+K]
AddressingModeMatcher matcher(&g, base, index);
InstructionCode code = opcode | AddressingModeField::encode(matcher.mode_);
InstructionOperand* inputs[AddressingModeMatcher::kMaxInputCount + 1];
int input_count = matcher.SetInputs(inputs);
inputs[input_count++] = val;
Emit(code, 0, static_cast<InstructionOperand**>(NULL), input_count, inputs);
} }
...@@ -702,8 +850,6 @@ void InstructionSelector::VisitCall(Node* call, BasicBlock* continuation, ...@@ -702,8 +850,6 @@ void InstructionSelector::VisitCall(Node* call, BasicBlock* continuation,
// Compute InstructionOperands for inputs and outputs. // Compute InstructionOperands for inputs and outputs.
InitializeCallBuffer(call, &buffer, true, true); InitializeCallBuffer(call, &buffer, true, true);
// TODO(dcarney): stack alignment for c calls.
// TODO(dcarney): shadow space on window for c calls.
// Push any stack arguments. // Push any stack arguments.
for (NodeVectorRIter input = buffer.pushed_nodes.rbegin(); for (NodeVectorRIter input = buffer.pushed_nodes.rbegin();
input != buffer.pushed_nodes.rend(); input++) { input != buffer.pushed_nodes.rend(); input++) {
......
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