Commit f9a9c6be authored by jarin's avatar jarin Committed by Commit bot

[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}
parent e1088b27
......@@ -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);
......
......@@ -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.
......
......@@ -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
......
......@@ -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.
......
......@@ -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.
......
......@@ -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*> {
......
......@@ -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.
......
......@@ -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));
......
......@@ -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.
......
......@@ -42,6 +42,7 @@ namespace compiler {
V(ArchTailCallJSFunction) \
V(ArchPrepareCallCFunction) \
V(ArchCallCFunction) \
V(ArchLazyBailout) \
V(ArchJmp) \
V(ArchLookupSwitch) \
V(ArchTableSwitch) \
......
......@@ -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());
......
......@@ -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) {
......
......@@ -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,
......
......@@ -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);
......
......@@ -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.
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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.
......
......@@ -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);
......
......@@ -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.
......
......@@ -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));
......
......@@ -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.
......
......@@ -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],
......
// 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();
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