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) {
}
break;
case kIA32Cmp:
if (HasImmediateInput(instr, 1)) {
__ cmp(i.InputOperand(0), i.InputImmediate(1));
if (AddressingModeField::decode(instr->opcode()) != kMode_None) {
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 {
__ 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;
case kIA32Test:
if (HasImmediateInput(instr, 1)) {
__ test(i.InputOperand(0), i.InputImmediate(1));
if (AddressingModeField::decode(instr->opcode()) != kMode_None) {
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 {
__ 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;
case kIA32Imul:
......
......@@ -989,6 +989,46 @@ bool InstructionSelector::IsTailCallAddressImmediate() { return true; }
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.
void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
InstructionOperand left, InstructionOperand right,
......@@ -1034,26 +1074,41 @@ void VisitFloat64Compare(InstructionSelector* selector, Node* node,
VisitCompare(selector, kSSEFloat64Cmp, right, left, cont, false);
}
// Shared routine for multiple word compare operations.
void VisitWordCompare(InstructionSelector* selector, Node* node,
InstructionCode opcode, FlagsContinuation* cont) {
IA32OperandGenerator g(selector);
Node* const left = node->InputAt(0);
Node* const right = node->InputAt(1);
Node* left = node->InputAt(0);
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)) {
VisitCompare(selector, opcode, g.Use(left), g.UseImmediate(right), cont);
} else if (g.CanBeImmediate(left)) {
if (CanUseMemoryOperand(selector, opcode, node, 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();
VisitCompare(selector, opcode, g.Use(right), g.UseImmediate(left), cont);
} else {
VisitCompare(selector, opcode, left, right, cont,
node->op()->HasProperty(Operator::kCommutative));
std::swap(left, right);
}
}
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,
FlagsContinuation* cont) {
......
......@@ -34,6 +34,7 @@ InstructionSelector::InstructionSelector(
instructions_(zone),
defined_(node_count, false, zone),
used_(node_count, false, zone),
effect_level_(node_count, 0, zone),
virtual_registers_(node_count,
InstructionOperand::kInvalidVirtualRegister, zone),
scheduler_(nullptr),
......@@ -218,10 +219,11 @@ Instruction* InstructionSelector::Emit(Instruction* instr) {
bool InstructionSelector::CanCover(Node* user, Node* node) const {
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) {
DCHECK_NOT_NULL(node);
size_t const id = node->id();
......@@ -280,6 +282,19 @@ void InstructionSelector::MarkAsUsed(Node* node) {
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,
const InstructionOperand& op) {
......@@ -669,6 +684,16 @@ void InstructionSelector::VisitBlock(BasicBlock* block) {
current_block_ = block;
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
// "bottom up".
VisitControl(block);
......
......@@ -149,6 +149,9 @@ class InstructionSelector final {
// Checks if {node} is currently live.
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);
const std::map<NodeId, int> GetVirtualRegistersForTesting() const;
......@@ -168,6 +171,9 @@ class InstructionSelector final {
// will need to generate code for it.
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
// by {node}.
void MarkAsRepresentation(MachineRepresentation rep, Node* node);
......@@ -269,6 +275,7 @@ class InstructionSelector final {
ZoneVector<Instruction*> instructions_;
BoolVector defined_;
BoolVector used_;
IntVector effect_level_;
IntVector virtual_registers_;
InstructionScheduler* scheduler_;
Frame* frame_;
......
......@@ -259,6 +259,32 @@ class OutOfLineRecordWrite final : public OutOfLineCode {
} \
} 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) \
do { \
......@@ -771,16 +797,16 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
ASSEMBLE_BINOP(andq);
break;
case kX64Cmp32:
ASSEMBLE_BINOP(cmpl);
ASSEMBLE_COMPARE(cmpl);
break;
case kX64Cmp:
ASSEMBLE_BINOP(cmpq);
ASSEMBLE_COMPARE(cmpq);
break;
case kX64Test32:
ASSEMBLE_BINOP(testl);
ASSEMBLE_COMPARE(testl);
break;
case kX64Test:
ASSEMBLE_BINOP(testq);
ASSEMBLE_COMPARE(testq);
break;
case kX64Imul32:
ASSEMBLE_MULT(imull);
......
......@@ -1338,6 +1338,48 @@ bool InstructionSelector::IsTailCallAddressImmediate() { return true; }
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.
void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
InstructionOperand left, InstructionOperand right,
......@@ -1365,26 +1407,41 @@ void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
VisitCompare(selector, opcode, g.UseRegister(left), g.Use(right), cont);
}
// Shared routine for multiple word compare operations.
void VisitWordCompare(InstructionSelector* selector, Node* node,
InstructionCode opcode, FlagsContinuation* cont) {
X64OperandGenerator g(selector);
Node* const left = node->InputAt(0);
Node* const right = node->InputAt(1);
Node* left = node->InputAt(0);
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)) {
VisitCompare(selector, opcode, g.Use(left), g.UseImmediate(right), cont);
} else if (g.CanBeImmediate(left)) {
if (CanUseMemoryOperand(selector, opcode, node, 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();
VisitCompare(selector, opcode, g.Use(right), g.UseImmediate(left), cont);
} else {
VisitCompare(selector, opcode, left, right, cont,
node->op()->HasProperty(Operator::kCommutative));
std::swap(left, right);
}
}
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.
void VisitWord64Compare(InstructionSelector* selector, Node* node,
......
......@@ -803,6 +803,11 @@ void Assembler::cmp(Register reg, const Operand& 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) {
EnsureSpace ensure_space(this);
......
......@@ -678,6 +678,7 @@ class Assembler : public AssemblerBase {
void cmp(Register reg0, Register reg1) { cmp(reg0, Operand(reg1)); }
void cmp(Register reg, const Operand& op);
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, Handle<Object> handle);
......
......@@ -28,32 +28,30 @@ struct ByteMnemonic {
OperandOrder op_order_;
};
static const ByteMnemonic two_operands_instr[] = {
{0x01, "add", OPER_REG_OP_ORDER},
{0x03, "add", REG_OPER_OP_ORDER},
{0x09, "or", OPER_REG_OP_ORDER},
{0x0B, "or", REG_OPER_OP_ORDER},
{0x1B, "sbb", REG_OPER_OP_ORDER},
{0x21, "and", OPER_REG_OP_ORDER},
{0x23, "and", REG_OPER_OP_ORDER},
{0x29, "sub", OPER_REG_OP_ORDER},
{0x2A, "subb", REG_OPER_OP_ORDER},
{0x2B, "sub", REG_OPER_OP_ORDER},
{0x31, "xor", OPER_REG_OP_ORDER},
{0x33, "xor", REG_OPER_OP_ORDER},
{0x38, "cmpb", OPER_REG_OP_ORDER},
{0x3A, "cmpb", REG_OPER_OP_ORDER},
{0x3B, "cmp", REG_OPER_OP_ORDER},
{0x84, "test_b", REG_OPER_OP_ORDER},
{0x85, "test", REG_OPER_OP_ORDER},
{0x87, "xchg", REG_OPER_OP_ORDER},
{0x8A, "mov_b", REG_OPER_OP_ORDER},
{0x8B, "mov", REG_OPER_OP_ORDER},
{0x8D, "lea", REG_OPER_OP_ORDER},
{-1, "", UNSET_OP_ORDER}
};
{0x01, "add", OPER_REG_OP_ORDER},
{0x03, "add", REG_OPER_OP_ORDER},
{0x09, "or", OPER_REG_OP_ORDER},
{0x0B, "or", REG_OPER_OP_ORDER},
{0x1B, "sbb", REG_OPER_OP_ORDER},
{0x21, "and", OPER_REG_OP_ORDER},
{0x23, "and", REG_OPER_OP_ORDER},
{0x29, "sub", OPER_REG_OP_ORDER},
{0x2A, "subb", REG_OPER_OP_ORDER},
{0x2B, "sub", REG_OPER_OP_ORDER},
{0x31, "xor", OPER_REG_OP_ORDER},
{0x33, "xor", REG_OPER_OP_ORDER},
{0x38, "cmpb", OPER_REG_OP_ORDER},
{0x39, "cmp", OPER_REG_OP_ORDER},
{0x3A, "cmpb", REG_OPER_OP_ORDER},
{0x3B, "cmp", REG_OPER_OP_ORDER},
{0x84, "test_b", REG_OPER_OP_ORDER},
{0x85, "test", REG_OPER_OP_ORDER},
{0x87, "xchg", REG_OPER_OP_ORDER},
{0x8A, "mov_b", REG_OPER_OP_ORDER},
{0x8B, "mov", REG_OPER_OP_ORDER},
{0x8D, "lea", REG_OPER_OP_ORDER},
{-1, "", UNSET_OP_ORDER}};
static const ByteMnemonic zero_operands_instr[] = {
{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