// continuation into a single InstructionCode which is stored as part of
// the instruction.
typedef BitField<ArchOpcode, 0, 7> ArchOpcodeField;
-typedef BitField<AddressingMode, 7, 4> AddressingModeField;
-typedef BitField<FlagsMode, 11, 2> FlagsModeField;
-typedef BitField<FlagsCondition, 13, 5> FlagsConditionField;
-typedef BitField<int, 13, 19> MiscField;
+typedef BitField<AddressingMode, 7, 5> AddressingModeField;
+typedef BitField<FlagsMode, 12, 2> FlagsModeField;
+typedef BitField<FlagsCondition, 14, 5> FlagsConditionField;
+typedef BitField<int, 14, 18> MiscField;
} // namespace compiler
} // namespace internal
return result;
}
- Operand MemoryOperand(int* first_input) {
- const int offset = *first_input;
- switch (AddressingModeField::decode(instr_->opcode())) {
- case kMode_MR1I: {
- *first_input += 2;
- Register index = InputRegister(offset + 1);
- return Operand(InputRegister(offset + 0), index, times_1,
- 0); // TODO(dcarney): K != 0
+ static int NextOffset(int* offset) {
+ int i = *offset;
+ (*offset)++;
+ return i;
+ }
+
+ static ScaleFactor ScaleFor(AddressingMode one, AddressingMode mode) {
+ STATIC_ASSERT(0 == static_cast<int>(times_1));
+ STATIC_ASSERT(1 == static_cast<int>(times_2));
+ STATIC_ASSERT(2 == static_cast<int>(times_4));
+ STATIC_ASSERT(3 == static_cast<int>(times_8));
+ int scale = static_cast<int>(mode - one);
+ 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_MRI:
- *first_input += 2;
- return Operand(InputRegister(offset + 0), InputInt32(offset + 1));
- default:
+ 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();
return Operand(no_reg, 0);
}
+ UNREACHABLE();
+ return Operand(no_reg, 0);
}
Operand MemoryOperand() {
//
// We use the following local notation for addressing modes:
//
-// R = register
-// O = register or stack slot
-// D = double register
-// I = immediate (handle, external, int32)
-// 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]
+// M = memory operand
+// R = base register
+// N = index register * N for N in {1, 2, 4, 8}
+// I = immediate displacement (int32_t)
+
#define TARGET_ADDRESSING_MODE_LIST(V) \
- V(MR) /* [%r1] */ \
- V(MRI) /* [%r1 + K] */ \
- V(MR1I) /* [%r1 + %r2 + K] */ \
+ V(MR) /* [%r1 ] */ \
+ V(MRI) /* [%r1 + 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(MR4I) /* [%r1 + %r2*4 + K] */ \
- V(MR8I) /* [%r1 + %r2*8 + K] */
+ V(MR4I) /* [%r1 + %r2*3 + 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 internal
InstructionSelectorMemoryAccessTest,
::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 internal
} // namespace v8
Register::ToAllocationIndex(reg));
}
- InstructionOperand* UseByteRegister(Node* node) {
- // TODO(dcarney): relax constraint.
- return UseFixed(node, rdx);
- }
-
InstructionOperand* UseImmediate64(Node* node) { return UseImmediate(node); }
bool CanBeImmediate(Node* node) {
};
+// 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) {
MachineType rep = RepresentationOf(OpParameter<LoadRepresentation>(node));
MachineType typ = TypeOf(OpParameter<LoadRepresentation>(node));
- X64OperandGenerator g(this);
Node* base = node->InputAt(0);
Node* index = node->InputAt(1);
UNREACHABLE();
return;
}
- if (g.CanBeImmediate(base)) {
- // load [#base + %index]
- Emit(opcode | AddressingModeField::encode(kMode_MRI),
- g.DefineAsRegister(node), g.UseRegister(index), g.UseImmediate(base));
- } else if (g.CanBeImmediate(index)) { // load [%base + #index]
- Emit(opcode | AddressingModeField::encode(kMode_MRI),
- g.DefineAsRegister(node), g.UseRegister(base), g.UseImmediate(index));
- } else { // load [%base + %index + K]
- 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]
+
+ X64OperandGenerator g(this);
+ AddressingModeMatcher matcher(&g, base, index);
+ InstructionCode code = opcode | AddressingModeField::encode(matcher.mode_);
+ InstructionOperand* outputs[] = {g.DefineAsRegister(node)};
+ InstructionOperand* inputs[AddressingModeMatcher::kMaxInputCount];
+ int input_count = matcher.SetInputs(inputs);
+ Emit(code, 1, outputs, input_count, inputs);
}
return;
}
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;
switch (rep) {
case kRepFloat32:
UNREACHABLE();
return;
}
- if (g.CanBeImmediate(base)) {
- // store [#base + %index], %|#value
- Emit(opcode | AddressingModeField::encode(kMode_MRI), NULL,
- g.UseRegister(index), g.UseImmediate(base), val);
- } else if (g.CanBeImmediate(index)) { // store [%base + #index], %|#value
- Emit(opcode | AddressingModeField::encode(kMode_MRI), NULL,
- 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);
+
+ InstructionOperand* val;
+ if (g.CanBeImmediate(value)) {
+ val = g.UseImmediate(value);
+ } else {
+ val = g.UseRegister(value);
}
- // 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);
}
// Compute InstructionOperands for inputs and outputs.
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.
for (NodeVectorRIter input = buffer.pushed_nodes.rbegin();
input != buffer.pushed_nodes.rend(); input++) {