Add deoptimization translations.

BUG=
R=bmeurer@chromium.org, mstarzinger@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22924 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
jarin@chromium.org 2014-08-06 11:49:02 +00:00
parent 71857295b4
commit 117945ec6e
15 changed files with 229 additions and 76 deletions

View File

@ -172,9 +172,9 @@ AstGraphBuilder::Environment::Environment(AstGraphBuilder* builder,
parameters_node_(NULL),
locals_node_(NULL),
stack_node_(NULL),
parameters_dirty_(false),
locals_dirty_(false),
stack_dirty_(false) {
parameters_dirty_(true),
locals_dirty_(true),
stack_dirty_(true) {
DCHECK_EQ(scope->num_parameters() + 1, parameters_count());
// Bind the receiver variable.
@ -210,22 +210,40 @@ AstGraphBuilder::Environment::Environment(const Environment& copy)
Node* AstGraphBuilder::Environment::Checkpoint(BailoutId ast_id) {
UNIMPLEMENTED(); // TODO(mstarzinger): Implementation below is incomplete.
if (parameters_dirty_) {
Node** parameters = &values()->front();
parameters_node_ = graph()->NewNode(NULL, parameters_count(), parameters);
Operator* op = common()->StateValues(parameters_count());
if (parameters_count() != 0) {
Node** parameters = &values()->front();
parameters_node_ = graph()->NewNode(op, parameters_count(), parameters);
} else {
parameters_node_ = graph()->NewNode(op);
}
parameters_dirty_ = false;
}
if (locals_dirty_) {
Node** locals = &values()->at(parameters_count_);
locals_node_ = graph()->NewNode(NULL, locals_count(), locals);
Operator* op = common()->StateValues(locals_count());
if (locals_count() != 0) {
Node** locals = &values()->at(parameters_count_);
locals_node_ = graph()->NewNode(op, locals_count(), locals);
} else {
locals_node_ = graph()->NewNode(op);
}
locals_dirty_ = false;
}
FrameStateDescriptor descriptor(ast_id);
// TODO(jarin): add environment to the node.
Operator* op = common()->FrameState(descriptor);
if (stack_dirty_) {
Operator* op = common()->StateValues(stack_height());
if (stack_height() != 0) {
Node** stack = &values()->at(parameters_count_ + locals_count_);
stack_node_ = graph()->NewNode(op, stack_height(), stack);
} else {
stack_node_ = graph()->NewNode(op);
}
stack_dirty_ = false;
}
return graph()->NewNode(op);
Operator* op = common()->FrameState(ast_id);
return graph()->NewNode(op, parameters_node_, locals_node_, stack_node_);
}
@ -1951,8 +1969,7 @@ void AstGraphBuilder::BuildLazyBailout(Node* node, BailoutId ast_id) {
NewNode(common()->LazyDeoptimization());
FrameStateDescriptor stateDescriptor(ast_id);
Node* state_node = NewNode(common()->FrameState(stateDescriptor));
Node* state_node = environment()->Checkpoint(ast_id);
Node* deoptimize_node = NewNode(common()->Deoptimize(), state_node);

View File

@ -236,6 +236,7 @@ class AstGraphBuilder::Environment
DCHECK(stack_height() > 0);
Node* back = values()->back();
values()->pop_back();
stack_dirty_ = true;
return back;
}
@ -244,6 +245,7 @@ class AstGraphBuilder::Environment
DCHECK(depth >= 0 && depth < stack_height());
int index = static_cast<int>(values()->size()) - depth - 1;
values()->at(index) = node;
stack_dirty_ = true;
}
Node* Peek(int depth) {
DCHECK(depth >= 0 && depth < stack_height());
@ -253,6 +255,7 @@ class AstGraphBuilder::Environment
void Drop(int depth) {
DCHECK(depth >= 0 && depth <= stack_height());
values()->erase(values()->end() - depth, values()->end());
stack_dirty_ = true;
}
// Preserve a checkpoint of the environment for the IR graph. Any

View File

@ -213,8 +213,8 @@ void CodeGenerator::PopulateDeoptimizationData(Handle<Code> code_object) {
// Populate deoptimization entries.
for (int i = 0; i < deopt_count; i++) {
FrameStateDescriptor descriptor = code()->GetDeoptimizationEntry(i);
data->SetAstId(i, descriptor.bailout_id());
FrameStateDescriptor* descriptor = code()->GetDeoptimizationEntry(i);
data->SetAstId(i, descriptor->bailout_id());
data->SetTranslationIndex(i, Smi::FromInt(0));
data->SetArgumentsStackHeight(i, Smi::FromInt(0));
data->SetPc(i, Smi::FromInt(-1));
@ -269,24 +269,61 @@ void CodeGenerator::BuildTranslation(Instruction* instr,
// We should build translation only once.
DCHECK_EQ(NULL, deoptimization_states_[deoptimization_id]);
// TODO(jarin) This should build translation codes from the instruction inputs
// and from the framestate descriptor. At the moment, we only create a dummy
// translation.
FrameStateDescriptor descriptor =
FrameStateDescriptor* descriptor =
code()->GetDeoptimizationEntry(deoptimization_id);
Translation translation(&translations_, 1, 1, zone());
translation.BeginJSFrame(descriptor.bailout_id(), Translation::kSelfLiteralId,
0);
int undefined_literal_id =
DefineDeoptimizationLiteral(isolate()->factory()->undefined_value());
translation.StoreLiteral(undefined_literal_id);
translation.BeginJSFrame(descriptor->bailout_id(),
Translation::kSelfLiteralId,
descriptor->size() - descriptor->parameters_count());
for (int i = 0; i < descriptor->size(); i++) {
AddTranslationForOperand(&translation, instr, instr->InputAt(i));
}
deoptimization_states_[deoptimization_id] =
new (zone()) DeoptimizationState(translation.index());
}
void CodeGenerator::AddTranslationForOperand(Translation* translation,
Instruction* instr,
InstructionOperand* op) {
if (op->IsStackSlot()) {
translation->StoreStackSlot(op->index());
} else if (op->IsDoubleStackSlot()) {
translation->StoreDoubleStackSlot(op->index());
} else if (op->IsRegister()) {
InstructionOperandConverter converter(this, instr);
translation->StoreRegister(converter.ToRegister(op));
} else if (op->IsDoubleRegister()) {
InstructionOperandConverter converter(this, instr);
translation->StoreDoubleRegister(converter.ToDoubleRegister(op));
} else if (op->IsImmediate()) {
InstructionOperandConverter converter(this, instr);
Constant constant = converter.ToConstant(op);
Handle<Object> constant_object;
switch (constant.type()) {
case Constant::kInt32:
constant_object =
isolate()->factory()->NewNumberFromInt(constant.ToInt32());
break;
case Constant::kFloat64:
constant_object =
isolate()->factory()->NewHeapNumber(constant.ToFloat64());
break;
case Constant::kHeapObject:
constant_object = constant.ToHeapObject();
break;
default:
UNREACHABLE();
}
int literal_id = DefineDeoptimizationLiteral(constant_object);
translation->StoreLiteral(literal_id);
} else {
UNREACHABLE();
}
}
#if !V8_TURBOFAN_BACKEND
void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
@ -337,7 +374,6 @@ bool CodeGenerator::IsNopForSmiCodeInlining(Handle<Code> code, int start_pc,
#endif // !V8_TURBOFAN_BACKEND
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -85,6 +85,8 @@ class CodeGenerator V8_FINAL : public GapResolver::Assembler {
void PopulateDeoptimizationData(Handle<Code> code);
int DefineDeoptimizationLiteral(Handle<Object> literal);
void BuildTranslation(Instruction* instr, int deoptimization_id);
void AddTranslationForOperand(Translation* translation, Instruction* instr,
InstructionOperand* op);
void AddNopForSmiCodeInlining();
#if DEBUG
static bool IsNopForSmiCodeInlining(Handle<Code> code, int start_pc,

View File

@ -43,17 +43,6 @@ class CallOperator : public Operator1<CallDescriptor*> {
}
};
class FrameStateDescriptor {
public:
explicit FrameStateDescriptor(BailoutId bailout_id)
: bailout_id_(bailout_id) {}
BailoutId bailout_id() const { return bailout_id_; }
private:
BailoutId bailout_id_;
};
// Interface for building common operators that can be used at any level of IR,
// including JavaScript, mid-level, and low-level.
// TODO(titzer): Move the mnemonics into SimpleOperator and Operator1 classes.
@ -141,9 +130,13 @@ class CommonOperatorBuilder {
return new (zone_) Operator1<int>(IrOpcode::kEffectPhi, Operator::kPure, 0,
0, "EffectPhi", arguments);
}
Operator* FrameState(const FrameStateDescriptor& descriptor) {
return new (zone_) Operator1<FrameStateDescriptor>(
IrOpcode::kFrameState, Operator::kPure, 0, 1, "FrameState", descriptor);
Operator* StateValues(int arguments) {
return new (zone_) Operator1<int>(IrOpcode::kStateValues, Operator::kPure,
arguments, 1, "StateValues", arguments);
}
Operator* FrameState(BailoutId ast_id) {
return new (zone_) Operator1<BailoutId>(
IrOpcode::kFrameState, Operator::kPure, 3, 1, "FrameState", ast_id);
}
Operator* Call(CallDescriptor* descriptor) {
return new (zone_) CallOperator(descriptor, "Call");

View File

@ -423,7 +423,7 @@ void InstructionSelector::VisitControl(BasicBlock* block) {
case BasicBlockData::kThrow:
return VisitThrow(input);
case BasicBlockData::kDeoptimize:
return VisitDeoptimization(input);
return VisitDeoptimize(input);
case BasicBlockData::kCall: {
BasicBlock* deoptimization = block->SuccessorAt(0);
BasicBlock* continuation = block->SuccessorAt(1);
@ -490,7 +490,7 @@ void InstructionSelector::VisitNode(Node* node) {
case IrOpcode::kCall:
return VisitCall(node, NULL, NULL);
case IrOpcode::kFrameState:
// TODO(titzer): state nodes should be combined into their users.
case IrOpcode::kStateValues:
return;
case IrOpcode::kLoad: {
MachineRepresentation load_rep = OpParameter<MachineRepresentation>(node);
@ -953,15 +953,56 @@ void InstructionSelector::VisitThrow(Node* value) {
}
void InstructionSelector::VisitDeoptimization(Node* deopt) {
static InstructionOperand* UseOrImmediate(OperandGenerator* g, Node* input) {
switch (input->opcode()) {
case IrOpcode::kInt32Constant:
case IrOpcode::kNumberConstant:
case IrOpcode::kFloat64Constant:
case IrOpcode::kHeapConstant:
return g->UseImmediate(input);
default:
return g->Use(input);
}
}
void InstructionSelector::VisitDeoptimize(Node* deopt) {
DCHECK(deopt->op()->opcode() == IrOpcode::kDeoptimize);
Node* state = deopt->InputAt(0);
DCHECK(state->op()->opcode() == IrOpcode::kFrameState);
FrameStateDescriptor descriptor = OpParameter<FrameStateDescriptor>(state);
// TODO(jarin) We should also add an instruction input for every input to
// the framestate node (and recurse for the inlined framestates).
BailoutId ast_id = OpParameter<BailoutId>(state);
// Add the inputs.
Node* parameters = state->InputAt(0);
int parameters_count = OpParameter<int>(parameters);
Node* locals = state->InputAt(1);
int locals_count = OpParameter<int>(locals);
Node* stack = state->InputAt(2);
int stack_count = OpParameter<int>(stack);
OperandGenerator g(this);
std::vector<InstructionOperand*> inputs;
inputs.reserve(parameters_count + locals_count + stack_count);
for (int i = 0; i < parameters_count; i++) {
inputs.push_back(UseOrImmediate(&g, parameters->InputAt(i)));
}
for (int i = 0; i < locals_count; i++) {
inputs.push_back(UseOrImmediate(&g, locals->InputAt(i)));
}
for (int i = 0; i < stack_count; i++) {
inputs.push_back(UseOrImmediate(&g, stack->InputAt(i)));
}
FrameStateDescriptor* descriptor = new (instruction_zone())
FrameStateDescriptor(ast_id, parameters_count, locals_count, stack_count);
DCHECK_EQ(descriptor->size(), inputs.size());
int deoptimization_id = sequence()->AddDeoptimizationEntry(descriptor);
Emit(kArchDeoptimize | MiscField::encode(deoptimization_id), NULL);
Emit(kArchDeoptimize | MiscField::encode(deoptimization_id), 0, NULL,
inputs.size(), &inputs.front(), 0, NULL);
}

View File

@ -179,7 +179,7 @@ class InstructionSelector V8_FINAL {
void VisitBranch(Node* input, BasicBlock* tbranch, BasicBlock* fbranch);
void VisitReturn(Node* value);
void VisitThrow(Node* value);
void VisitDeoptimization(Node* deopt);
void VisitDeoptimize(Node* deopt);
// ===========================================================================

View File

@ -394,13 +394,13 @@ void InstructionSequence::AddGapMove(int index, InstructionOperand* from,
int InstructionSequence::AddDeoptimizationEntry(
const FrameStateDescriptor& descriptor) {
FrameStateDescriptor* descriptor) {
int deoptimization_id = static_cast<int>(deoptimization_entries_.size());
deoptimization_entries_.push_back(descriptor);
return deoptimization_id;
}
FrameStateDescriptor InstructionSequence::GetDeoptimizationEntry(
FrameStateDescriptor* InstructionSequence::GetDeoptimizationEntry(
int deoptimization_id) {
return deoptimization_entries_[deoptimization_id];
}

View File

@ -694,6 +694,30 @@ class Constant V8_FINAL {
int64_t value_;
};
class FrameStateDescriptor : public ZoneObject {
public:
FrameStateDescriptor(BailoutId bailout_id, int parameters_count,
int locals_count, int stack_count)
: bailout_id_(bailout_id),
parameters_count_(parameters_count),
locals_count_(locals_count),
stack_count_(stack_count) {}
BailoutId bailout_id() const { return bailout_id_; }
int parameters_count() { return parameters_count_; }
int locals_count() { return locals_count_; }
int stack_count() { return stack_count_; }
int size() { return parameters_count_ + locals_count_ + stack_count_; }
private:
BailoutId bailout_id_;
int parameters_count_;
int locals_count_;
int stack_count_;
};
OStream& operator<<(OStream& os, const Constant& constant);
typedef std::deque<Constant, zone_allocator<Constant> > ConstantDeque;
@ -704,7 +728,8 @@ typedef std::map<int, Constant, std::less<int>,
typedef std::deque<Instruction*, zone_allocator<Instruction*> >
InstructionDeque;
typedef std::deque<PointerMap*, zone_allocator<PointerMap*> > PointerMapDeque;
typedef std::vector<FrameStateDescriptor, zone_allocator<FrameStateDescriptor> >
typedef std::vector<FrameStateDescriptor*,
zone_allocator<FrameStateDescriptor*> >
DeoptimizationVector;
@ -814,8 +839,8 @@ class InstructionSequence V8_FINAL {
return immediates_[index];
}
int AddDeoptimizationEntry(const FrameStateDescriptor& descriptor);
FrameStateDescriptor GetDeoptimizationEntry(int deoptimization_id);
int AddDeoptimizationEntry(FrameStateDescriptor* descriptor);
FrameStateDescriptor* GetDeoptimizationEntry(int deoptimization_id);
int GetDeoptimizationEntryCount();
private:

View File

@ -34,6 +34,7 @@
V(Phi) \
V(EffectPhi) \
V(FrameState) \
V(StateValues) \
V(Call) \
V(Parameter) \
V(Projection)

View File

@ -272,6 +272,11 @@ Bounds Typer::Visitor::TypeFrameState(Node* node) {
}
Bounds Typer::Visitor::TypeStateValues(Node* node) {
return Bounds(Type::None(zone()));
}
Bounds Typer::Visitor::TypeCall(Node* node) {
return Bounds::Unbounded(zone());
}

View File

@ -79,9 +79,6 @@
##############################################################################
# TurboFan compiler failures.
# TODO(jarin): Lazy deoptimization test.
'test-run-deopt/TurboSimpleDeopt': [SKIP],
# TODO(mstarzinger): These need investigation and are not categorized yet.
'test-cpu-profiler/*': [SKIP],
'test-heap/NextCodeLinkIsWeak': [PASS, NO_VARIANTS],

View File

@ -149,8 +149,12 @@ class TrivialDeoptCodegenTester : public DeoptCodegenTester {
m.NewNode(common.LazyDeoptimization(), call);
bailout_id = GetCallBailoutId();
FrameStateDescriptor stateDescriptor(bailout_id);
Node* state_node = m.NewNode(common.FrameState(stateDescriptor));
Node* parameters = m.NewNode(common.StateValues(1), undef_node);
Node* locals = m.NewNode(common.StateValues(0));
Node* stack = m.NewNode(common.StateValues(0));
Node* state_node =
m.NewNode(common.FrameState(bailout_id), parameters, locals, stack);
m.Deoptimize(state_node);
// Schedule the graph:
@ -280,8 +284,12 @@ class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
m.NewNode(common.LazyDeoptimization(), call);
bailout_id = GetCallBailoutId();
FrameStateDescriptor stateDescriptor(bailout_id);
Node* state_node = m.NewNode(common.FrameState(stateDescriptor));
Node* parameters = m.NewNode(common.StateValues(1), undef_node);
Node* locals = m.NewNode(common.StateValues(0));
Node* stack = m.NewNode(common.StateValues(0));
Node* state_node =
m.NewNode(common.FrameState(bailout_id), parameters, locals, stack);
m.Deoptimize(state_node);
// Schedule the graph:

View File

@ -9,6 +9,7 @@
using namespace v8::internal;
using namespace v8::internal::compiler;
#if V8_TURBOFAN_TARGET
TEST(TurboSimpleDeopt) {
FLAG_allow_natives_syntax = true;
@ -26,6 +27,24 @@ TEST(TurboSimpleDeopt) {
}
TEST(TurboSimpleDeoptInExpr) {
FLAG_allow_natives_syntax = true;
FLAG_turbo_deoptimization = true;
FunctionTester T(
"(function f(a) {"
"var b = 1;"
"var c = 2;"
"if (!%IsOptimized()) return 0;"
"var d = b + (%DeoptimizeFunction(f), c);"
"if (%IsOptimized()) return 0;"
"return d + a; })");
T.CheckCall(T.Val(6), T.Val(3));
}
#endif
TEST(TurboTrivialDeopt) {
FLAG_allow_natives_syntax = true;
FLAG_turbo_deoptimization = true;

View File

@ -1716,7 +1716,7 @@ TEST(BuildScheduleTrivialLazyDeoptCall) {
HandleAndZoneScope scope;
Isolate* isolate = scope.main_isolate();
Graph graph(scope.main_zone());
CommonOperatorBuilder common_builder(scope.main_zone());
CommonOperatorBuilder common(scope.main_zone());
JSOperatorBuilder js_builder(scope.main_zone());
InitializedHandleScope handles;
@ -1761,37 +1761,40 @@ TEST(BuildScheduleTrivialLazyDeoptCall) {
PrintableUnique<Object>::CreateUninitialized(scope.main_zone(),
undef_object);
Node* undef_node = graph.NewNode(common_builder.HeapConstant(undef_constant));
Node* undef_node = graph.NewNode(common.HeapConstant(undef_constant));
Node* start_node = graph.NewNode(common_builder.Start(0));
Node* start_node = graph.NewNode(common.Start(0));
CallDescriptor* descriptor = linkage.GetJSCallDescriptor(0);
Node* call_node = graph.NewNode(common_builder.Call(descriptor),
Node* call_node = graph.NewNode(common.Call(descriptor),
undef_node, // function
undef_node, // context
start_node, // effect
start_node); // control
Node* cont_node = graph.NewNode(common_builder.Continuation(), call_node);
Node* lazy_deopt_node =
graph.NewNode(common_builder.LazyDeoptimization(), call_node);
Node* cont_node = graph.NewNode(common.Continuation(), call_node);
Node* lazy_deopt_node = graph.NewNode(common.LazyDeoptimization(), call_node);
FrameStateDescriptor stateDescriptor(BailoutId(1234));
Node* state_node = graph.NewNode(common_builder.FrameState(stateDescriptor));
Node* parameters = graph.NewNode(common.StateValues(1), undef_node);
Node* locals = graph.NewNode(common.StateValues(0));
Node* stack = graph.NewNode(common.StateValues(0));
Node* return_node = graph.NewNode(common_builder.Return(),
Node* state_node = graph.NewNode(common.FrameState(BailoutId(1234)),
parameters, locals, stack);
Node* return_node = graph.NewNode(common.Return(),
undef_node, // return value
call_node, // effect
cont_node); // control
Node* deoptimization_node = graph.NewNode(common_builder.Deoptimize(),
Node* deoptimization_node = graph.NewNode(common.Deoptimize(),
state_node, // deopt environment
call_node, // effect
lazy_deopt_node); // control
Node* merge_node =
graph.NewNode(common_builder.Merge(2), return_node, deoptimization_node);
graph.NewNode(common.Merge(2), return_node, deoptimization_node);
Node* end_node = graph.NewNode(common_builder.End(), merge_node);
Node* end_node = graph.NewNode(common.End(), merge_node);
graph.SetStart(start_node);
graph.SetEnd(end_node);
@ -1824,9 +1827,12 @@ TEST(BuildScheduleTrivialLazyDeoptCall) {
CHECK(!cont_block->deferred_);
// The lazy deopt block contains framestate + bailout (and nothing else).
CHECK_EQ(deoptimization_node, deopt_block->control_input_);
CHECK_EQ(2, static_cast<int>(deopt_block->nodes_.size()));
CHECK_EQ(5, static_cast<int>(deopt_block->nodes_.size()));
CHECK_EQ(lazy_deopt_node, deopt_block->nodes_[0]);
CHECK_EQ(state_node, deopt_block->nodes_[1]);
CHECK_EQ(IrOpcode::kStateValues, deopt_block->nodes_[1]->op()->opcode());
CHECK_EQ(IrOpcode::kStateValues, deopt_block->nodes_[2]->op()->opcode());
CHECK_EQ(IrOpcode::kStateValues, deopt_block->nodes_[3]->op()->opcode());
CHECK_EQ(state_node, deopt_block->nodes_[4]);
}
#endif