Commit 47880b83 authored by dcarney@chromium.org's avatar dcarney@chromium.org

[turbofan] add new ia32 addressing modes

R=bmeurer@chromium.org

BUG=

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24277 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 85f47bd8
......@@ -76,25 +76,83 @@ class IA32OperandConverter : public InstructionOperandConverter {
return Immediate(-1);
}
Operand MemoryOperand(int* first_input) {
const int offset = *first_input;
switch (AddressingModeField::decode(instr_->opcode())) {
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_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:
*first_input += 2;
return Operand(InputRegister(offset + 0), InputRegister(offset + 1),
times_1,
0); // TODO(dcarney): K != 0
case kMode_MRI:
*first_input += 2;
return Operand::ForRegisterPlusImmediate(InputRegister(offset + 0),
InputImmediate(offset + 1));
case kMode_MI:
*first_input += 1;
return Operand(InputImmediate(offset + 0));
default:
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_MI: {
int32_t disp = InputInt32(NextOffset(offset));
return Operand(Immediate(disp));
}
case kMode_None:
UNREACHABLE();
return Operand(no_reg);
return Operand(no_reg, 0);
}
UNREACHABLE();
return Operand(no_reg, 0);
}
Operand MemoryOperand() {
......
......@@ -61,23 +61,31 @@ namespace compiler {
//
// 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(MI) /* [K] */ \
V(MR) /* [%r0] */ \
V(MRI) /* [%r0 + K] */ \
V(MR1I) /* [%r0 + %r1 * 1 + K] */ \
V(MR2I) /* [%r0 + %r1 * 2 + K] */ \
V(MR4I) /* [%r0 + %r1 * 4 + K] */ \
V(MR8I) /* [%r0 + %r1 * 8 + 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*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] */ \
V(MI) /* [ K] */
} // namespace compiler
} // namespace internal
......
......@@ -189,9 +189,13 @@ TEST_P(InstructionSelectorMemoryAccessTest, LoadWithImmediateBase) {
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(memacc.load_opcode, s[0]->arch_opcode());
if (base == 0) {
ASSERT_EQ(1U, s[0]->InputCount());
} else {
ASSERT_EQ(2U, s[0]->InputCount());
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
EXPECT_EQ(base, s.ToInt32(s[0]->InputAt(1)));
}
EXPECT_EQ(1U, s[0]->OutputCount());
}
}
......@@ -205,9 +209,13 @@ TEST_P(InstructionSelectorMemoryAccessTest, LoadWithImmediateIndex) {
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(memacc.load_opcode, s[0]->arch_opcode());
if (index == 0) {
ASSERT_EQ(1U, s[0]->InputCount());
} else {
ASSERT_EQ(2U, s[0]->InputCount());
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1)));
}
EXPECT_EQ(1U, s[0]->OutputCount());
}
}
......@@ -235,9 +243,13 @@ TEST_P(InstructionSelectorMemoryAccessTest, StoreWithImmediateBase) {
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(memacc.store_opcode, s[0]->arch_opcode());
if (base == 0) {
ASSERT_EQ(2U, s[0]->InputCount());
} else {
ASSERT_EQ(3U, s[0]->InputCount());
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
EXPECT_EQ(base, s.ToInt32(s[0]->InputAt(1)));
}
EXPECT_EQ(0U, s[0]->OutputCount());
}
}
......@@ -253,9 +265,13 @@ TEST_P(InstructionSelectorMemoryAccessTest, StoreWithImmediateIndex) {
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(memacc.store_opcode, s[0]->arch_opcode());
ASSERT_EQ(3U, s[0]->InputCount());
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1)));
if (index == 0) {
ASSERT_EQ(2U, s[0]->InputCount());
} else {
ASSERT_EQ(3U, s[0]->InputCount());
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1)));
}
EXPECT_EQ(0U, s[0]->OutputCount());
}
}
......@@ -265,6 +281,149 @@ INSTANTIATE_TEST_CASE_P(InstructionSelectorTest,
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->Int32Constant(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_MR);
}
TEST_F(AddressingModeUnitTest, AddressingMode_MN) {
AddressingMode expected[] = {kMode_MR, 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_MRI);
}
TEST_F(AddressingModeUnitTest, AddressingMode_MNI) {
AddressingMode expected[] = {kMode_MRI, 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]);
}
}
TEST_F(AddressingModeUnitTest, AddressingMode_MI) {
Node* bases[] = {null_ptr, non_zero};
Node* indices[] = {zero, non_zero};
for (size_t i = 0; i < arraysize(bases); ++i) {
for (size_t j = 0; j < arraysize(indices); ++j) {
Reset();
Node* base = bases[i];
Node* index = indices[j];
Run(base, index, kMode_MI);
}
}
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -44,10 +44,108 @@ class IA32OperandGenerator FINAL : public OperandGenerator {
};
class AddressingModeMatcher {
public:
AddressingModeMatcher(IA32OperandGenerator* 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 displacement = index_imm.Value();
// Compute base operand and fold base immediate into displacement.
Int32Matcher base_imm(base);
if (!base_imm.HasValue()) {
base_operand_ = g->UseRegister(base);
} else {
displacement += base_imm.Value();
}
if (displacement != 0 || base_operand_ == NULL) {
displacement_operand_ = g->TempImmediate(displacement);
}
if (base_operand_ == NULL) {
mode_ = kMode_MI;
} else {
if (displacement == 0) {
mode_ = kMode_MR;
} else {
mode_ = kMode_MRI;
}
}
} else {
// Compute index and displacement.
IndexAndDisplacementMatcher matcher(index);
index_operand_ = g->UseRegister(matcher.index_node());
int32_t displacement = matcher.displacement();
// Compute base operand and fold base immediate into displacement.
Int32Matcher base_imm(base);
if (!base_imm.HasValue()) {
base_operand_ = g->UseRegister(base);
} else {
displacement += base_imm.Value();
}
// Compute displacement operand.
if (displacement != 0) {
displacement_operand_ = g->TempImmediate(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());
// Don't emit instructions with scale factor 1 if there's no base.
if (mode_ == kMode_M1) {
mode_ = kMode_MR;
} else if (mode_ == kMode_M1I) {
mode_ = kMode_MRI;
}
}
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) {
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));
IA32OperandGenerator g(this);
Node* base = node->InputAt(0);
Node* index = node->InputAt(1);
......@@ -75,23 +173,14 @@ void InstructionSelector::VisitLoad(Node* node) {
UNREACHABLE();
return;
}
if (g.CanBeImmediate(base)) {
if (Int32Matcher(index).Is(0)) { // load [#base + #0]
Emit(opcode | AddressingModeField::encode(kMode_MI),
g.DefineAsRegister(node), g.UseImmediate(base));
} else { // 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]
IA32OperandGenerator g(this);
AddressingModeMatcher matcher(&g, base, index);
InstructionCode code = opcode | AddressingModeField::encode(matcher.mode_);
InstructionOperand* outputs[] = {g.DefineAsRegister(node)};
InstructionOperand* inputs[AddressingModeMatcher::kMaxInputCount];
size_t input_count = matcher.SetInputs(inputs);
Emit(code, 1, outputs, input_count, inputs);
}
......@@ -115,14 +204,7 @@ void InstructionSelector::VisitStore(Node* node) {
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:
......@@ -146,22 +228,22 @@ void InstructionSelector::VisitStore(Node* node) {
UNREACHABLE();
return;
}
if (g.CanBeImmediate(base)) {
if (Int32Matcher(index).Is(0)) { // store [#base], %|#value
Emit(opcode | AddressingModeField::encode(kMode_MI), NULL,
g.UseImmediate(base), val);
} else { // 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 if (rep == kRepWord8 || rep == kRepBit) {
val = g.UseByteRegister(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];
size_t input_count = matcher.SetInputs(inputs);
inputs[input_count++] = val;
Emit(code, 0, static_cast<InstructionOperand**>(NULL), input_count, inputs);
}
......
......@@ -139,6 +139,96 @@ typedef BinopMatcher<Int64Matcher, Int64Matcher> Int64BinopMatcher;
typedef BinopMatcher<Uint64Matcher, Uint64Matcher> Uint64BinopMatcher;
typedef BinopMatcher<Float64Matcher, Float64Matcher> Float64BinopMatcher;
// Fairly intel-specify node matcher used for matching scale factors in
// addressing modes.
// 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_;
};
// Fairly intel-specify node matcher used for matching index and displacement
// operands in addressing modes.
// 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_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
......
......@@ -54,91 +54,6 @@ 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)
......
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