[x86] Faster/shorter code for stack checks.

Avoid loading the stack pointer and the stack limit into allocatable
registers first, but generate a cmp rsp,[limit] instead.

R=dcarney@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#27065}
This commit is contained in:
Benedikt Meurer 2015-03-09 12:06:30 +01:00
parent 82a28adf05
commit d18bfa1130
11 changed files with 169 additions and 26 deletions

View File

@ -730,6 +730,12 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
case kCheckedStoreFloat64:
ASSEMBLE_CHECKED_STORE_FLOAT(movsd);
break;
case kIA32StackCheck: {
ExternalReference const stack_limit =
ExternalReference::address_of_stack_limit(isolate());
__ cmp(esp, Operand::StaticVariable(stack_limit));
break;
}
}
}

View File

@ -66,7 +66,8 @@ namespace compiler {
V(IA32Movsd) \
V(IA32Lea) \
V(IA32Push) \
V(IA32StoreWriteBarrier)
V(IA32StoreWriteBarrier) \
V(IA32StackCheck)
// Addressing modes represent the "shape" of inputs to an instruction.

View File

@ -841,6 +841,26 @@ void VisitWordCompare(InstructionSelector* selector, Node* node,
void VisitWordCompare(InstructionSelector* selector, Node* node,
FlagsContinuation* cont) {
IA32OperandGenerator g(selector);
Int32BinopMatcher m(node);
if (m.left().IsLoad() && m.right().IsLoadStackPointer()) {
LoadMatcher<ExternalReferenceMatcher> mleft(m.left().node());
ExternalReference js_stack_limit =
ExternalReference::address_of_stack_limit(selector->isolate());
if (mleft.object().Is(js_stack_limit) && mleft.index().Is(0)) {
// Compare(Load(js_stack_limit), LoadStackPointer)
if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute();
InstructionCode opcode = cont->Encode(kIA32StackCheck);
if (cont->IsBranch()) {
selector->Emit(opcode, g.NoOutput(), g.Label(cont->true_block()),
g.Label(cont->false_block()))->MarkAsControl();
} else {
DCHECK(cont->IsSet());
selector->Emit(opcode, g.DefineAsRegister(cont->result()));
}
return;
}
}
VisitWordCompare(selector, node, kIA32Cmp, cont);
}

View File

@ -129,6 +129,8 @@ class InstructionSelector FINAL {
int GetVirtualRegister(const Node* node);
const std::map<NodeId, int> GetVirtualRegistersForTesting() const;
Isolate* isolate() const { return sequence()->isolate(); }
private:
friend class OperandGenerator;

View File

@ -7,6 +7,8 @@
#include <cmath>
// TODO(turbofan): Move ExternalReference out of assembler.h
#include "src/assembler.h"
#include "src/compiler/node.h"
#include "src/compiler/operator.h"
#include "src/unique.h"
@ -155,6 +157,32 @@ struct HeapObjectMatcher FINAL
};
// A pattern matcher for external reference constants.
struct ExternalReferenceMatcher FINAL
: public ValueMatcher<ExternalReference, IrOpcode::kExternalConstant> {
explicit ExternalReferenceMatcher(Node* node)
: ValueMatcher<ExternalReference, IrOpcode::kExternalConstant>(node) {}
};
// For shorter pattern matching code, this struct matches the inputs to
// machine-level load operations.
template <typename Object>
struct LoadMatcher : public NodeMatcher {
explicit LoadMatcher(Node* node)
: NodeMatcher(node), object_(InputAt(0)), index_(InputAt(1)) {}
typedef Object ObjectMatcher;
Object const& object() const { return object_; }
IntPtrMatcher const& index() const { return index_; }
private:
Object const object_;
IntPtrMatcher const index_;
};
// For shorter pattern matching code, this struct matches both the left and
// right hand sides of a binary operation and can put constants on the right
// if they appear on the left hand side of a commutative operation.

View File

@ -90,6 +90,9 @@ class RawMachineAssembler : public GraphBuilder {
Unique<HeapObject> val = Unique<HeapObject>::CreateUninitialized(object);
return NewNode(common()->HeapConstant(val));
}
Node* ExternalConstant(ExternalReference address) {
return NewNode(common()->ExternalConstant(address));
}
Node* Projection(int index, Node* a) {
return NewNode(common()->Projection(index), a);
@ -97,14 +100,14 @@ class RawMachineAssembler : public GraphBuilder {
// Memory Operations.
Node* Load(MachineType rep, Node* base) {
return Load(rep, base, Int32Constant(0));
return Load(rep, base, IntPtrConstant(0));
}
Node* Load(MachineType rep, Node* base, Node* index) {
return NewNode(machine()->Load(rep), base, index, graph()->start(),
graph()->start());
}
void Store(MachineType rep, Node* base, Node* value) {
Store(rep, base, Int32Constant(0), value);
Store(rep, base, IntPtrConstant(0), value);
}
void Store(MachineType rep, Node* base, Node* index, Node* value) {
NewNode(machine()->Store(StoreRepresentation(rep, kNoWriteBarrier)), base,
@ -281,6 +284,9 @@ class RawMachineAssembler : public GraphBuilder {
Node* Int64LessThan(Node* a, Node* b) {
return NewNode(machine()->Int64LessThan(), a, b);
}
Node* Uint64LessThan(Node* a, Node* b) {
return NewNode(machine()->Uint64LessThan(), a, b);
}
Node* Int64LessThanOrEqual(Node* a, Node* b) {
return NewNode(machine()->Int64LessThanOrEqual(), a, b);
}
@ -407,6 +413,9 @@ class RawMachineAssembler : public GraphBuilder {
return NewNode(machine()->Float64InsertHighWord32(), a, b);
}
// Stack operations.
Node* LoadStackPointer() { return NewNode(machine()->LoadStackPointer()); }
// Parameters.
Node* Parameter(size_t index);

View File

@ -1051,6 +1051,9 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
case kCheckedStoreFloat64:
ASSEMBLE_CHECKED_STORE_FLOAT(movsd);
break;
case kX64StackCheck:
__ CompareRoot(rsp, Heap::kStackLimitRootIndex);
break;
}
} // NOLINT(readability/fn_size)

View File

@ -87,7 +87,8 @@ namespace compiler {
V(X64Dec32) \
V(X64Inc32) \
V(X64Push) \
V(X64StoreWriteBarrier)
V(X64StoreWriteBarrier) \
V(X64StackCheck)
// Addressing modes represent the "shape" of inputs to an instruction.

View File

@ -987,10 +987,12 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
}
namespace {
// Shared routine for multiple compare operations.
static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
InstructionOperand left, InstructionOperand right,
FlagsContinuation* cont) {
void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
InstructionOperand left, InstructionOperand right,
FlagsContinuation* cont) {
X64OperandGenerator g(selector);
opcode = cont->Encode(opcode);
if (cont->IsBranch()) {
@ -1005,9 +1007,9 @@ static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
// Shared routine for multiple compare operations.
static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
Node* left, Node* right, FlagsContinuation* cont,
bool commutative) {
void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
Node* left, Node* right, FlagsContinuation* cont,
bool commutative) {
X64OperandGenerator g(selector);
if (commutative && g.CanBeBetterLeftOperand(right)) {
std::swap(left, right);
@ -1017,8 +1019,8 @@ static void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
// Shared routine for multiple word compare operations.
static void VisitWordCompare(InstructionSelector* selector, Node* node,
InstructionCode opcode, FlagsContinuation* cont) {
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);
@ -1036,22 +1038,51 @@ static void VisitWordCompare(InstructionSelector* selector, Node* node,
}
// Shared routine for 64-bit word comparison operations.
void VisitWord64Compare(InstructionSelector* selector, Node* node,
FlagsContinuation* cont) {
X64OperandGenerator g(selector);
Int64BinopMatcher m(node);
if (m.left().IsLoad() && m.right().IsLoadStackPointer()) {
LoadMatcher<ExternalReferenceMatcher> mleft(m.left().node());
ExternalReference js_stack_limit =
ExternalReference::address_of_stack_limit(selector->isolate());
if (mleft.object().Is(js_stack_limit) && mleft.index().Is(0)) {
// Compare(Load(js_stack_limit), LoadStackPointer)
if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute();
InstructionCode opcode = cont->Encode(kX64StackCheck);
if (cont->IsBranch()) {
selector->Emit(opcode, g.NoOutput(), g.Label(cont->true_block()),
g.Label(cont->false_block()))->MarkAsControl();
} else {
DCHECK(cont->IsSet());
selector->Emit(opcode, g.DefineAsRegister(cont->result()));
}
return;
}
}
VisitWordCompare(selector, node, kX64Cmp, cont);
}
// Shared routine for comparison with zero.
static void VisitCompareZero(InstructionSelector* selector, Node* node,
InstructionCode opcode, FlagsContinuation* cont) {
void VisitCompareZero(InstructionSelector* selector, Node* node,
InstructionCode opcode, FlagsContinuation* cont) {
X64OperandGenerator g(selector);
VisitCompare(selector, opcode, g.Use(node), g.TempImmediate(0), cont);
}
// Shared routine for multiple float64 compare operations (inputs commuted).
static void VisitFloat64Compare(InstructionSelector* selector, Node* node,
FlagsContinuation* cont) {
void VisitFloat64Compare(InstructionSelector* selector, Node* node,
FlagsContinuation* cont) {
Node* const left = node->InputAt(0);
Node* const right = node->InputAt(1);
VisitCompare(selector, kSSEFloat64Cmp, right, left, cont, false);
}
} // namespace
void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
BasicBlock* fbranch) {
@ -1093,16 +1124,16 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
return VisitWordCompare(this, value, kX64Cmp32, &cont);
case IrOpcode::kWord64Equal:
cont.OverwriteAndNegateIfEqual(kEqual);
return VisitWordCompare(this, value, kX64Cmp, &cont);
return VisitWord64Compare(this, value, &cont);
case IrOpcode::kInt64LessThan:
cont.OverwriteAndNegateIfEqual(kSignedLessThan);
return VisitWordCompare(this, value, kX64Cmp, &cont);
return VisitWord64Compare(this, value, &cont);
case IrOpcode::kInt64LessThanOrEqual:
cont.OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
return VisitWordCompare(this, value, kX64Cmp, &cont);
return VisitWord64Compare(this, value, &cont);
case IrOpcode::kUint64LessThan:
cont.OverwriteAndNegateIfEqual(kUnsignedLessThan);
return VisitWordCompare(this, value, kX64Cmp, &cont);
return VisitWord64Compare(this, value, &cont);
case IrOpcode::kFloat64Equal:
cont.OverwriteAndNegateIfEqual(kUnorderedEqual);
return VisitFloat64Compare(this, value, &cont);
@ -1140,7 +1171,7 @@ void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
case IrOpcode::kInt32Sub:
return VisitWordCompare(this, value, kX64Cmp32, &cont);
case IrOpcode::kInt64Sub:
return VisitWordCompare(this, value, kX64Cmp, &cont);
return VisitWord64Compare(this, value, &cont);
case IrOpcode::kWord32And:
return VisitWordCompare(this, value, kX64Test32, &cont);
case IrOpcode::kWord64And:
@ -1302,7 +1333,7 @@ void InstructionSelector::VisitWord64Equal(Node* const node) {
if (CanCover(user, value)) {
switch (value->opcode()) {
case IrOpcode::kInt64Sub:
return VisitWordCompare(this, value, kX64Cmp, &cont);
return VisitWord64Compare(this, value, &cont);
case IrOpcode::kWord64And:
return VisitWordCompare(this, value, kX64Test, &cont);
default:
@ -1311,7 +1342,7 @@ void InstructionSelector::VisitWord64Equal(Node* const node) {
}
return VisitCompareZero(this, value, kX64Cmp, &cont);
}
VisitWordCompare(this, node, kX64Cmp, &cont);
VisitWord64Compare(this, node, &cont);
}
@ -1337,19 +1368,19 @@ void InstructionSelector::VisitInt32SubWithOverflow(Node* node) {
void InstructionSelector::VisitInt64LessThan(Node* node) {
FlagsContinuation cont(kSignedLessThan, node);
VisitWordCompare(this, node, kX64Cmp, &cont);
VisitWord64Compare(this, node, &cont);
}
void InstructionSelector::VisitInt64LessThanOrEqual(Node* node) {
FlagsContinuation cont(kSignedLessThanOrEqual, node);
VisitWordCompare(this, node, kX64Cmp, &cont);
VisitWord64Compare(this, node, &cont);
}
void InstructionSelector::VisitUint64LessThan(Node* node) {
FlagsContinuation cont(kUnsignedLessThan, node);
VisitWordCompare(this, node, kX64Cmp, &cont);
VisitWord64Compare(this, node, &cont);
}

View File

@ -666,6 +666,29 @@ TEST_F(InstructionSelectorTest, Float64BinopArithmetic) {
}
}
// -----------------------------------------------------------------------------
// Miscellaneous.
TEST_F(InstructionSelectorTest, Uint32LessThanWithLoadAndLoadStackPointer) {
StreamBuilder m(this, kMachBool);
Node* const sl = m.Load(
kMachPtr,
m.ExternalConstant(ExternalReference::address_of_stack_limit(isolate())));
Node* const sp = m.LoadStackPointer();
Node* const n = m.Uint32LessThan(sl, sp);
m.Return(n);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kIA32StackCheck, s[0]->arch_opcode());
ASSERT_EQ(0U, s[0]->InputCount());
ASSERT_EQ(1U, s[0]->OutputCount());
EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
EXPECT_EQ(kFlags_set, s[0]->flags_mode());
EXPECT_EQ(kUnsignedGreaterThan, s[0]->flags_condition());
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -1030,6 +1030,25 @@ TEST_F(InstructionSelectorTest, Float64BinopArithmetic) {
// Miscellaneous.
TEST_F(InstructionSelectorTest, Uint64LessThanWithLoadAndLoadStackPointer) {
StreamBuilder m(this, kMachBool);
Node* const sl = m.Load(
kMachPtr,
m.ExternalConstant(ExternalReference::address_of_stack_limit(isolate())));
Node* const sp = m.LoadStackPointer();
Node* const n = m.Uint64LessThan(sl, sp);
m.Return(n);
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(kX64StackCheck, s[0]->arch_opcode());
ASSERT_EQ(0U, s[0]->InputCount());
ASSERT_EQ(1U, s[0]->OutputCount());
EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output()));
EXPECT_EQ(kFlags_set, s[0]->flags_mode());
EXPECT_EQ(kUnsignedGreaterThan, s[0]->flags_condition());
}
TEST_F(InstructionSelectorTest, Word64ShlWithChangeInt32ToInt64) {
TRACED_FORRANGE(int64_t, x, 32, 63) {
StreamBuilder m(this, kMachInt64, kMachInt32);