[turbofan] Add the StackSlot operator to turbofan.

The StackSlot operator allows to allocate a spill slot on the stack. We
are going to use this operator to pass floats through pointers to c
functions, which we need for floating point rounding in the case where
the architecture does not provide rounding instructions.

R=titzer@chromium.org, v8-arm-ports@googlegroups.com, v8-ppc-ports@googlegroups.com, v8-mips-ports@googlegroups.com

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

Cr-Commit-Position: refs/heads/master@{#33600}
This commit is contained in:
ahaas 2016-01-29 00:07:40 -08:00 committed by Commit bot
parent 3251a03e81
commit 7a69343778
23 changed files with 226 additions and 13 deletions

View File

@ -535,6 +535,18 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ bind(ool->exit());
break;
}
case kArchStackSlot: {
FrameOffset offset =
frame_access_state()->GetFrameOffset(i.InputInt32(0));
Register base;
if (offset.from_stack_pointer()) {
base = sp;
} else {
base = fp;
}
__ add(i.OutputRegister(0), base, Operand(offset.offset()));
break;
}
case kArmAdd:
__ add(i.OutputRegister(), i.InputRegister(0), i.InputOperand2(1),
i.OutputSBit());

View File

@ -646,6 +646,18 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ Bind(ool->exit());
break;
}
case kArchStackSlot: {
FrameOffset offset =
frame_access_state()->GetFrameOffset(i.InputInt32(0));
Register base;
if (offset.from_stack_pointer()) {
base = __ StackPointer();
} else {
base = fp;
}
__ Add(i.OutputRegister(0), base, Operand(offset.offset()));
break;
}
case kArm64Float32RoundDown:
__ Frintm(i.OutputFloat32Register(), i.InputFloat32Register(0));
break;

View File

