void AstNumberingVisitor::VisitTryCatchStatement(TryCatchStatement* node) {
IncrementNodeCount();
DisableOptimization(kTryCatchStatement);
+ node->set_base_id(ReserveIdRange(TryCatchStatement::num_ids()));
Visit(node->try_block());
Visit(node->catch_block());
}
void AstNumberingVisitor::VisitTryFinallyStatement(TryFinallyStatement* node) {
IncrementNodeCount();
DisableOptimization(kTryFinallyStatement);
+ node->set_base_id(ReserveIdRange(TryFinallyStatement::num_ids()));
Visit(node->try_block());
Visit(node->finally_block());
}
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_;
};
}
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();
}
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.
}
// 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) {
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),
code_start_(-1),
code_end_(-1),
deferred_(deferred),
+ handler_(handler),
needs_frame_(false),
must_construct_frame_(false),
must_deconstruct_frame_(false) {}
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()) {
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 {
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_; }
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_;
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");
// 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
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_);
}
}
-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));
}
}
}
// 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);