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