[turbofan] Introduce lazy bailout, masked as a call.

This introduces an explicit lazy bailout. It is wrapped in the call
node, mostly because the lazy deoptimization processing is married
to the call processing in the instruction selector and the code generator.

It is still a terrible hack.

R=bmeurer@chromium.org,mstarzinger@chromium.org
BUG=chromium:543994,v8:4195
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#31353}
This commit is contained in:
jarin 2015-10-18 23:20:58 -07:00 committed by Commit bot
parent e1088b27b5
commit f9a9c6be0e
25 changed files with 151 additions and 46 deletions

View File

@ -384,6 +384,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
}
case kArchLazyBailout: {
EnsureSpaceForLazyDeopt();
RecordCallPosition(instr);
break;
}
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
__ PrepareCallCFunction(num_parameters, kScratchReg);

View File

@ -1164,7 +1164,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
}
// Select the appropriate opcode based on the call type.
InstructionCode opcode;
InstructionCode opcode = kArchNop;
switch (descriptor->kind()) {
case CallDescriptor::kCallAddress:
opcode =
@ -1177,9 +1177,9 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction | MiscField::encode(flags);
break;
default:
UNREACHABLE();
return;
case CallDescriptor::kLazyBailout:
opcode = kArchLazyBailout | MiscField::encode(flags);
break;
}
// Emit the call instruction.

View File

@ -477,6 +477,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ Jump(x10);
break;
}
case kArchLazyBailout: {
EnsureSpaceForLazyDeopt();
RecordCallPosition(instr);
break;
}
case kArchPrepareCallCFunction:
// We don't need kArchPrepareCallCFunction on arm64 as the instruction
// selector already perform a Claim to reserve space on the stack and

View File

@ -1477,7 +1477,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
}
// Select the appropriate opcode based on the call type.
InstructionCode opcode;
InstructionCode opcode = kArchNop;
switch (descriptor->kind()) {
case CallDescriptor::kCallAddress:
opcode =
@ -1490,9 +1490,9 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction | MiscField::encode(flags);
break;
default:
UNREACHABLE();
return;
case CallDescriptor::kLazyBailout:
opcode = kArchLazyBailout | MiscField::encode(flags);
break;
}
// Emit the call instruction.

View File

@ -1410,10 +1410,13 @@ void AstGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
}
try_control.EndTry();
// TODO(mstarzinger): We are only using a runtime call to get a lazy bailout
// point, there is no need to really emit an actual call. Optimize this!
Node* guard = NewNode(javascript()->CallRuntime(Runtime::kMaxSmi, 0));
PrepareFrameState(guard, stmt->HandlerId());
// Insert lazy bailout point.
// TODO(mstarzinger): We are only using a 'call' to get a lazy bailout
// point. Ideally, we whould not re-enter optimized code when deoptimized
// lazily. Tracked by issue v8:4195.
NewNode(common()->LazyBailout(),
jsgraph()->ZeroConstant(), // dummy target.
environment()->Checkpoint(stmt->HandlerId())); // frame state.
// Clear message object as we enter the catch block.
Node* the_hole = jsgraph()->TheHoleConstant();
@ -1461,10 +1464,13 @@ void AstGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
}
try_control.EndTry(commands->GetFallThroughToken(), fallthrough_result);
// TODO(mstarzinger): We are only using a runtime call to get a lazy bailout
// point, there is no need to really emit an actual call. Optimize this!
Node* guard = NewNode(javascript()->CallRuntime(Runtime::kMaxSmi, 0));
PrepareFrameState(guard, stmt->HandlerId());
// Insert lazy bailout point.
// TODO(mstarzinger): We are only using a 'call' to get a lazy bailout
// point. Ideally, we whould not re-enter optimized code when deoptimized
// lazily. Tracked by issue v8:4195.
NewNode(common()->LazyBailout(),
jsgraph()->ZeroConstant(), // dummy target.
environment()->Checkpoint(stmt->HandlerId())); // frame state.
// The result value semantics depend on how the block was entered:
// - ReturnStatement: It represents the return value being returned.

View File

@ -740,6 +740,11 @@ const Operator* CommonOperatorBuilder::Call(const CallDescriptor* descriptor) {
}
const Operator* CommonOperatorBuilder::LazyBailout() {
return Call(Linkage::GetLazyBailoutDescriptor(zone()));
}
const Operator* CommonOperatorBuilder::TailCall(
const CallDescriptor* descriptor) {
class TailCallOperator final : public Operator1<const CallDescriptor*> {

View File

@ -155,6 +155,7 @@ class CommonOperatorBuilder final : public ZoneObject {
const Operator* Call(const CallDescriptor* descriptor);
const Operator* TailCall(const CallDescriptor* descriptor);
const Operator* Projection(size_t index);
const Operator* LazyBailout();
// Constructs a new merge or phi operator with the same opcode as {op}, but
// with {size} inputs.

View File

@ -349,6 +349,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset));
break;
}
case kArchLazyBailout: {
EnsureSpaceForLazyDeopt();
RecordCallPosition(instr);
break;
}
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
__ PrepareCallCFunction(num_parameters, i.TempRegister(0));