@ -499,6 +499,18 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ bind(ool->exit());
break;
}
case kArchStackSlot: {
FrameOffset offset =
frame_access_state()->GetFrameOffset(i.InputInt32(0));
Register base;
if (offset.from_stack_pointer()) {
base = esp;
} else {
base = ebp;
}
__ lea(i.OutputRegister(), Operand(base, offset.offset()));
break;
}
case kIA32Add:
if (HasImmediateInput(instr, 1)) {
__ add(i.InputOperand(0), i.InputImmediate(1));

View File

@ -72,7 +72,8 @@ enum class RecordWriteMode { kValueIsMap, kValueIsPointer, kValueIsAny };
V(CheckedStoreWord32) \
V(CheckedStoreWord64) \
V(CheckedStoreFloat32) \
V(CheckedStoreFloat64)
V(CheckedStoreFloat64) \
V(ArchStackSlot)
#define ARCH_OPCODE_LIST(V) \
COMMON_ARCH_OPCODE_LIST(V) \

View File

@ -175,6 +175,7 @@ int InstructionScheduler::GetInstructionFlags(const Instruction* instr) const {
case kArchStackPointer:
case kArchFramePointer:
case kArchTruncateDoubleToI:
case kArchStackSlot:
return kNoOpcodeFlags;
case kArchPrepareCallCFunction:

View File

@ -21,7 +21,7 @@ namespace compiler {
InstructionSelector::InstructionSelector(
Zone* zone, size_t node_count, Linkage* linkage,
InstructionSequence* sequence, Schedule* schedule,
SourcePositionTable* source_positions,
SourcePositionTable* source_positions, Frame* frame,
SourcePositionMode source_position_mode, Features features)
: zone_(zone),
linkage_(linkage),
@ -36,7 +36,8 @@ InstructionSelector::InstructionSelector(
used_(node_count, false, zone),
virtual_registers_(node_count,
InstructionOperand::kInvalidVirtualRegister, zone),
scheduler_(nullptr) {
scheduler_(nullptr),
frame_(frame) {
instructions_.reserve(node_count);
}
@ -1066,6 +1067,8 @@ void InstructionSelector::VisitNode(Node* node) {
return MarkAsFloat64(node), VisitFloat64InsertLowWord32(node);
case IrOpcode::kFloat64InsertHighWord32:
return MarkAsFloat64(node), VisitFloat64InsertHighWord32(node);
case IrOpcode::kStackSlot:
return VisitStackSlot(node);
case IrOpcode::kLoadStackPointer:
return VisitLoadStackPointer(node);
case IrOpcode::kLoadFramePointer:
@ -1133,6 +1136,14 @@ void InstructionSelector::EmitLookupSwitch(const SwitchInfo& sw,
Emit(kArchLookupSwitch, 0, nullptr, input_count, inputs, 0, nullptr);
}
void InstructionSelector::VisitStackSlot(Node* node) {
int size = 1 << ElementSizeLog2Of(StackSlotRepresentationOf(node->op()));
int slot = frame_->AllocateSpillSlot(size);
OperandGenerator g(this);
Emit(kArchStackSlot, g.DefineAsRegister(node),
sequence()->AddImmediate(Constant(slot)), 0, nullptr);
}
// 32 bit targets do not implement the following instructions.
#if V8_TARGET_ARCH_32_BIT

View File

@ -52,7 +52,7 @@ class InstructionSelector final {
InstructionSelector(
Zone* zone, size_t node_count, Linkage* linkage,
InstructionSequence* sequence, Schedule* schedule,
SourcePositionTable* source_positions,
SourcePositionTable* source_positions, Frame* frame,
SourcePositionMode source_position_mode = kCallSourcePositions,
Features features = SupportedFeatures());
@ -271,6 +271,7 @@ class InstructionSelector final {
BoolVector used_;
IntVector virtual_registers_;
InstructionScheduler* scheduler_;
Frame* frame_;
};
} // namespace compiler

View File

@ -91,6 +91,10 @@ CheckedStoreRepresentation CheckedStoreRepresentationOf(Operator const* op) {
return OpParameter<CheckedStoreRepresentation>(op);
}
MachineRepresentation StackSlotRepresentationOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kStackSlot, op->opcode());
return OpParameter<MachineRepresentation>(op);
}
#define PURE_OP_LIST(V) \
V(Word32And, Operator::kAssociative | Operator::kCommutative, 2, 0, 1) \
@ -281,6 +285,18 @@ struct MachineOperatorGlobalCache {
MACHINE_TYPE_LIST(LOAD)
#undef LOAD
#define STACKSLOT(Type) \
struct StackSlot##Type##Operator final \
: public Operator1<MachineRepresentation> { \
StackSlot##Type##Operator() \
: Operator1<MachineRepresentation>( \
IrOpcode::kStackSlot, Operator::kNoThrow, "StackSlot", 0, 0, 0, \
1, 0, 0, MachineType::Type().representation()) {} \
}; \
StackSlot##Type##Operator kStackSlot##Type;
MACHINE_TYPE_LIST(STACKSLOT)
#undef STACKSLOT
#define STORE(Type) \
struct Store##Type##Operator : public Operator1<StoreRepresentation> { \
explicit Store##Type##Operator(WriteBarrierKind write_barrier_kind) \
@ -381,6 +397,16 @@ const Operator* MachineOperatorBuilder::Load(LoadRepresentation rep) {
return nullptr;
}
const Operator* MachineOperatorBuilder::StackSlot(MachineRepresentation rep) {
#define STACKSLOT(Type) \
if (rep == MachineType::Type().representation()) { \
return &cache_.kStackSlot##Type; \
}
MACHINE_TYPE_LIST(STACKSLOT)
#undef STACKSLOT
UNREACHABLE();
return nullptr;
}
const Operator* MachineOperatorBuilder::Store(StoreRepresentation store_rep) {
switch (store_rep.representation()) {

View File

@ -102,6 +102,7 @@ typedef MachineRepresentation CheckedStoreRepresentation;
CheckedStoreRepresentation CheckedStoreRepresentationOf(Operator const*);
MachineRepresentation StackSlotRepresentationOf(Operator const* op);
// Interface for building machine-level operators. These operators are
// machine-level but machine-independent and thus define a language suitable
@ -305,6 +306,8 @@ class MachineOperatorBuilder final : public ZoneObject {
// store [base + index], value
const Operator* Store(StoreRepresentation rep);
const Operator* StackSlot(MachineRepresentation rep);
// Access to the machine stack.
const Operator* LoadStackPointer();
const Operator* LoadFramePointer();

View File

@ -625,6 +625,13 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ bind(ool->exit());
break;
}
case kArchStackSlot: {
FrameOffset offset =
frame_access_state()->GetFrameOffset(i.InputInt32(0));
__ Addu(i.OutputRegister(), offset.from_stack_pointer() ? sp : fp,
Operand(offset.offset()));
break;
}
case kMipsAdd:
__ Addu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;

View File

@ -635,6 +635,13 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ bind(ool->exit());
break;
}
case kArchStackSlot: {
FrameOffset offset =
frame_access_state()->GetFrameOffset(i.InputInt32(0));
__ Daddu(i.OutputRegister(), offset.from_stack_pointer() ? sp : fp,
Operand(offset.offset()));
break;
}
case kMips64Add:
__ Addu(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
break;

View File

@ -227,6 +227,7 @@
MACHINE_COMPARE_BINOP_LIST(V) \
V(Load) \
V(Store) \
V(StackSlot) \
V(Word32And) \
V(Word32Or) \
V(Word32Xor) \

View File

@ -276,11 +276,8 @@ class PipelineData {
info()->isolate(), instruction_zone(), instruction_blocks);
}
void InitializeRegisterAllocationData(const RegisterConfiguration* config,
CallDescriptor* descriptor,
const char* debug_name) {
void InitializeFrameData(CallDescriptor* descriptor) {
DCHECK(frame_ == nullptr);
DCHECK(register_allocation_data_ == nullptr);
int fixed_frame_size = 0;
if (descriptor != nullptr) {
fixed_frame_size = (descriptor->IsCFunctionCall())
@ -289,6 +286,12 @@ class PipelineData {
: StandardFrameConstants::kFixedSlotCount;
}
frame_ = new (instruction_zone()) Frame(fixed_frame_size, descriptor);
}
void InitializeRegisterAllocationData(const RegisterConfiguration* config,
CallDescriptor* descriptor,
const char* debug_name) {
DCHECK(register_allocation_data_ == nullptr);
register_allocation_data_ = new (register_allocation_zone())
RegisterAllocationData(config, register_allocation_zone(), frame(),
sequence(), debug_name);
@ -816,7 +819,7 @@ struct InstructionSelectionPhase {
void Run(PipelineData* data, Zone* temp_zone, Linkage* linkage) {
InstructionSelector selector(
temp_zone, data->graph()->NodeCount(), linkage, data->sequence(),
data->schedule(), data->source_positions(),
data->schedule(), data->source_positions(), data->frame(),
data->info()->is_source_positions_enabled()
? InstructionSelector::kAllSourcePositions
: InstructionSelector::kCallSourcePositions);
@ -1282,6 +1285,7 @@ bool Pipeline::AllocateRegistersForTesting(const RegisterConfiguration* config,
PipelineData data(&zone_pool, &info, sequence);
Pipeline pipeline(&info);
pipeline.data_ = &data;
pipeline.data_->InitializeFrameData(nullptr);
pipeline.AllocateRegisters(config, nullptr, run_verifier);
return !data.compilation_failed();
}
@ -1304,6 +1308,7 @@ Handle<Code> Pipeline::ScheduleAndGenerateCode(
data->InitializeInstructionSequence();
data->InitializeFrameData(call_descriptor);
// Select and schedule instructions covering the scheduled graph.
Linkage linkage(call_descriptor);
Run<InstructionSelectionPhase>(&linkage);
@ -1325,6 +1330,7 @@ Handle<Code> Pipeline::ScheduleAndGenerateCode(
BeginPhaseKind("register allocation");
bool run_verifier = FLAG_turbo_verify_allocation;
// Allocate registers.
AllocateRegisters(
RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN),

View File

@ -823,6 +823,13 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ bind(ool->exit());
break;
}
case kArchStackSlot: {
FrameOffset offset =
frame_access_state()->GetFrameOffset(i.InputInt32(0));
__ addi(i.OutputRegister(), offset.from_stack_pointer() ? sp : fp,
Operand(offset.offset()));
break;
}
case kPPC_And:
if (HasRegisterInput(instr, 1)) {
__ and_(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1),

View File

@ -79,6 +79,9 @@ class RawMachineAssembler {
Node* Int32Constant(int32_t value) {
return AddNode(common()->Int32Constant(value));
}
Node* StackSlot(MachineRepresentation rep) {
return AddNode(machine()->StackSlot(rep));
}
Node* Int64Constant(int64_t value) {
return AddNode(common()->Int64Constant(value));
}

View File

@ -1962,6 +1962,7 @@ Type* Typer::Visitor::TypeObjectIsSmi(Node* node) {
Type* Typer::Visitor::TypeLoad(Node* node) { return Type::Any(); }
Type* Typer::Visitor::TypeStackSlot(Node* node) { return Type::Any(); }
Type* Typer::Visitor::TypeStore(Node* node) {
UNREACHABLE();

View File

@ -831,6 +831,7 @@ void Verifier::Visitor::Check(Node* node) {
// -----------------------
case IrOpcode::kLoad:
case IrOpcode::kStore:
case IrOpcode::kStackSlot:
case IrOpcode::kWord32And:
case IrOpcode::kWord32Or:
case IrOpcode::kWord32Xor:

View File

@ -740,6 +740,18 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ bind(ool->exit());
break;
}
case kArchStackSlot: {
FrameOffset offset =
frame_access_state()->GetFrameOffset(i.InputInt32(0));
Register base;
if (offset.from_stack_pointer()) {
base = rsp;
} else {
base = rbp;
}
__ leaq(i.OutputRegister(), Operand(base, offset.offset()));
break;
}
case kX64Add32:
ASSEMBLE_BINOP(addl);
break;

View File

@ -587,6 +587,18 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ bind(ool->exit());
break;
}
case kArchStackSlot: {
FrameOffset offset =
frame_access_state()->GetFrameOffset(i.InputInt32(0));
Register base;
if (offset.from_stack_pointer()) {
base = esp;
} else {
base = ebp;
}
__ lea(i.OutputRegister(), Operand(base, offset.offset()));
break;
}
case kX87Add:
if (HasImmediateInput(instr, 1)) {
__ add(i.InputOperand(0), i.InputImmediate(1));

View File

@ -96,7 +96,6 @@ class BufferedRawMachineAssemblerTester
return parameter_nodes_[index];
}
// The BufferedRawMachineAssemblerTester adds a Store node to the IR graph
// to store the graph's return value in memory. The memory address for the
// Store node is provided as a parameter. By storing the return value in
@ -245,7 +244,6 @@ class BufferedRawMachineAssemblerTester<void>
: Load(p3, RawMachineAssembler::Parameter(3));
}
// The BufferedRawMachineAssemblerTester does not pass parameters directly
// to the constructed IR graph. Instead it passes a pointer to the parameter
// to the IR graph, and adds Load nodes to the IR graph to load the

View File

@ -72,7 +72,6 @@ TEST(RunWord32Ctz) {
CHECK_EQ(0, m.Call(uint32_t(0x9afdbc81)));
}
TEST(RunWord32Clz) {
BufferedRawMachineAssemblerTester<int32_t> m(MachineType::Uint32());
m.Return(m.Word32Clz(m.Parameter(0)));

View File

@ -1157,6 +1157,86 @@ TEST(MixedParams_1) { MixedParamTest(1); }
TEST(MixedParams_2) { MixedParamTest(2); }
TEST(MixedParams_3) { MixedParamTest(3); }
template <typename T>
void TestStackSlot(MachineType slot_type, T expected) {
// Test: Generate with a function f which reserves a stack slot, call an inner
// function g from f which writes into the stack slot of f.
if (RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN)
->num_double_registers() < 2)
return;
Isolate* isolate = CcTest::InitIsolateOnce();
// Lots of code to generate the build descriptor for the inner function.
int parray_gp[] = {
RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN)
->GetAllocatableGeneralCode(0),
RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN)
->GetAllocatableGeneralCode(1)};
int rarray_gp[] = {
RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN)
->GetAllocatableGeneralCode(0)};
int parray_fp[] = {
RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN)
->GetAllocatableDoubleCode(0),
RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN)
->GetAllocatableDoubleCode(1)};
int rarray_fp[] = {
RegisterConfiguration::ArchDefault(RegisterConfiguration::TURBOFAN)
->GetAllocatableDoubleCode(0)};
Allocator palloc(parray_gp, 2, parray_fp, 2);
Allocator ralloc(rarray_gp, 1, rarray_fp, 1);
RegisterConfig config(palloc, ralloc);
Zone zone;
HandleScope scope(isolate);
MachineSignature::Builder builder(&zone, 1, 12);
builder.AddReturn(MachineType::Int32());
for (int i = 0; i < 10; i++) {
builder.AddParam(MachineType::Int32());
}
builder.AddParam(slot_type);
builder.AddParam(MachineType::Pointer());
MachineSignature* sig = builder.Build();
CallDescriptor* desc = config.Create(&zone, sig);
// Create inner function g. g has lots of parameters so that they are passed
// over the stack.
Handle<Code> inner;
Graph graph(&zone);
RawMachineAssembler g(isolate, &graph, desc);
g.Store(slot_type.representation(), g.Parameter(11), g.Parameter(10),
WriteBarrierKind::kNoWriteBarrier);
g.Return(g.Parameter(9));
inner = CompileGraph("Compute", desc, &graph, g.Export());
// Create function f with a stack slot which calls the inner function g.
BufferedRawMachineAssemblerTester<T> f(slot_type);
Node* target = f.HeapConstant(inner);
Node* stack_slot = f.StackSlot(slot_type.representation());
Node* args[12];
for (int i = 0; i < 10; i++) {
args[i] = f.Int32Constant(i);
}
args[10] = f.Parameter(0);
args[11] = stack_slot;
f.CallN(desc, target, args);
f.Return(f.Load(slot_type, stack_slot, f.IntPtrConstant(0)));
CHECK_EQ(expected, f.Call(expected));
}
TEST(RunStackSlotInt32) { TestStackSlot(MachineType::Int32(), 0x12345678); }
#if !V8_TARGET_ARCH_32_BIT
TEST(RunStackSlotInt64) {
TestStackSlot(MachineType::Int64(), 0x123456789abcdef0);
}
#endif
TEST(RunStackSlotFloat32) { TestStackSlot(MachineType::Float32(), 1234.125f); }
TEST(RunStackSlotFloat64) { TestStackSlot(MachineType::Float64(), 3456.375); }
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -40,7 +40,7 @@ InstructionSelectorTest::Stream InstructionSelectorTest::StreamBuilder::Build(
instruction_blocks);
SourcePositionTable source_position_table(graph());
InstructionSelector selector(test_->zone(), node_count, &linkage, &sequence,
schedule, &source_position_table,
schedule, &source_position_table, nullptr,
source_position_mode, features);
selector.SelectInstructions();
if (FLAG_trace_turbo) {