Commit 0e43ff56 authored by epertoso's avatar epertoso Committed by Commit bot

Emit memory operands for cmp and test on ia32 and x64 when it makes sense.

The InstructionSelector now associates an effect level to every node in a block.

The effect level of a node is the number of non-eliminatable nodes encountered from the beginning of the block to the node itself.

With this change, on ia32 and x64, a load from memory into a register can be replaced by a memory operand if all of the following conditions hold:

1. The only use of the load is in a 32 or 64 bit word comparison.
2. The user node and the load node belong to the same block.
3. The values of the operands have the same size (i.e., no need to zero-extend or sign-extend the result of the load).

BUG=

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

Cr-Commit-Position: refs/heads/master@{#34187}
parent 344d99c4
...@@ -527,17 +527,37 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -527,17 +527,37 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
} }
break; break;
case kIA32Cmp: case kIA32Cmp:
if (HasImmediateInput(instr, 1)) { if (AddressingModeField::decode(instr->opcode()) != kMode_None) {
__ cmp(i.InputOperand(0), i.InputImmediate(1)); size_t index = 0;
Operand operand = i.MemoryOperand(&index);
if (HasImmediateInput(instr, index)) {
__ cmp(operand, i.InputImmediate(index));
} else {
__ cmp(operand, i.InputRegister(index));
}
} else { } else {
__ cmp(i.InputRegister(0), i.InputOperand(1)); if (HasImmediateInput(instr, 1)) {
__ cmp(i.InputOperand(0), i.InputImmediate(1));
} else {
__ cmp(i.InputRegister(0), i.InputOperand(1));
}
} }
break; break;
case kIA32Test: case kIA32Test:
if (HasImmediateInput(instr, 1)) { if (AddressingModeField::decode(instr->opcode()) != kMode_None) {
__ test(i.InputOperand(0), i.InputImmediate(1)); size_t index = 0;
Operand operand = i.MemoryOperand(&index);
if (HasImmediateInput(instr, index)) {
__ test(operand, i.InputImmediate(index));
} else {
__ test(i.InputRegister(index), operand);
}
} else { } else {
__ test(i.InputRegister(0), i.InputOperand(1)); if (HasImmediateInput(instr, 1)) {
__ test(i.InputOperand(0), i.InputImmediate(1));
} else {
__ test(i.InputRegister(0), i.InputOperand(1));
}
} }
break; break;
case kIA32Imul: case kIA32Imul:
......
...@@ -989,6 +989,46 @@ bool InstructionSelector::IsTailCallAddressImmediate() { return true; } ...@@ -989,6 +989,46 @@ bool InstructionSelector::IsTailCallAddressImmediate() { return true; }
namespace { namespace {
void VisitCompareWithMemoryOperand(InstructionSelector* selector,
InstructionCode opcode, Node* left,
InstructionOperand right,
FlagsContinuation* cont) {
DCHECK(left->opcode() == IrOpcode::kLoad);
IA32OperandGenerator g(selector);
size_t input_count = 0;
InstructionOperand inputs[6];
AddressingMode addressing_mode =
g.GetEffectiveAddressMemoryOperand(left, inputs, &input_count);
opcode |= AddressingModeField::encode(addressing_mode);
opcode = cont->Encode(opcode);
inputs[input_count++] = right;
if (cont->IsBranch()) {
inputs[input_count++] = g.Label(cont->true_block());
inputs[input_count++] = g.Label(cont->false_block());
selector->Emit(opcode, 0, nullptr, input_count, inputs);
} else {
DCHECK(cont->IsSet());
InstructionOperand output = g.DefineAsRegister(cont->result());
selector->Emit(opcode, 1, &output, input_count, inputs);
}
}
// Determines if {input} of {node} can be replaced by a memory operand.
bool CanUseMemoryOperand(InstructionSelector* selector, InstructionCode opcode,
Node* node, Node* input) {
if (input->opcode() != IrOpcode::kLoad || !selector->CanCover(node, input)) {
return false;
}
MachineRepresentation load_representation =
LoadRepresentationOf(input->op()).representation();
if (load_representation == MachineRepresentation::kWord32 ||
load_representation == MachineRepresentation::kTagged) {
return opcode == kIA32Cmp || opcode == kIA32Test;
}
return false;
}
// Shared routine for multiple compare operations. // Shared routine for multiple compare operations.
void VisitCompare(InstructionSelector* selector, InstructionCode opcode, void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
InstructionOperand left, InstructionOperand right, InstructionOperand left, InstructionOperand right,
...@@ -1034,26 +1074,41 @@ void VisitFloat64Compare(InstructionSelector* selector, Node* node, ...@@ -1034,26 +1074,41 @@ void VisitFloat64Compare(InstructionSelector* selector, Node* node,
VisitCompare(selector, kSSEFloat64Cmp, right, left, cont, false); VisitCompare(selector, kSSEFloat64Cmp, right, left, cont, false);
} }
// Shared routine for multiple word compare operations. // Shared routine for multiple word compare operations.
void VisitWordCompare(InstructionSelector* selector, Node* node, void VisitWordCompare(InstructionSelector* selector, Node* node,
InstructionCode opcode, FlagsContinuation* cont) { InstructionCode opcode, FlagsContinuation* cont) {
IA32OperandGenerator g(selector); IA32OperandGenerator g(selector);
Node* const left = node->InputAt(0); Node* left = node->InputAt(0);
Node* const right = node->InputAt(1); Node* right = node->InputAt(1);
// If one of the two inputs is an immediate, make sure it's on the right.
if (!g.CanBeImmediate(right) && g.CanBeImmediate(left)) {
if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute();
std::swap(left, right);
}
// Match immediates on left or right side of comparison. // Match immediates on right side of comparison.
if (g.CanBeImmediate(right)) { if (g.CanBeImmediate(right)) {
VisitCompare(selector, opcode, g.Use(left), g.UseImmediate(right), cont); if (CanUseMemoryOperand(selector, opcode, node, left)) {
} else if (g.CanBeImmediate(left)) { return VisitCompareWithMemoryOperand(selector, opcode, left,
g.UseImmediate(right), cont);
}
return VisitCompare(selector, opcode, g.Use(left), g.UseImmediate(right),
cont);
}
if (g.CanBeBetterLeftOperand(right)) {
if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute(); if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute();
VisitCompare(selector, opcode, g.Use(right), g.UseImmediate(left), cont); std::swap(left, right);
} else {
VisitCompare(selector, opcode, left, right, cont,
node->op()->HasProperty(Operator::kCommutative));
} }
}
if (CanUseMemoryOperand(selector, opcode, node, left)) {
return VisitCompareWithMemoryOperand(selector, opcode, left,
g.UseRegister(right), cont);
}
return VisitCompare(selector, opcode, left, right, cont,
node->op()->HasProperty(Operator::kCommutative));
}
void VisitWordCompare(InstructionSelector* selector, Node* node, void VisitWordCompare(InstructionSelector* selector, Node* node,
FlagsContinuation* cont) { FlagsContinuation* cont) {
......
...@@ -34,6 +34,7 @@ InstructionSelector::InstructionSelector( ...@@ -34,6 +34,7 @@ InstructionSelector::InstructionSelector(
instructions_(zone), instructions_(zone),
defined_(node_count, false, zone), defined_(node_count, false, zone),
used_(node_count, false, zone), used_(node_count, false, zone),
effect_level_(node_count, 0, zone),
virtual_registers_(node_count, virtual_registers_(node_count,
InstructionOperand::kInvalidVirtualRegister, zone), InstructionOperand::kInvalidVirtualRegister, zone),
scheduler_(nullptr), scheduler_(nullptr),
...@@ -218,10 +219,11 @@ Instruction* InstructionSelector::Emit(Instruction* instr) { ...@@ -218,10 +219,11 @@ Instruction* InstructionSelector::Emit(Instruction* instr) {
bool InstructionSelector::CanCover(Node* user, Node* node) const { bool InstructionSelector::CanCover(Node* user, Node* node) const {
return node->OwnedBy(user) && return node->OwnedBy(user) &&
schedule()->block(node) == schedule()->block(user); schedule()->block(node) == schedule()->block(user) &&
(node->op()->HasProperty(Operator::kPure) ||
GetEffectLevel(node) == GetEffectLevel(user));
} }
int InstructionSelector::GetVirtualRegister(const Node* node) { int InstructionSelector::GetVirtualRegister(const Node* node) {
DCHECK_NOT_NULL(node); DCHECK_NOT_NULL(node);
size_t const id = node->id(); size_t const id = node->id();
...@@ -280,6 +282,19 @@ void InstructionSelector::MarkAsUsed(Node* node) { ...@@ -280,6 +282,19 @@ void InstructionSelector::MarkAsUsed(Node* node) {
used_[id] = true; used_[id] = true;
} }
int InstructionSelector::GetEffectLevel(Node* node) const {
DCHECK_NOT_NULL(node);
size_t const id = node->id();
DCHECK_LT(id, effect_level_.size());
return effect_level_[id];
}
void InstructionSelector::SetEffectLevel(Node* node, int effect_level) {
DCHECK_NOT_NULL(node);
size_t const id = node->id();
DCHECK_LT(id, effect_level_.size());
effect_level_[id] = effect_level;
}
void InstructionSelector::MarkAsRepresentation(MachineRepresentation rep, void InstructionSelector::MarkAsRepresentation(MachineRepresentation rep,
const InstructionOperand& op) { const InstructionOperand& op) {
...@@ -669,6 +684,16 @@ void InstructionSelector::VisitBlock(BasicBlock* block) { ...@@ -669,6 +684,16 @@ void InstructionSelector::VisitBlock(BasicBlock* block) {
current_block_ = block; current_block_ = block;
int current_block_end = static_cast<int>(instructions_.size()); int current_block_end = static_cast<int>(instructions_.size());
int effect_level = 0;
for (Node* const node : *block) {
if (node->opcode() == IrOpcode::kStore ||
node->opcode() == IrOpcode::kCheckedStore ||
node->opcode() == IrOpcode::kCall) {
++effect_level;
}
SetEffectLevel(node, effect_level);
}
// Generate code for the block control "top down", but schedule the code // Generate code for the block control "top down", but schedule the code
// "bottom up". // "bottom up".
VisitControl(block); VisitControl(block);
......
...@@ -149,6 +149,9 @@ class InstructionSelector final { ...@@ -149,6 +149,9 @@ class InstructionSelector final {
// Checks if {node} is currently live. // Checks if {node} is currently live.
bool IsLive(Node* node) const { return !IsDefined(node) && IsUsed(node); } bool IsLive(Node* node) const { return !IsDefined(node) && IsUsed(node); }
// Gets the effect level of {node}.
int GetEffectLevel(Node* node) const;
int GetVirtualRegister(const Node* node); int GetVirtualRegister(const Node* node);
const std::map<NodeId, int> GetVirtualRegistersForTesting() const; const std::map<NodeId, int> GetVirtualRegistersForTesting() const;
...@@ -168,6 +171,9 @@ class InstructionSelector final { ...@@ -168,6 +171,9 @@ class InstructionSelector final {
// will need to generate code for it. // will need to generate code for it.
void MarkAsUsed(Node* node); void MarkAsUsed(Node* node);
// Sets the effect level of {node}.
void SetEffectLevel(Node* node, int effect_level);
// Inform the register allocation of the representation of the value produced // Inform the register allocation of the representation of the value produced
// by {node}. // by {node}.
void MarkAsRepresentation(MachineRepresentation rep, Node* node); void MarkAsRepresentation(MachineRepresentation rep, Node* node);
...@@ -269,6 +275,7 @@ class InstructionSelector final { ...@@ -269,6 +275,7 @@ class InstructionSelector final {
ZoneVector<Instruction*> instructions_; ZoneVector<Instruction*> instructions_;
BoolVector defined_; BoolVector defined_;
BoolVector used_; BoolVector used_;
IntVector effect_level_;
IntVector virtual_registers_; IntVector virtual_registers_;
InstructionScheduler* scheduler_; InstructionScheduler* scheduler_;
Frame* frame_; Frame* frame_;
......
...@@ -259,6 +259,32 @@ class OutOfLineRecordWrite final : public OutOfLineCode { ...@@ -259,6 +259,32 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
} \ } \
} while (0) } while (0)
#define ASSEMBLE_COMPARE(asm_instr) \
do { \
if (AddressingModeField::decode(instr->opcode()) != kMode_None) { \
size_t index = 0; \
Operand left = i.MemoryOperand(&index); \
if (HasImmediateInput(instr, index)) { \
__ asm_instr(left, i.InputImmediate(index)); \
} else { \
__ asm_instr(left, i.InputRegister(index)); \
} \
} 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_MULT(asm_instr) \ #define ASSEMBLE_MULT(asm_instr) \
do { \ do { \
...@@ -771,16 +797,16 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) { ...@@ -771,16 +797,16 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
ASSEMBLE_BINOP(andq); ASSEMBLE_BINOP(andq);
break; break;
case kX64Cmp32: case kX64Cmp32:
ASSEMBLE_BINOP(cmpl); ASSEMBLE_COMPARE(cmpl);
break; break;
case kX64Cmp: case kX64Cmp:
ASSEMBLE_BINOP(cmpq); ASSEMBLE_COMPARE(cmpq);
break; break;
case kX64Test32: case kX64Test32:
ASSEMBLE_BINOP(testl); ASSEMBLE_COMPARE(testl);
break; break;
case kX64Test: case kX64Test:
ASSEMBLE_BINOP(testq); ASSEMBLE_COMPARE(testq);
break; break;
case kX64Imul32: case kX64Imul32:
ASSEMBLE_MULT(imull); ASSEMBLE_MULT(imull);
......
...@@ -1338,6 +1338,48 @@ bool InstructionSelector::IsTailCallAddressImmediate() { return true; } ...@@ -1338,6 +1338,48 @@ bool InstructionSelector::IsTailCallAddressImmediate() { return true; }
namespace { namespace {
void VisitCompareWithMemoryOperand(InstructionSelector* selector,
InstructionCode opcode, Node* left,
InstructionOperand right,
FlagsContinuation* cont) {
DCHECK(left->opcode() == IrOpcode::kLoad);
X64OperandGenerator g(selector);
size_t input_count = 0;
InstructionOperand inputs[6];
AddressingMode addressing_mode =
g.GetEffectiveAddressMemoryOperand(left, inputs, &input_count);
opcode |= AddressingModeField::encode(addressing_mode);
opcode = cont->Encode(opcode);
inputs[input_count++] = right;
if (cont->IsBranch()) {
inputs[input_count++] = g.Label(cont->true_block());
inputs[input_count++] = g.Label(cont->false_block());
selector->Emit(opcode, 0, nullptr, input_count, inputs);
} else {
DCHECK(cont->IsSet());
InstructionOperand output = g.DefineAsRegister(cont->result());
selector->Emit(opcode, 1, &output, input_count, inputs);
}
}
// Determines if {input} of {node} can be replaced by a memory operand.
bool CanUseMemoryOperand(InstructionSelector* selector, InstructionCode opcode,
Node* node, Node* input) {
if (input->opcode() != IrOpcode::kLoad || !selector->CanCover(node, input)) {
return false;
}
MachineRepresentation rep =
LoadRepresentationOf(input->op()).representation();
if (rep == MachineRepresentation::kWord64 ||
rep == MachineRepresentation::kTagged) {
return opcode == kX64Cmp || opcode == kX64Test;
} else if (rep == MachineRepresentation::kWord32) {
return opcode == kX64Cmp32 || opcode == kX64Test32;
}
return false;
}
// Shared routine for multiple compare operations. // Shared routine for multiple compare operations.
void VisitCompare(InstructionSelector* selector, InstructionCode opcode, void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
InstructionOperand left, InstructionOperand right, InstructionOperand left, InstructionOperand right,
...@@ -1365,26 +1407,41 @@ void VisitCompare(InstructionSelector* selector, InstructionCode opcode, ...@@ -1365,26 +1407,41 @@ void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
VisitCompare(selector, opcode, g.UseRegister(left), g.Use(right), cont); VisitCompare(selector, opcode, g.UseRegister(left), g.Use(right), cont);
} }
// Shared routine for multiple word compare operations. // Shared routine for multiple word compare operations.
void VisitWordCompare(InstructionSelector* selector, Node* node, void VisitWordCompare(InstructionSelector* selector, Node* node,
InstructionCode opcode, FlagsContinuation* cont) { InstructionCode opcode, FlagsContinuation* cont) {
X64OperandGenerator g(selector); X64OperandGenerator g(selector);
Node* const left = node->InputAt(0); Node* left = node->InputAt(0);
Node* const right = node->InputAt(1); Node* right = node->InputAt(1);
// If one of the two inputs is an immediate, make sure it's on the right.
if (!g.CanBeImmediate(right) && g.CanBeImmediate(left)) {
if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute();
std::swap(left, right);
}
// Match immediates on left or right side of comparison. // Match immediates on right side of comparison.
if (g.CanBeImmediate(right)) { if (g.CanBeImmediate(right)) {
VisitCompare(selector, opcode, g.Use(left), g.UseImmediate(right), cont); if (CanUseMemoryOperand(selector, opcode, node, left)) {
} else if (g.CanBeImmediate(left)) { return VisitCompareWithMemoryOperand(selector, opcode, left,
g.UseImmediate(right), cont);
}
return VisitCompare(selector, opcode, g.Use(left), g.UseImmediate(right),
cont);
}
if (g.CanBeBetterLeftOperand(right)) {
if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute(); if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute();
VisitCompare(selector, opcode, g.Use(right), g.UseImmediate(left), cont); std::swap(left, right);
} else {
VisitCompare(selector, opcode, left, right, cont,
node->op()->HasProperty(Operator::kCommutative));
} }
}
if (CanUseMemoryOperand(selector, opcode, node, left)) {
return VisitCompareWithMemoryOperand(selector, opcode, left,
g.UseRegister(right), cont);
}
return VisitCompare(selector, opcode, left, right, cont,
node->op()->HasProperty(Operator::kCommutative));
}
// Shared routine for 64-bit word comparison operations. // Shared routine for 64-bit word comparison operations.
void VisitWord64Compare(InstructionSelector* selector, Node* node, void VisitWord64Compare(InstructionSelector* selector, Node* node,
......
...@@ -803,6 +803,11 @@ void Assembler::cmp(Register reg, const Operand& op) { ...@@ -803,6 +803,11 @@ void Assembler::cmp(Register reg, const Operand& op) {
emit_operand(reg, op); emit_operand(reg, op);
} }
void Assembler::cmp(const Operand& op, Register reg) {
EnsureSpace ensure_space(this);
EMIT(0x39);
emit_operand(reg, op);
}
void Assembler::cmp(const Operand& op, const Immediate& imm) { void Assembler::cmp(const Operand& op, const Immediate& imm) {
EnsureSpace ensure_space(this); EnsureSpace ensure_space(this);
......
...@@ -678,6 +678,7 @@ class Assembler : public AssemblerBase { ...@@ -678,6 +678,7 @@ class Assembler : public AssemblerBase {
void cmp(Register reg0, Register reg1) { cmp(reg0, Operand(reg1)); } void cmp(Register reg0, Register reg1) { cmp(reg0, Operand(reg1)); }
void cmp(Register reg, const Operand& op); void cmp(Register reg, const Operand& op);
void cmp(Register reg, const Immediate& imm) { cmp(Operand(reg), imm); } void cmp(Register reg, const Immediate& imm) { cmp(Operand(reg), imm); }
void cmp(const Operand& op, Register reg);
void cmp(const Operand& op, const Immediate& imm); void cmp(const Operand& op, const Immediate& imm);
void cmp(const Operand& op, Handle<Object> handle); void cmp(const Operand& op, Handle<Object> handle);
......
...@@ -28,32 +28,30 @@ struct ByteMnemonic { ...@@ -28,32 +28,30 @@ struct ByteMnemonic {
OperandOrder op_order_; OperandOrder op_order_;
}; };
static const ByteMnemonic two_operands_instr[] = { static const ByteMnemonic two_operands_instr[] = {
{0x01, "add", OPER_REG_OP_ORDER}, {0x01, "add", OPER_REG_OP_ORDER},
{0x03, "add", REG_OPER_OP_ORDER}, {0x03, "add", REG_OPER_OP_ORDER},
{0x09, "or", OPER_REG_OP_ORDER}, {0x09, "or", OPER_REG_OP_ORDER},
{0x0B, "or", REG_OPER_OP_ORDER}, {0x0B, "or", REG_OPER_OP_ORDER},
{0x1B, "sbb", REG_OPER_OP_ORDER}, {0x1B, "sbb", REG_OPER_OP_ORDER},
{0x21, "and", OPER_REG_OP_ORDER}, {0x21, "and", OPER_REG_OP_ORDER},
{0x23, "and", REG_OPER_OP_ORDER}, {0x23, "and", REG_OPER_OP_ORDER},
{0x29, "sub", OPER_REG_OP_ORDER}, {0x29, "sub", OPER_REG_OP_ORDER},
{0x2A, "subb", REG_OPER_OP_ORDER}, {0x2A, "subb", REG_OPER_OP_ORDER},
{0x2B, "sub", REG_OPER_OP_ORDER}, {0x2B, "sub", REG_OPER_OP_ORDER},
{0x31, "xor", OPER_REG_OP_ORDER}, {0x31, "xor", OPER_REG_OP_ORDER},
{0x33, "xor", REG_OPER_OP_ORDER}, {0x33, "xor", REG_OPER_OP_ORDER},
{0x38, "cmpb", OPER_REG_OP_ORDER}, {0x38, "cmpb", OPER_REG_OP_ORDER},
{0x3A, "cmpb", REG_OPER_OP_ORDER}, {0x39, "cmp", OPER_REG_OP_ORDER},
{0x3B, "cmp", REG_OPER_OP_ORDER}, {0x3A, "cmpb", REG_OPER_OP_ORDER},
{0x84, "test_b", REG_OPER_OP_ORDER}, {0x3B, "cmp", REG_OPER_OP_ORDER},
{0x85, "test", REG_OPER_OP_ORDER}, {0x84, "test_b", REG_OPER_OP_ORDER},
{0x87, "xchg", REG_OPER_OP_ORDER}, {0x85, "test", REG_OPER_OP_ORDER},
{0x8A, "mov_b", REG_OPER_OP_ORDER}, {0x87, "xchg", REG_OPER_OP_ORDER},
{0x8B, "mov", REG_OPER_OP_ORDER}, {0x8A, "mov_b", REG_OPER_OP_ORDER},
{0x8D, "lea", REG_OPER_OP_ORDER}, {0x8B, "mov", REG_OPER_OP_ORDER},
{-1, "", UNSET_OP_ORDER} {0x8D, "lea", REG_OPER_OP_ORDER},
}; {-1, "", UNSET_OP_ORDER}};
static const ByteMnemonic zero_operands_instr[] = { static const ByteMnemonic zero_operands_instr[] = {
{0xC3, "ret", UNSET_OP_ORDER}, {0xC3, "ret", UNSET_OP_ORDER},
......
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