View File

@ -907,7 +907,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
}
// Select the appropriate opcode based on the call type.
InstructionCode opcode;
InstructionCode opcode = kArchNop;
switch (descriptor->kind()) {
case CallDescriptor::kCallAddress:
opcode =
@ -920,9 +920,9 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction | MiscField::encode(flags);
break;
default:
UNREACHABLE();
return;
case CallDescriptor::kLazyBailout:
opcode = kArchLazyBailout | MiscField::encode(flags);
break;
}
// Emit the call instruction.

View File

@ -42,6 +42,7 @@ namespace compiler {
V(ArchTailCallJSFunction) \
V(ArchPrepareCallCFunction) \
V(ArchCallCFunction) \
V(ArchLazyBailout) \
V(ArchJmp) \
V(ArchLookupSwitch) \
V(ArchTableSwitch) \

View File

@ -345,6 +345,10 @@ void InstructionSelector::InitializeCallBuffer(Node* call, CallBuffer* buffer,
g.UseLocation(callee, buffer->descriptor->GetInputLocation(0),
buffer->descriptor->GetInputType(0)));
break;
case CallDescriptor::kLazyBailout:
// The target is ignored, but we still need to pass a value here.
buffer->instruction_args.push_back(g.UseImmediate(callee));
break;
}
DCHECK_EQ(1u, buffer->instruction_args.size());

View File

@ -63,6 +63,9 @@ std::ostream& operator<<(std::ostream& os, const CallDescriptor::Kind& k) {
case CallDescriptor::kCallAddress:
os << "Addr";
break;
case CallDescriptor::kLazyBailout:
os << "LazyBail";
break;
}
return os;
}
@ -351,6 +354,31 @@ CallDescriptor* Linkage::GetRuntimeCallDescriptor(
}
CallDescriptor* Linkage::GetLazyBailoutDescriptor(Zone* zone) {
const size_t return_count = 0;
const size_t parameter_count = 0;
LocationSignature::Builder locations(zone, return_count, parameter_count);
MachineSignature::Builder types(zone, return_count, parameter_count);
// The target is ignored, but we need to give some values here.
MachineType target_type = kMachAnyTagged;
LinkageLocation target_loc = regloc(kJSFunctionRegister);
return new (zone) CallDescriptor( // --
CallDescriptor::kLazyBailout, // kind
target_type, // target MachineType
target_loc, // target location
types.Build(), // machine_sig
locations.Build(), // location_sig
0, // stack_parameter_count
Operator::kNoThrow, // properties
kNoCalleeSaved, // callee-saved
kNoCalleeSaved, // callee-saved fp
CallDescriptor::kNeedsFrameState, // flags
"lazy-bailout");
}
CallDescriptor* Linkage::GetJSCallDescriptor(Zone* zone, bool is_osr,
int js_parameter_count,
CallDescriptor::Flags flags) {

View File

@ -110,9 +110,10 @@ class CallDescriptor final : public ZoneObject {
public:
// Describes the kind of this call, which determines the target.
enum Kind {
kCallCodeObject, // target is a Code object
kCallJSFunction, // target is a JSFunction object
kCallAddress, // target is a machine pointer
kCallCodeObject, // target is a Code object
kCallJSFunction, // target is a JSFunction object
kCallAddress, // target is a machine pointer
kLazyBailout // the call is no-op, only used for lazy bailout
};
enum Flag {
@ -271,10 +272,13 @@ class Linkage : public ZoneObject {
static CallDescriptor* GetJSCallDescriptor(Zone* zone, bool is_osr,
int parameter_count,
CallDescriptor::Flags flags);
static CallDescriptor* GetRuntimeCallDescriptor(
Zone* zone, Runtime::FunctionId function, int parameter_count,
Operator::Properties properties, bool needs_frame_state = true);
static CallDescriptor* GetLazyBailoutDescriptor(Zone* zone);
static CallDescriptor* GetStubCallDescriptor(
Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor,
int stack_parameter_count, CallDescriptor::Flags flags,

View File

@ -457,6 +457,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ Jump(at);
break;
}
case kArchLazyBailout: {
EnsureSpaceForLazyDeopt();
RecordCallPosition(instr);
break;
}
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
__ PrepareCallCFunction(num_parameters, kScratchReg);

View File

@ -584,7 +584,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
}
// Select the appropriate opcode based on the call type.
InstructionCode opcode;
InstructionCode opcode = kArchNop;
switch (descriptor->kind()) {
case CallDescriptor::kCallAddress:
opcode =
@ -597,9 +597,9 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction | MiscField::encode(flags);
break;
default:
UNREACHABLE();
return;
case CallDescriptor::kLazyBailout:
opcode = kArchLazyBailout | MiscField::encode(flags);
break;
}
// Emit the call instruction.

View File

@ -455,6 +455,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ Jump(at);
break;
}
case kArchLazyBailout: {
EnsureSpaceForLazyDeopt();
RecordCallPosition(instr);
break;
}
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
__ PrepareCallCFunction(num_parameters, kScratchReg);

View File

@ -771,7 +771,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
}
// Select the appropriate opcode based on the call type.
InstructionCode opcode;
InstructionCode opcode = kArchNop;
switch (descriptor->kind()) {
case CallDescriptor::kCallAddress:
opcode =
@ -784,9 +784,9 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction | MiscField::encode(flags);
break;
default:
UNREACHABLE();
return;
case CallDescriptor::kLazyBailout:
opcode = kArchLazyBailout | MiscField::encode(flags);
break;
}
opcode |= MiscField::encode(flags);

View File

@ -654,6 +654,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
DCHECK_EQ(LeaveRC, i.OutputRCBit());
break;
}
case kArchLazyBailout: {
EnsureSpaceForLazyDeopt();
RecordCallPosition(instr);
break;
}
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
__ PrepareCallCFunction(num_parameters, kScratchReg);

