Commit 77d61269 authored by mstarzinger's avatar mstarzinger Committed by Commit bot

First stab at try-catch and try-finally in TurboFan.

R=titzer@chromium.org,jarin@chromium.org
TEST=cctest/test-run-jsexceptions

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

Cr-Commit-Position: refs/heads/master@{#26398}
parent 327393b8
This diff is collapsed.
......@@ -63,8 +63,12 @@ class AstGraphBuilder : public AstVisitor {
class AstEffectContext;
class AstValueContext;
class AstTestContext;
class BreakableScope;
class ContextScope;
class ControlScope;
class ControlScopeForBreakable;
class ControlScopeForIteration;
class ControlScopeForCatch;
class ControlScopeForFinally;
class Environment;
friend class ControlBuilder;
......@@ -77,8 +81,8 @@ class AstGraphBuilder : public AstVisitor {
// List of global declarations for functions and variables.
ZoneVector<Handle<Object>> globals_;
// Stack of breakable statements entered by the visitor.
BreakableScope* breakable_;
// Stack of control scopes currently entered by the visitor.
ControlScope* execution_control_;
// Stack of context objects pushed onto the chain by the visitor.
ContextScope* execution_context_;
......@@ -110,7 +114,7 @@ class AstGraphBuilder : public AstVisitor {
Zone* local_zone() const { return local_zone_; }
Environment* environment() { return environment_; }
AstContext* ast_context() const { return ast_context_; }
BreakableScope* breakable() const { return breakable_; }
ControlScope* execution_control() const { return execution_control_; }
ContextScope* execution_context() const { return execution_context_; }
CommonOperatorBuilder* common() const { return jsgraph_->common(); }
CompilationInfo* info() const { return info_; }
......@@ -126,7 +130,7 @@ class AstGraphBuilder : public AstVisitor {
Node* exit_control() const { return exit_control_; }
void set_ast_context(AstContext* ctx) { ast_context_ = ctx; }
void set_breakable(BreakableScope* brk) { breakable_ = brk; }
void set_execution_control(ControlScope* ctrl) { execution_control_ = ctrl; }
void set_execution_context(ContextScope* ctx) { execution_context_ = ctx; }
void set_exit_control(Node* exit) { exit_control_ = exit; }
void set_current_context(Node* ctx) { current_context_ = ctx; }
......@@ -237,6 +241,10 @@ class AstGraphBuilder : public AstVisitor {
Node* BuildHoleCheckThrow(Node* value, Variable* var, Node* not_hole,
BailoutId bailout_id);
// Builders for non-local control flow.
Node* BuildReturn(Node* return_value);
Node* BuildThrow(Node* exception_value);
// Builders for binary operations.
Node* BuildBinaryOp(Node* left, Node* right, Token::Value op);
......@@ -529,42 +537,6 @@ class AstGraphBuilder::AstTestContext FINAL : public AstContext {
};
// Scoped class tracking breakable statements entered by the visitor. Allows to
// properly 'break' and 'continue' iteration statements as well as to 'break'
// from blocks within switch statements.
class AstGraphBuilder::BreakableScope BASE_EMBEDDED {
public:
BreakableScope(AstGraphBuilder* owner, BreakableStatement* target,
ControlBuilder* control, int drop_extra)
: owner_(owner),
target_(target),
next_(owner->breakable()),
control_(control),
drop_extra_(drop_extra) {
owner_->set_breakable(this); // Push.
}
~BreakableScope() {
owner_->set_breakable(next_); // Pop.
}
// Either 'break' or 'continue' the target statement.
void BreakTarget(BreakableStatement* target);
void ContinueTarget(BreakableStatement* target);
private:
AstGraphBuilder* owner_;
BreakableStatement* target_;
BreakableScope* next_;
ControlBuilder* control_;
int drop_extra_;
// Find the correct scope for the target statement. Note that this also drops
// extra operands from the environment for each scope skipped along the way.
BreakableScope* FindBreakable(BreakableStatement* target);
};
// Scoped class tracking context objects created by the visitor. Represents
// mutations of the context chain within the function body and allows to
// change the current {scope} and {context} during visitation.
......
......@@ -147,6 +147,61 @@ void BlockBuilder::EndBlock() {
break_environment_->Merge(environment());
set_environment(break_environment_);
}
void TryCatchBuilder::BeginTry() {
catch_environment_ = environment()->CopyAsUnreachable();
catch_environment_->Push(nullptr);
}
void TryCatchBuilder::Throw(Node* exception) {
environment()->Push(exception);
catch_environment_->Merge(environment());
environment()->Pop();
environment()->MarkAsUnreachable();
}
void TryCatchBuilder::EndTry() {
exit_environment_ = environment();
exception_node_ = catch_environment_->Pop();
set_environment(catch_environment_);
}
void TryCatchBuilder::EndCatch() {
exit_environment_->Merge(environment());
set_environment(exit_environment_);
}
void TryFinallyBuilder::BeginTry() {
finally_environment_ = environment()->CopyAsUnreachable();
finally_environment_->Push(nullptr);
}
void TryFinallyBuilder::LeaveTry(Node* token) {
environment()->Push(token);
finally_environment_->Merge(environment());
environment()->Pop();
}
void TryFinallyBuilder::EndTry(Node* fallthrough_token) {
environment()->Push(fallthrough_token);
finally_environment_->Merge(environment());
environment()->Pop();
token_node_ = finally_environment_->Pop();
set_environment(finally_environment_);
}
void TryFinallyBuilder::EndFinally() {
// Nothing to be done here.
}
} // namespace v8::internal::compiler
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -15,16 +15,15 @@ namespace internal {
namespace compiler {
// Base class for all control builders. Also provides a common interface for
// control builders to handle 'break' and 'continue' statements when they are
// used to model breakable statements.
// control builders to handle 'break' statements when they are used to model
// breakable statements.
class ControlBuilder {
public:
explicit ControlBuilder(AstGraphBuilder* builder) : builder_(builder) {}
virtual ~ControlBuilder() {}
// Interface for break and continue.
// Interface for break.
virtual void Break() { UNREACHABLE(); }
virtual void Continue() { UNREACHABLE(); }
protected:
typedef AstGraphBuilder Builder;
......@@ -69,11 +68,11 @@ class LoopBuilder FINAL : public ControlBuilder {
// Primitive control commands.
void BeginLoop(BitVector* assigned, bool is_osr = false);
void Continue();
void EndBody();
void EndLoop();
// Primitive support for break and continue.
void Continue() FINAL;
// Primitive support for break.
void Break() FINAL;
// Compound control commands for conditional break.
......@@ -137,6 +136,54 @@ class BlockBuilder FINAL : public ControlBuilder {
Environment* break_environment_; // Environment after the block exits.
};
// Tracks control flow for a try-catch statement.
class TryCatchBuilder FINAL : public ControlBuilder {
public:
explicit TryCatchBuilder(AstGraphBuilder* builder)
: ControlBuilder(builder),
catch_environment_(NULL),
exit_environment_(NULL),
exception_node_(NULL) {}
// Primitive control commands.
void BeginTry();
void Throw(Node* exception);
void EndTry();
void EndCatch();
// Returns the exception value inside the 'catch' body.
Node* GetExceptionNode() const { return exception_node_; }
private:
Environment* catch_environment_; // Environment for the 'catch' body.
Environment* exit_environment_; // Environment after the statement.
Node* exception_node_; // Node for exception in 'catch' body.
};
// Tracks control flow for a try-finally statement.
class TryFinallyBuilder FINAL : public ControlBuilder {
public:
explicit TryFinallyBuilder(AstGraphBuilder* builder)
: ControlBuilder(builder),
finally_environment_(NULL),
token_node_(NULL) {}
// Primitive control commands.
void BeginTry();
void LeaveTry(Node* token);
void EndTry(Node* token);
void EndFinally();
// Returns the dispatch token value inside the 'finally' body.
Node* GetDispatchTokenNode() const { return token_node_; }
private:
Environment* finally_environment_; // Environment for the 'finally' body.
Node* token_node_; // Node for token in 'finally' body.
};
} // namespace compiler
} // namespace internal
} // namespace v8
......
......@@ -77,8 +77,8 @@ class GraphBuilder {
Graph* graph_;
};
}
}
} // namespace v8::internal::compiler
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_GRAPH_BUILDER_H__
......@@ -104,7 +104,6 @@ REPLACE_COMPARE_IC_CALL(JSGreaterThanOrEqual, Token::GTE)
REPLACE_RUNTIME_CALL(JSTypeOf, Runtime::kTypeof)
REPLACE_RUNTIME_CALL(JSCreate, Runtime::kAbort)
REPLACE_RUNTIME_CALL(JSCreateFunctionContext, Runtime::kNewFunctionContext)
REPLACE_RUNTIME_CALL(JSCreateCatchContext, Runtime::kPushCatchContext)
REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext)
REPLACE_RUNTIME_CALL(JSCreateBlockContext, Runtime::kPushBlockContext)
REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext)
......@@ -364,6 +363,13 @@ void JSGenericLowering::LowerJSStoreContext(Node* node) {
}
void JSGenericLowering::LowerJSCreateCatchContext(Node* node) {
Unique<String> name = OpParameter<Unique<String>>(node);
PatchInsertInput(node, 0, jsgraph()->HeapConstant(name));
ReplaceWithRuntimeCall(node, Runtime::kPushCatchContext);
}
void JSGenericLowering::LowerJSCallConstruct(Node* node) {
int arity = OpParameter<int>(node);
CallConstructStub stub(isolate(), NO_CALL_CONSTRUCTOR_FLAGS);
......
......@@ -402,7 +402,7 @@ const Operator* JSOperatorBuilder::CreateCatchContext(
return new (zone()) Operator1<Unique<String>>( // --
IrOpcode::kJSCreateCatchContext, Operator::kNoProperties, // opcode
"JSCreateCatchContext", // name
1, 1, 1, 1, 1, 0, // counts
2, 1, 1, 1, 1, 0, // counts
name); // parameter
}
......
......@@ -416,6 +416,7 @@ DEFINE_BOOL(turbo_verify_allocation, DEBUG_BOOL,
DEFINE_BOOL(turbo_move_optimization, true, "optimize gap moves in TurboFan")
DEFINE_BOOL(turbo_jt, true, "enable jump threading in TurboFan")
DEFINE_BOOL(turbo_osr, false, "enable OSR in TurboFan")
DEFINE_BOOL(turbo_exceptions, false, "enable exception handling in TurboFan")
DEFINE_BOOL(turbo_stress_loop_peeling, false,
"stress loop peeling optimization")
......
......@@ -43,3 +43,117 @@ TEST(ThrowSourcePosition) {
CHECK_EQ(4, message->GetLineNumber());
CHECK_EQ(95, message->GetStartPosition());
}
// TODO(mstarzinger): Increase test coverage by having similar tests within the
// mjsunit suite to also test integration with other components (e.g. OSR).
TEST(Catch) {
i::FLAG_turbo_exceptions = true;
const char* src =
"(function(a,b) {"
" var r = '-';"
" try {"
" r += 'A-';"
" throw 'B-';"
" } catch (e) {"
" r += e;"
" }"
" return r;"
"})";
FunctionTester T(src);
T.CheckCall(T.Val("-A-B-"));
}
TEST(CatchNested) {
i::FLAG_turbo_exceptions = true;
const char* src =
"(function(a,b) {"
" var r = '-';"
" try {"
" r += 'A-';"
" throw 'C-';"
" } catch (e) {"
" try {"
" throw 'B-';"
" } catch (e) {"
" r += e;"
" }"
" r += e;"
" }"
" return r;"
"})";
FunctionTester T(src);
T.CheckCall(T.Val("-A-B-C-"));
}
TEST(CatchBreak) {
i::FLAG_turbo_exceptions = true;
const char* src =
"(function(a,b) {"
" var r = '-';"
" L: try {"
" r += 'A-';"
" if (a) break L;"
" r += 'B-';"
" throw 'C-';"
" } catch (e) {"
" if (b) break L;"
" r += e;"
" }"
" r += 'D-';"
" return r;"
"})";
FunctionTester T(src);
T.CheckCall(T.Val("-A-D-"), T.true_value(), T.false_value());
T.CheckCall(T.Val("-A-B-D-"), T.false_value(), T.true_value());
T.CheckCall(T.Val("-A-B-C-D-"), T.false_value(), T.false_value());
}
TEST(Finally) {
i::FLAG_turbo_exceptions = true;
const char* src =
"(function(a,b) {"
" var r = '-';"
" try {"
" r += 'A-';"
" } finally {"
" r += 'B-';"
" }"
" return r;"
"})";
FunctionTester T(src);
T.CheckCall(T.Val("-A-B-"));
}
TEST(FinallyBreak) {
i::FLAG_turbo_exceptions = true;
const char* src =
"(function(a,b) {"
" var r = '-';"
" L: try {"
" r += 'A-';"
" if (a) return r;"
" r += 'B-';"
" if (b) break L;"
" r += 'C-';"
" } finally {"
" r += 'D-';"
" }"
" return r;"
"})";
FunctionTester T(src);
T.CheckCall(T.Val("-A-"), T.true_value(), T.false_value());
T.CheckCall(T.Val("-A-B-D-"), T.false_value(), T.true_value());
T.CheckCall(T.Val("-A-B-C-D-"), T.false_value(), T.false_value());
}
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