Commit 3161cb55 authored by mstarzinger's avatar mstarzinger Committed by Commit bot

[turbofan] Ensure lazy bailout point in exception handler.

This ensures there is a lazy bailout point at the entry of every
exception handler so that deoptimized code is not re-entered through
caught exceptions.

R=jarin@chromium.org
TEST=cctest/test-run-deopt/DeoptExceptionHandler

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

Cr-Commit-Position: refs/heads/master@{#29061}
parent ce3b5350
......@@ -296,6 +296,7 @@ void AstNumberingVisitor::VisitWhileStatement(WhileStatement* node) {
void AstNumberingVisitor::VisitTryCatchStatement(TryCatchStatement* node) {
IncrementNodeCount();
DisableOptimization(kTryCatchStatement);
node->set_base_id(ReserveIdRange(TryCatchStatement::num_ids()));
Visit(node->try_block());
Visit(node->catch_block());
}
......@@ -304,6 +305,7 @@ void AstNumberingVisitor::VisitTryCatchStatement(TryCatchStatement* node) {
void AstNumberingVisitor::VisitTryFinallyStatement(TryFinallyStatement* node) {
IncrementNodeCount();
DisableOptimization(kTryFinallyStatement);
node->set_base_id(ReserveIdRange(TryFinallyStatement::num_ids()));
Visit(node->try_block());
Visit(node->finally_block());
}
......
......@@ -1163,12 +1163,27 @@ class TryStatement : public Statement {
public:
Block* try_block() const { return try_block_; }
void set_base_id(int id) { base_id_ = id; }
static int num_ids() { return parent_num_ids() + 1; }
BailoutId HandlerId() const { return BailoutId(local_id(0)); }
protected:
TryStatement(Zone* zone, Block* try_block, int pos)
: Statement(zone, pos), try_block_(try_block) {}
: Statement(zone, pos),
try_block_(try_block),
base_id_(BailoutId::None().ToInt()) {}
static int parent_num_ids() { return 0; }
int base_id() const {
DCHECK(!BailoutId(base_id_).IsNone());
return base_id_;
}
private:
int local_id(int n) const { return base_id() + parent_num_ids() + n; }
Block* try_block_;
int base_id_;
};
......
......@@ -1415,16 +1415,21 @@ 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());
// Clear message object as we enter the catch block.
Node* the_hole = jsgraph()->TheHoleConstant();
BuildStoreExternal(message_object, kMachAnyTagged, the_hole);
// Create a catch scope that binds the exception.
Node* exception = try_control.GetExceptionNode();
Unique<String> name = MakeUnique(stmt->variable()->name());
const Operator* op = javascript()->CreateCatchContext(name);
Node* context = NewNode(op, exception, GetFunctionClosureForContext());
// Clear message object as we enter the catch block.
Node* the_hole = jsgraph()->TheHoleConstant();
BuildStoreExternal(message_object, kMachAnyTagged, the_hole);
// Evaluate the catch-block.
VisitInScope(stmt->catch_block(), stmt->scope(), context);
try_control.EndCatch();
......@@ -1464,6 +1469,11 @@ 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());
// The result value semantics depend on how the block was entered:
// - ReturnStatement: It represents the return value being returned.
// - ThrowStatement: It represents the exception being thrown.
......
......@@ -92,6 +92,8 @@ Handle<Code> CodeGenerator::GenerateCode() {
}
// Align loop headers on 16-byte boundaries.
if (block->IsLoopHeader()) masm()->Align(16);
// Ensure lazy deopt doesn't patch handler entry points.
if (block->IsHandler()) EnsureSpaceForLazyDeopt();
// Bind a label for a block.
current_block_ = block->rpo_number();
if (FLAG_code_comments) {
......
......@@ -403,7 +403,7 @@ void PhiInstruction::SetInput(size_t offset, int virtual_register) {
InstructionBlock::InstructionBlock(Zone* zone, RpoNumber rpo_number,
RpoNumber loop_header, RpoNumber loop_end,
bool deferred)
bool deferred, bool handler)
: successors_(zone),
predecessors_(zone),
phis_(zone),
......@@ -414,6 +414,7 @@ InstructionBlock::InstructionBlock(Zone* zone, RpoNumber rpo_number,
code_start_(-1),
code_end_(-1),
deferred_(deferred),
handler_(handler),
needs_frame_(false),
must_construct_frame_(false),
must_deconstruct_frame_(false) {}
......@@ -443,9 +444,11 @@ static RpoNumber GetLoopEndRpo(const BasicBlock* block) {
static InstructionBlock* InstructionBlockFor(Zone* zone,
const BasicBlock* block) {
bool is_handler =
!block->empty() && block->front()->opcode() == IrOpcode::kIfException;
InstructionBlock* instr_block = new (zone)
InstructionBlock(zone, GetRpo(block), GetRpo(block->loop_header()),
GetLoopEndRpo(block), block->deferred());
GetLoopEndRpo(block), block->deferred(), is_handler);
// Map successors and precessors
instr_block->successors().reserve(block->SuccessorCount());
for (BasicBlock* successor : block->successors()) {
......
......@@ -930,7 +930,7 @@ class PhiInstruction final : public ZoneObject {
class InstructionBlock final : public ZoneObject {
public:
InstructionBlock(Zone* zone, RpoNumber rpo_number, RpoNumber loop_header,
RpoNumber loop_end, bool deferred);
RpoNumber loop_end, bool deferred, bool handler);
// Instruction indexes (used by the register allocator).
int first_instruction_index() const {
......@@ -953,6 +953,7 @@ class InstructionBlock final : public ZoneObject {
void set_code_end(int32_t end) { code_end_ = end; }
bool IsDeferred() const { return deferred_; }
bool IsHandler() const { return handler_; }
RpoNumber ao_number() const { return ao_number_; }
RpoNumber rpo_number() const { return rpo_number_; }
......@@ -1000,6 +1001,7 @@ class InstructionBlock final : public ZoneObject {
int32_t code_start_; // start index of arch-specific code.
int32_t code_end_; // end index of arch-specific code.
const bool deferred_; // Block contains deferred code.
const bool handler_; // Block is a handler entry point.
bool needs_frame_;
bool must_construct_frame_;
bool must_deconstruct_frame_;
......
......@@ -1226,8 +1226,9 @@ void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
Label try_entry, handler_entry, exit;
__ jmp(&try_entry);
__ bind(&handler_entry);
PrepareForBailoutForId(stmt->HandlerId(), NO_REGISTERS);
ClearPendingMessage();
// Exception handler code, the exception is in the result register.
// Extend the context before executing the catch block.
{ Comment cmnt(masm_, "[ Extend catch context");
......@@ -1295,6 +1296,8 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
// Jump to try-handler setup and try-block code.
__ jmp(&try_entry);
__ bind(&handler_entry);
PrepareForBailoutForId(stmt->HandlerId(), NO_REGISTERS);
// Exception handler code. This code is only executed when an exception
// is thrown. The exception is in the result register, and must be
// preserved by the finally block. Call the finally block and then
......
......@@ -85,7 +85,7 @@ class TestCode : public HandleAndZoneScope {
if (current_ == NULL) {
current_ = new (main_zone())
InstructionBlock(main_zone(), rpo_number_, RpoNumber::Invalid(),
RpoNumber::Invalid(), deferred);
RpoNumber::Invalid(), deferred, false);
blocks_.push_back(current_);
sequence_.StartBlock(rpo_number_);
}
......
......@@ -27,47 +27,90 @@ static void InstallIsOptimizedHelper(v8::Isolate* isolate) {
}
TEST(TurboSimpleDeopt) {
TEST(DeoptSimple) {
FLAG_allow_natives_syntax = true;
FunctionTester T(
"(function f(a) {"
"var b = 1;"
"if (!IsOptimized()) return 0;"
"%DeoptimizeFunction(f);"
"if (IsOptimized()) return 0;"
"return a + b; })");
" var b = 1;"
" if (!IsOptimized()) return 0;"
" %DeoptimizeFunction(f);"
" if (IsOptimized()) return 0;"
" return a + b;"
"})");
InstallIsOptimizedHelper(CcTest::isolate());
T.CheckCall(T.Val(2), T.Val(1));
}
TEST(TurboSimpleDeoptInExpr) {
TEST(DeoptSimpleInExpr) {
FLAG_allow_natives_syntax = 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; })");
" var b = 1;"
" var c = 2;"
" if (!IsOptimized()) return 0;"
" var d = b + (%DeoptimizeFunction(f), c);"
" if (IsOptimized()) return 0;"
" return d + a;"
"})");
InstallIsOptimizedHelper(CcTest::isolate());
T.CheckCall(T.Val(6), T.Val(3));
}
TEST(DeoptExceptionHandlerCatch) {
FLAG_allow_natives_syntax = true;
FLAG_turbo_try_catch = true;
FunctionTester T(
"(function f() {"
" var is_opt = IsOptimized;"
" try {"
" DeoptAndThrow(f);"
" } catch (e) {"
" return is_opt();"
" }"
"})");
CompileRun("function DeoptAndThrow(f) { %DeoptimizeFunction(f); throw 0; }");
InstallIsOptimizedHelper(CcTest::isolate());
T.CheckCall(T.false_value());
}
TEST(DeoptExceptionHandlerFinally) {
FLAG_allow_natives_syntax = true;
FLAG_turbo_try_finally = true;
FunctionTester T(
"(function f() {"
" var is_opt = IsOptimized;"
" try {"
" DeoptAndThrow(f);"
" } finally {"
" return is_opt();"
" }"
"})");
CompileRun("function DeoptAndThrow(f) { %DeoptimizeFunction(f); throw 0; }");
InstallIsOptimizedHelper(CcTest::isolate());
T.CheckCall(T.false_value());
}
#endif
TEST(TurboTrivialDeopt) {
TEST(DeoptTrivial) {
FLAG_allow_natives_syntax = true;
FunctionTester T(
"(function foo() {"
"%DeoptimizeFunction(foo);"
"return 1; })");
" %DeoptimizeFunction(foo);"
" return 1;"
"})");
T.CheckCall(T.Val(1));
}
......@@ -429,8 +429,8 @@ InstructionBlock* InstructionSequenceTest::NewBlock() {
}
}
// Construct instruction block.
auto instruction_block =
new (zone()) InstructionBlock(zone(), rpo, loop_header, loop_end, false);
auto instruction_block = new (zone())
InstructionBlock(zone(), rpo, loop_header, loop_end, false, false);
instruction_blocks_.push_back(instruction_block);
current_block_ = instruction_block;
sequence()->StartBlock(rpo);
......
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