View File

@ -1545,7 +1545,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
}
// Select the appropriate opcode based on the call type.
InstructionCode opcode;
InstructionCode opcode = kArchNop;
switch (descriptor->kind()) {
case CallDescriptor::kCallAddress:
opcode =
@ -1558,9 +1558,9 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction | MiscField::encode(flags);
break;
default:
UNREACHABLE();
return;
case CallDescriptor::kLazyBailout:
opcode = kArchLazyBailout | MiscField::encode(flags);
break;
}
// Emit the call instruction.

View File

@ -596,6 +596,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset));
break;
}
case kArchLazyBailout: {
EnsureSpaceForLazyDeopt();
RecordCallPosition(instr);
break;
}
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
__ PrepareCallCFunction(num_parameters);

View File

@ -1132,7 +1132,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
}
// Select the appropriate opcode based on the call type.
InstructionCode opcode;
InstructionCode opcode = kArchNop;
switch (descriptor->kind()) {
case CallDescriptor::kCallAddress:
opcode =
@ -1145,9 +1145,9 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction | MiscField::encode(flags);
break;
default:
UNREACHABLE();
return;
case CallDescriptor::kLazyBailout:
opcode = kArchLazyBailout | MiscField::encode(flags);
break;
}
// Emit the call instruction.

View File

@ -379,6 +379,11 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ jmp(FieldOperand(func, JSFunction::kCodeEntryOffset));
break;
}
case kArchLazyBailout: {
EnsureSpaceForLazyDeopt();
RecordCallPosition(instr);
break;
}
case kArchPrepareCallCFunction: {
int const num_parameters = MiscField::decode(instr->opcode());
__ PrepareCallCFunction(num_parameters, i.TempRegister(0));

View File

@ -897,7 +897,7 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
}
// Select the appropriate opcode based on the call type.
InstructionCode opcode;
InstructionCode opcode = kArchNop;
switch (descriptor->kind()) {
case CallDescriptor::kCallAddress:
opcode =
@ -910,9 +910,9 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
case CallDescriptor::kCallJSFunction:
opcode = kArchCallJSFunction | MiscField::encode(flags);
break;
default:
UNREACHABLE();
return;
case CallDescriptor::kLazyBailout:
opcode = kArchLazyBailout | MiscField::encode(flags);
break;
}
// Emit the call instruction.

View File

@ -152,9 +152,6 @@
# TODO(titzer): too slow in --turbo mode due to O(n^2) graph verification.
'regress/regress-1122': [PASS, NO_VARIANTS],
# TODO(mstarzinger): try..catch and lazy deopts don't seem to work correctly.
'regress/regress-crbug-450960': [PASS, NO_VARIANTS],
# issue 4078:
'allocation-site-info': [PASS, NO_VARIANTS],

View File

@ -0,0 +1,19 @@
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flass: --allow-natives-syntax --always-opt --gc-interval=163 --stress-compaction
try { a = f();
} catch(e) {
}
var i = 0;
function f() {
try {
f();
} catch(e) {
i++;
[];
}
}
f();