Commit 78199ce5 authored by shiyu.zhang's avatar shiyu.zhang Committed by Commit bot

[turbofan] Enable complex memory operands for binops on ia32/x64

With this change, on ia32 and x64, a load from memory into a register can be replaced by a memory operand for integer binops if it makes sense.

BUG=

Review-Url: https://codereview.chromium.org/2728533003
Cr-Commit-Position: refs/heads/master@{#43739}
parent 4aeb2657
......@@ -760,6 +760,21 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
__ add(esp, Immediate(kDoubleSize)); \
} while (false)
#define ASSEMBLE_BINOP(asm_instr) \
do { \
if (AddressingModeField::decode(instr->opcode()) != kMode_None) { \
size_t index = 1; \
Operand right = i.MemoryOperand(&index); \
__ asm_instr(i.InputRegister(0), right); \
} else { \
if (HasImmediateInput(instr, 1)) { \
__ asm_instr(i.InputOperand(0), i.InputImmediate(1)); \
} else { \
__ asm_instr(i.InputRegister(0), i.InputOperand(1)); \
} \
} \
} while (0)
void CodeGenerator::AssembleDeconstructFrame() {
__ mov(esp, ebp);
__ pop(ebp);
......@@ -1130,18 +1145,10 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
ASSEMBLE_IEEE754_UNOP(tanh);
break;
case kIA32Add:
if (HasImmediateInput(instr, 1)) {
__ add(i.InputOperand(0), i.InputImmediate(1));
} else {
__ add(i.InputRegister(0), i.InputOperand(1));
}
ASSEMBLE_BINOP(add);
break;
case kIA32And:
if (HasImmediateInput(instr, 1)) {
__ and_(i.InputOperand(0), i.InputImmediate(1));
} else {
__ and_(i.InputRegister(0), i.InputOperand(1));
}
ASSEMBLE_BINOP(and_);
break;
case kIA32Cmp:
ASSEMBLE_COMPARE(cmp);
......@@ -1189,25 +1196,13 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ neg(i.OutputOperand());
break;
case kIA32Or:
if (HasImmediateInput(instr, 1)) {
__ or_(i.InputOperand(0), i.InputImmediate(1));
} else {
__ or_(i.InputRegister(0), i.InputOperand(1));
}
ASSEMBLE_BINOP(or_);
break;
case kIA32Xor:
if (HasImmediateInput(instr, 1)) {
__ xor_(i.InputOperand(0), i.InputImmediate(1));
} else {
__ xor_(i.InputRegister(0), i.InputOperand(1));
}
ASSEMBLE_BINOP(xor_);
break;
case kIA32Sub:
if (HasImmediateInput(instr, 1)) {
__ sub(i.InputOperand(0), i.InputImmediate(1));
} else {
__ sub(i.InputRegister(0), i.InputOperand(1));
}
ASSEMBLE_BINOP(sub);
break;
case kIA32Shl:
if (HasImmediateInput(instr, 1)) {
......
......@@ -39,6 +39,11 @@ class IA32OperandGenerator final : public OperandGenerator {
MachineRepresentation rep =
LoadRepresentationOf(input->op()).representation();
switch (opcode) {
case kIA32And:
case kIA32Or:
case kIA32Xor:
case kIA32Add:
case kIA32Sub:
case kIA32Cmp:
case kIA32Test:
return rep == MachineRepresentation::kWord32 ||
......@@ -533,7 +538,7 @@ void VisitBinop(InstructionSelector* selector, Node* node,
Int32BinopMatcher m(node);
Node* left = m.left().node();
Node* right = m.right().node();
InstructionOperand inputs[4];
InstructionOperand inputs[6];
size_t input_count = 0;
InstructionOperand outputs[2];
size_t output_count = 0;
......@@ -554,12 +559,26 @@ void VisitBinop(InstructionSelector* selector, Node* node,
inputs[input_count++] = g.UseRegister(left);
inputs[input_count++] = g.UseImmediate(right);
} else {
int effect_level = selector->GetEffectLevel(node);
if (cont->IsBranch()) {
effect_level = selector->GetEffectLevel(
cont->true_block()->PredecessorAt(0)->control_input());
}
if (node->op()->HasProperty(Operator::kCommutative) &&
g.CanBeBetterLeftOperand(right)) {
g.CanBeBetterLeftOperand(right) &&
(!g.CanBeBetterLeftOperand(left) ||
!g.CanBeMemoryOperand(opcode, node, right, effect_level))) {
std::swap(left, right);
}
inputs[input_count++] = g.UseRegister(left);
inputs[input_count++] = g.Use(right);
if (g.CanBeMemoryOperand(opcode, node, right, effect_level)) {
inputs[input_count++] = g.UseRegister(left);
AddressingMode addressing_mode =
g.GetEffectiveAddressMemoryOperand(right, inputs, &input_count);
opcode |= AddressingModeField::encode(addressing_mode);
} else {
inputs[input_count++] = g.UseRegister(left);
inputs[input_count++] = g.Use(right);
}
}
if (cont->IsBranch()) {
......
......@@ -331,22 +331,27 @@ void EmitOOLTrapIfNeeded(Zone* zone, CodeGenerator* codegen,
} \
} while (0)
#define ASSEMBLE_BINOP(asm_instr) \
do { \
if (HasImmediateInput(instr, 1)) { \
if (instr->InputAt(0)->IsRegister()) { \
__ asm_instr(i.InputRegister(0), i.InputImmediate(1)); \
} else { \
__ asm_instr(i.InputOperand(0), i.InputImmediate(1)); \
} \
} else { \
if (instr->InputAt(1)->IsRegister()) { \
__ asm_instr(i.InputRegister(0), i.InputRegister(1)); \
} else { \
__ asm_instr(i.InputRegister(0), i.InputOperand(1)); \
} \
} \
#define ASSEMBLE_BINOP(asm_instr) \
do { \
if (AddressingModeField::decode(instr->opcode()) != kMode_None) { \
size_t index = 1; \
Operand right = i.MemoryOperand(&index); \
__ asm_instr(i.InputRegister(0), right); \
} else { \
if (HasImmediateInput(instr, 1)) { \
if (instr->InputAt(0)->IsRegister()) { \
__ asm_instr(i.InputRegister(0), i.InputImmediate(1)); \
} else { \
__ asm_instr(i.InputOperand(0), i.InputImmediate(1)); \
} \
} else { \
if (instr->InputAt(1)->IsRegister()) { \
__ asm_instr(i.InputRegister(0), i.InputRegister(1)); \
} else { \
__ asm_instr(i.InputRegister(0), i.InputOperand(1)); \
} \
} \
} \
} while (0)
#define ASSEMBLE_COMPARE(asm_instr) \
......
......@@ -58,10 +58,20 @@ class X64OperandGenerator final : public OperandGenerator {
MachineRepresentation rep =
LoadRepresentationOf(input->op()).representation();
switch (opcode) {
case kX64And:
case kX64Or:
case kX64Xor:
case kX64Add:
case kX64Sub:
case kX64Push:
case kX64Cmp:
case kX64Test:
return rep == MachineRepresentation::kWord64 || IsAnyTagged(rep);
case kX64And32:
case kX64Or32:
case kX64Xor32:
case kX64Add32:
case kX64Sub32:
case kX64Cmp32:
case kX64Test32:
return rep == MachineRepresentation::kWord32;
......@@ -507,7 +517,7 @@ static void VisitBinop(InstructionSelector* selector, Node* node,
Int32BinopMatcher m(node);
Node* left = m.left().node();
Node* right = m.right().node();
InstructionOperand inputs[4];
InstructionOperand inputs[6];
size_t input_count = 0;
InstructionOperand outputs[2];
size_t output_count = 0;
......@@ -528,12 +538,26 @@ static void VisitBinop(InstructionSelector* selector, Node* node,
inputs[input_count++] = g.UseRegister(left);
inputs[input_count++] = g.UseImmediate(right);
} else {
int effect_level = selector->GetEffectLevel(node);
if (cont->IsBranch()) {
effect_level = selector->GetEffectLevel(
cont->true_block()->PredecessorAt(0)->control_input());
}
if (node->op()->HasProperty(Operator::kCommutative) &&
g.CanBeBetterLeftOperand(right)) {
g.CanBeBetterLeftOperand(right) &&
(!g.CanBeBetterLeftOperand(left) ||
!g.CanBeMemoryOperand(opcode, node, right, effect_level))) {
std::swap(left, right);
}
inputs[input_count++] = g.UseRegister(left);
inputs[input_count++] = g.Use(right);
if (g.CanBeMemoryOperand(opcode, node, right, effect_level)) {
inputs[input_count++] = g.UseRegister(left);
AddressingMode addressing_mode =
g.GetEffectiveAddressMemoryOperand(right, inputs, &input_count);
opcode |= AddressingModeField::encode(addressing_mode);
} else {
inputs[input_count++] = g.UseRegister(left);
inputs[input_count++] = g.Use(right);
}
}
if (cont->IsBranch()) {
......
......@@ -649,8 +649,84 @@ TEST_F(InstructionSelectorTest, Int32MulHigh) {
// -----------------------------------------------------------------------------
// Floating point operations.
// Binops with a memory operand.
TEST_F(InstructionSelectorTest, LoadAnd32) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Word32And(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127))));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kIA32And, s[0]->arch_opcode());
ASSERT_EQ(3U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, LoadOr32) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Word32Or(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127))));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kIA32Or, s[0]->arch_opcode());
ASSERT_EQ(3U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, LoadXor32) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Word32Xor(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127))));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kIA32Xor, s[0]->arch_opcode());
ASSERT_EQ(3U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, LoadAdd32) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Int32Add(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127))));
Stream s = m.Build();
// Use lea instead of add, so memory operand is invalid.
ASSERT_EQ(2U, s.size());
EXPECT_EQ(kIA32Movl, s[0]->arch_opcode());
EXPECT_EQ(kIA32Lea, s[1]->arch_opcode());
}
TEST_F(InstructionSelectorTest, LoadSub32) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Int32Sub(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127))));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kIA32Sub, s[0]->arch_opcode());
ASSERT_EQ(3U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
}
// -----------------------------------------------------------------------------
// Floating point operations.
TEST_F(InstructionSelectorTest, Float32Abs) {
{
......
......@@ -1207,8 +1207,158 @@ TEST_F(InstructionSelectorTest, Int32Shl4BecomesLea) {
// -----------------------------------------------------------------------------
// Floating point operations.
// Binops with a memory operand.
TEST_F(InstructionSelectorTest, LoadAnd32) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Word32And(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127))));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64And32, s[0]->arch_opcode());
ASSERT_EQ(3U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, LoadOr32) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Word32Or(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127))));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Or32, s[0]->arch_opcode());
ASSERT_EQ(3U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, LoadXor32) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Word32Xor(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127))));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Xor32, s[0]->arch_opcode());
ASSERT_EQ(3U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, LoadAdd32) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Int32Add(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127))));
Stream s = m.Build();
// Use lea instead of add, so memory operand is invalid.
ASSERT_EQ(2U, s.size());
EXPECT_EQ(kX64Movl, s[0]->arch_opcode());
EXPECT_EQ(kX64Lea32, s[1]->arch_opcode());
}
TEST_F(InstructionSelectorTest, LoadSub32) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Int32Sub(p0, m.Load(MachineType::Int32(), p1, m.Int32Constant(127))));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Sub32, s[0]->arch_opcode());
ASSERT_EQ(3U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, LoadAnd64) {
StreamBuilder m(this, MachineType::Int64(), MachineType::Int64(),
MachineType::Int64());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Word64And(p0, m.Load(MachineType::Int64(), p1, m.Int32Constant(127))));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64And, s[0]->arch_opcode());
ASSERT_EQ(3U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, LoadOr64) {
StreamBuilder m(this, MachineType::Int64(), MachineType::Int64(),
MachineType::Int64());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Word64Or(p0, m.Load(MachineType::Int64(), p1, m.Int32Constant(127))));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Or, s[0]->arch_opcode());
ASSERT_EQ(3U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, LoadXor64) {
StreamBuilder m(this, MachineType::Int64(), MachineType::Int64(),
MachineType::Int64());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Word64Xor(p0, m.Load(MachineType::Int64(), p1, m.Int32Constant(127))));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Xor, s[0]->arch_opcode());
ASSERT_EQ(3U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
}
TEST_F(InstructionSelectorTest, LoadAdd64) {
StreamBuilder m(this, MachineType::Int64(), MachineType::Int64(),
MachineType::Int64());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Int64Add(p0, m.Load(MachineType::Int64(), p1, m.Int32Constant(127))));
Stream s = m.Build();
// Use lea instead of add, so memory operand is invalid.
ASSERT_EQ(2U, s.size());
EXPECT_EQ(kX64Movq, s[0]->arch_opcode());
EXPECT_EQ(kX64Lea, s[1]->arch_opcode());
}
TEST_F(InstructionSelectorTest, LoadSub64) {
StreamBuilder m(this, MachineType::Int64(), MachineType::Int64(),
MachineType::Int64());
Node* const p0 = m.Parameter(0);
Node* const p1 = m.Parameter(1);
m.Return(
m.Int64Sub(p0, m.Load(MachineType::Int64(), p1, m.Int32Constant(127))));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64Sub, s[0]->arch_opcode());
ASSERT_EQ(3U, s[0]->InputCount());
EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0)));
EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1)));
}
// -----------------------------------------------------------------------------
// Floating point operations.
TEST_F(InstructionSelectorTest, Float32Abs) {
{
......
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