Commit ba55f559 authored by mstarzinger's avatar mstarzinger Committed by Commit bot

[interpreter] Correctly thread through catch prediction.

This change correctly sets the {CatchPrediction} field in exception
handler tables for bytecode and optimized code. It also adds tests
independent of promise handling for this prediction, to ensure all our
backends are in sync on their prediction.

R=rmcilroy@chromium.org,yangguo@chromium.org
TEST=mjsunit/compiler/debug-catch-prediction
BUG=v8:4674
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#33906}
parent 82434b1b
...@@ -1637,8 +1637,10 @@ void BytecodeGraphBuilder::EnterAndExitExceptionHandlers(int current_offset) { ...@@ -1637,8 +1637,10 @@ void BytecodeGraphBuilder::EnterAndExitExceptionHandlers(int current_offset) {
int next_end = table->GetRangeEnd(current_exception_handler_); int next_end = table->GetRangeEnd(current_exception_handler_);
int next_handler = table->GetRangeHandler(current_exception_handler_); int next_handler = table->GetRangeHandler(current_exception_handler_);
int context_register = table->GetRangeData(current_exception_handler_); int context_register = table->GetRangeData(current_exception_handler_);
CatchPrediction pred =
table->GetRangePrediction(current_exception_handler_);
exception_handlers_.push( exception_handlers_.push(
{next_start, next_end, next_handler, context_register}); {next_start, next_end, next_handler, context_register, pred});
current_exception_handler_++; current_exception_handler_++;
} }
} }
...@@ -1696,9 +1698,11 @@ Node* BytecodeGraphBuilder::MakeNode(const Operator* op, int value_input_count, ...@@ -1696,9 +1698,11 @@ Node* BytecodeGraphBuilder::MakeNode(const Operator* op, int value_input_count,
if (!result->op()->HasProperty(Operator::kNoThrow) && inside_handler) { if (!result->op()->HasProperty(Operator::kNoThrow) && inside_handler) {
int handler_offset = exception_handlers_.top().handler_offset_; int handler_offset = exception_handlers_.top().handler_offset_;
int context_index = exception_handlers_.top().context_register_; int context_index = exception_handlers_.top().context_register_;
CatchPrediction prediction = exception_handlers_.top().pred_;
interpreter::Register context_register(context_index); interpreter::Register context_register(context_index);
// TODO(mstarzinger): Thread through correct prediction! IfExceptionHint hint = prediction == CatchPrediction::CAUGHT
IfExceptionHint hint = IfExceptionHint::kLocallyCaught; ? IfExceptionHint::kLocallyCaught
: IfExceptionHint::kLocallyUncaught;
Environment* success_env = environment()->CopyForConditional(); Environment* success_env = environment()->CopyForConditional();
const Operator* op = common()->IfException(hint); const Operator* op = common()->IfException(hint);
Node* effect = environment()->GetEffectDependency(); Node* effect = environment()->GetEffectDependency();
......
...@@ -157,6 +157,9 @@ class BytecodeGraphBuilder { ...@@ -157,6 +157,9 @@ class BytecodeGraphBuilder {
// new nodes. // new nodes.
static const int kInputBufferSizeIncrement = 64; static const int kInputBufferSizeIncrement = 64;
// The catch prediction from the handler table is reused.
typedef HandlerTable::CatchPrediction CatchPrediction;
// An abstract representation for an exception handler that is being // An abstract representation for an exception handler that is being
// entered and exited while the graph builder is iterating over the // entered and exited while the graph builder is iterating over the
// underlying bytecode. The exception handlers within the bytecode are // underlying bytecode. The exception handlers within the bytecode are
...@@ -166,6 +169,7 @@ class BytecodeGraphBuilder { ...@@ -166,6 +169,7 @@ class BytecodeGraphBuilder {
int end_offset_; // End offset of the handled area in the bytecode. int end_offset_; // End offset of the handled area in the bytecode.
int handler_offset_; // Handler entry offset within the bytecode. int handler_offset_; // Handler entry offset within the bytecode.
int context_register_; // Index of register holding handler context. int context_register_; // Index of register holding handler context.
CatchPrediction pred_; // Prediction of whether handler is catching.
}; };
// Field accessors // Field accessors
......
...@@ -301,7 +301,12 @@ class BytecodeGenerator::ControlScopeForTryCatch final ...@@ -301,7 +301,12 @@ class BytecodeGenerator::ControlScopeForTryCatch final
public: public:
ControlScopeForTryCatch(BytecodeGenerator* generator, ControlScopeForTryCatch(BytecodeGenerator* generator,
TryCatchBuilder* try_catch_builder) TryCatchBuilder* try_catch_builder)
: ControlScope(generator) {} : ControlScope(generator) {
generator->try_catch_nesting_level_++;
}
virtual ~ControlScopeForTryCatch() {
generator()->try_catch_nesting_level_--;
}
protected: protected:
bool Execute(Command command, Statement* statement) override { bool Execute(Command command, Statement* statement) override {
...@@ -328,7 +333,12 @@ class BytecodeGenerator::ControlScopeForTryFinally final ...@@ -328,7 +333,12 @@ class BytecodeGenerator::ControlScopeForTryFinally final
DeferredCommands* commands) DeferredCommands* commands)
: ControlScope(generator), : ControlScope(generator),
try_finally_builder_(try_finally_builder), try_finally_builder_(try_finally_builder),
commands_(commands) {} commands_(commands) {
generator->try_finally_nesting_level_++;
}
virtual ~ControlScopeForTryFinally() {
generator()->try_finally_nesting_level_--;
}
protected: protected:
bool Execute(Command command, Statement* statement) override { bool Execute(Command command, Statement* statement) override {
...@@ -543,7 +553,9 @@ BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone) ...@@ -543,7 +553,9 @@ BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone)
execution_control_(nullptr), execution_control_(nullptr),
execution_context_(nullptr), execution_context_(nullptr),
execution_result_(nullptr), execution_result_(nullptr),
register_allocator_(nullptr) { register_allocator_(nullptr),
try_catch_nesting_level_(0),
try_finally_nesting_level_(0) {
InitializeAstVisitor(isolate); InitializeAstVisitor(isolate);
} }
...@@ -1153,7 +1165,7 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { ...@@ -1153,7 +1165,7 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
TryFinallyBuilder try_control_builder(builder()); TryFinallyBuilder try_control_builder(builder(), IsInsideTryCatch());
Register no_reg; Register no_reg;
// We keep a record of all paths that enter the finally-block to be able to // We keep a record of all paths that enter the finally-block to be able to
......
...@@ -124,6 +124,10 @@ class BytecodeGenerator final : public AstVisitor { ...@@ -124,6 +124,10 @@ class BytecodeGenerator final : public AstVisitor {
void RecordStoreToRegister(Register reg); void RecordStoreToRegister(Register reg);
Register LoadFromAliasedRegister(Register reg); Register LoadFromAliasedRegister(Register reg);
// Methods for tracking try-block nesting.
bool IsInsideTryCatch() const { return try_catch_nesting_level_ > 0; }
bool IsInsideTryFinally() const { return try_finally_nesting_level_ > 0; }
inline void set_builder(BytecodeArrayBuilder* builder) { builder_ = builder; } inline void set_builder(BytecodeArrayBuilder* builder) { builder_ = builder; }
inline BytecodeArrayBuilder* builder() const { return builder_; } inline BytecodeArrayBuilder* builder() const { return builder_; }
...@@ -170,6 +174,8 @@ class BytecodeGenerator final : public AstVisitor { ...@@ -170,6 +174,8 @@ class BytecodeGenerator final : public AstVisitor {
ContextScope* execution_context_; ContextScope* execution_context_;
ExpressionResultScope* execution_result_; ExpressionResultScope* execution_result_;
RegisterAllocationScope* register_allocator_; RegisterAllocationScope* register_allocator_;
int try_catch_nesting_level_;
int try_finally_nesting_level_;
}; };
} // namespace interpreter } // namespace interpreter
......
...@@ -172,7 +172,7 @@ void TryFinallyBuilder::EndTry() { ...@@ -172,7 +172,7 @@ void TryFinallyBuilder::EndTry() {
void TryFinallyBuilder::BeginHandler() { void TryFinallyBuilder::BeginHandler() {
builder()->Bind(&handler_); builder()->Bind(&handler_);
builder()->MarkHandler(handler_id_, false); builder()->MarkHandler(handler_id_, will_catch_);
} }
......
...@@ -165,10 +165,11 @@ class TryCatchBuilder final : public ControlFlowBuilder { ...@@ -165,10 +165,11 @@ class TryCatchBuilder final : public ControlFlowBuilder {
// A class to help with co-ordinating control flow in try-finally statements. // A class to help with co-ordinating control flow in try-finally statements.
class TryFinallyBuilder final : public ControlFlowBuilder { class TryFinallyBuilder final : public ControlFlowBuilder {
public: public:
explicit TryFinallyBuilder(BytecodeArrayBuilder* builder) explicit TryFinallyBuilder(BytecodeArrayBuilder* builder, bool will_catch)
: ControlFlowBuilder(builder), : ControlFlowBuilder(builder),
handler_id_(builder->NewHandlerEntry()), handler_id_(builder->NewHandlerEntry()),
finalization_sites_(builder->zone()) {} finalization_sites_(builder->zone()),
will_catch_(will_catch) {}
void BeginTry(Register context); void BeginTry(Register context);
void LeaveTry(); void LeaveTry();
...@@ -183,6 +184,11 @@ class TryFinallyBuilder final : public ControlFlowBuilder { ...@@ -183,6 +184,11 @@ class TryFinallyBuilder final : public ControlFlowBuilder {
// Unbound labels that identify jumps to the finally block in the code. // Unbound labels that identify jumps to the finally block in the code.
ZoneVector<BytecodeLabel> finalization_sites_; ZoneVector<BytecodeLabel> finalization_sites_;
// Conservative prediction of whether exceptions thrown into the handler for
// this finally block will be caught. Note that such a prediction depends on
// whether this try-finally is nested inside a surrounding try-catch.
bool will_catch_;
}; };
} // namespace interpreter } // namespace interpreter
......
...@@ -3410,6 +3410,12 @@ int HandlerTable::GetRangeData(int index) const { ...@@ -3410,6 +3410,12 @@ int HandlerTable::GetRangeData(int index) const {
return Smi::cast(get(index * kRangeEntrySize + kRangeDataIndex))->value(); return Smi::cast(get(index * kRangeEntrySize + kRangeDataIndex))->value();
} }
HandlerTable::CatchPrediction HandlerTable::GetRangePrediction(
int index) const {
return HandlerPredictionField::decode(
Smi::cast(get(index * kRangeEntrySize + kRangeHandlerIndex))->value());
}
void HandlerTable::SetRangeStart(int index, int value) { void HandlerTable::SetRangeStart(int index, int value) {
set(index * kRangeEntrySize + kRangeStartIndex, Smi::FromInt(value)); set(index * kRangeEntrySize + kRangeStartIndex, Smi::FromInt(value));
} }
......
...@@ -4803,6 +4803,9 @@ class HandlerTable : public FixedArray { ...@@ -4803,6 +4803,9 @@ class HandlerTable : public FixedArray {
// Lookup handler in a table based on return addresses. // Lookup handler in a table based on return addresses.
int LookupReturn(int pc_offset, CatchPrediction* prediction); int LookupReturn(int pc_offset, CatchPrediction* prediction);
// Returns the conservative catch predication.
inline CatchPrediction GetRangePrediction(int index) const;
// Returns the number of entries in the table. // Returns the number of entries in the table.
inline int NumberOfRangeEntries() const; inline int NumberOfRangeEntries() const;
......
// Copyright 2016 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.
// Flags: --expose-debug-as debug --allow-natives-syntax
// Test debug event catch prediction for thrown exceptions. We distinguish
// between "caught" and "uncaught" based on the following assumptions:
// 1) try-catch : Will always catch the exception.
// 2) try-finally : Will always re-throw the exception.
Debug = debug.Debug;
var log = [];
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Exception) {
log.push([event_data.exception(), event_data.uncaught()]);
}
} catch (e) {
%AbortJS(e + "\n" + e.stack);
}
}
Debug.setBreakOnException();
Debug.setListener(listener);
(function TryCatch() {
log = []; // Clear log.
function f(a) {
try {
throw "boom" + a;
} catch(e) {
return e;
}
}
assertEquals("boom1", f(1));
assertEquals("boom2", f(2));
%OptimizeFunctionOnNextCall(f);
assertEquals("boom3", f(3));
print("Collect log:", log);
assertEquals([["boom1",false], ["boom2",false], ["boom3",false]], log);
})();
(function TryFinally() {
log = []; // Clear log.
function f(a) {
try {
throw "baem" + a;
} finally {
return a + 10;
}
}
assertEquals(11, f(1));
assertEquals(12, f(2));
%OptimizeFunctionOnNextCall(f);
assertEquals(13, f(3));
print("Collect log:", log);
assertEquals([["baem1",true], ["baem2",true], ["baem3",true]], log);
})();
(function TryCatchFinally() {
log = []; // Clear log.
function f(a) {
try {
throw "wosh" + a;
} catch(e) {
return e + a;
} finally {
// Nothing.
}
}
assertEquals("wosh11", f(1));
assertEquals("wosh22", f(2));
%OptimizeFunctionOnNextCall(f);
assertEquals("wosh33", f(3));
print("Collect log:", log);
assertEquals([["wosh1",false], ["wosh2",false], ["wosh3",false]], log);
})();
(function TryCatchNestedFinally() {
log = []; // Clear log.
function f(a) {
try {
try {
throw "bang" + a;
} finally {
// Nothing.
}
} catch(e) {
return e + a;
}
}
assertEquals("bang11", f(1));
assertEquals("bang22", f(2));
%OptimizeFunctionOnNextCall(f);
assertEquals("bang33", f(3));
print("Collect log:", log);
assertEquals([["bang1",false], ["bang2",false], ["bang3",false]], log);
})();
(function TryFinallyNestedCatch() {
log = []; // Clear log.
function f(a) {
try {
try {
throw "peng" + a;
} catch(e) {
return e
}
} finally {
return a + 10;
}
}
assertEquals(11, f(1));
assertEquals(12, f(2));
%OptimizeFunctionOnNextCall(f);
assertEquals(13, f(3));
print("Collect log:", log);
assertEquals([["peng1",false], ["peng2",false], ["peng3",false]], log);
})();
(function TryFinallyNestedFinally() {
log = []; // Clear log.
function f(a) {
try {
try {
throw "oops" + a;
} finally {
// Nothing.
}
} finally {
return a + 10;
}
}
assertEquals(11, f(1));
assertEquals(12, f(2));
%OptimizeFunctionOnNextCall(f);
assertEquals(13, f(3));
print("Collect log:", log);
assertEquals([["oops1",true], ["oops2",true], ["oops3",true]], log);
})();
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