Reland of [interpreter] Correctly thread through catch prediction. (patchset #1 id:1 of https://codereview.chromium.org/1695613002/ )

Reason for revert:
No fix needed, original CL was perfectly fine!

Original issue's description:
> Revert of [interpreter] Correctly thread through catch prediction. (patchset #1 id:1 of https://codereview.chromium.org/1690973002/ )
>
> Reason for revert:
> Depends on the reverted https://codereview.chromium.org/1691723002
>
> Original issue's description:
> > [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
> >
> > Committed: https://crrev.com/ba55f5594cb0b4a1a1e9b35d87fe54afe2d93f3b
> > Cr-Commit-Position: refs/heads/master@{#33906}
>
> TBR=rmcilroy@chromium.org,yangguo@chromium.org,mstarzinger@chromium.org
> # Skipping CQ checks because original CL landed less than 1 days ago.
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=v8:4674
>
> Committed: https://crrev.com/c5229b311968fd638a6cd537c341b1055eb7be97
> Cr-Commit-Position: refs/heads/master@{#33922}

TBR=rmcilroy@chromium.org,yangguo@chromium.org,adamk@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:4674

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

Cr-Commit-Position: refs/heads/master@{#33933}
This commit is contained in:
mstarzinger 2016-02-12 01:51:53 -08:00 committed by Commit bot
parent 14fa0fa831
commit 5bbcdfe680
9 changed files with 194 additions and 10 deletions

View File

@ -1629,8 +1629,10 @@ void BytecodeGraphBuilder::EnterAndExitExceptionHandlers(int current_offset) {
int next_end = table->GetRangeEnd(current_exception_handler_);
int next_handler = table->GetRangeHandler(current_exception_handler_);
int context_register = table->GetRangeData(current_exception_handler_);
CatchPrediction pred =
table->GetRangePrediction(current_exception_handler_);
exception_handlers_.push(
{next_start, next_end, next_handler, context_register});
{next_start, next_end, next_handler, context_register, pred});
current_exception_handler_++;
}
}
@ -1688,9 +1690,11 @@ Node* BytecodeGraphBuilder::MakeNode(const Operator* op, int value_input_count,
if (!result->op()->HasProperty(Operator::kNoThrow) && inside_handler) {
int handler_offset = exception_handlers_.top().handler_offset_;
int context_index = exception_handlers_.top().context_register_;
CatchPrediction prediction = exception_handlers_.top().pred_;
interpreter::Register context_register(context_index);
// TODO(mstarzinger): Thread through correct prediction!
IfExceptionHint hint = IfExceptionHint::kLocallyCaught;
IfExceptionHint hint = prediction == CatchPrediction::CAUGHT
? IfExceptionHint::kLocallyCaught
: IfExceptionHint::kLocallyUncaught;
Environment* success_env = environment()->CopyForConditional();
const Operator* op = common()->IfException(hint);
Node* effect = environment()->GetEffectDependency();

View File

@ -157,6 +157,9 @@ class BytecodeGraphBuilder {
// new nodes.
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
// entered and exited while the graph builder is iterating over the
// underlying bytecode. The exception handlers within the bytecode are
@ -166,6 +169,7 @@ class BytecodeGraphBuilder {
int end_offset_; // End offset of the handled area in the bytecode.
int handler_offset_; // Handler entry offset within the bytecode.
int context_register_; // Index of register holding handler context.
CatchPrediction pred_; // Prediction of whether handler is catching.
};
// Field accessors

View File

@ -301,7 +301,12 @@ class BytecodeGenerator::ControlScopeForTryCatch final
public:
ControlScopeForTryCatch(BytecodeGenerator* generator,
TryCatchBuilder* try_catch_builder)
: ControlScope(generator) {}
: ControlScope(generator) {
generator->try_catch_nesting_level_++;
}
virtual ~ControlScopeForTryCatch() {
generator()->try_catch_nesting_level_--;
}
protected:
bool Execute(Command command, Statement* statement) override {
@ -328,7 +333,12 @@ class BytecodeGenerator::ControlScopeForTryFinally final
DeferredCommands* commands)
: ControlScope(generator),
try_finally_builder_(try_finally_builder),
commands_(commands) {}
commands_(commands) {
generator->try_finally_nesting_level_++;
}
virtual ~ControlScopeForTryFinally() {
generator()->try_finally_nesting_level_--;
}
protected:
bool Execute(Command command, Statement* statement) override {
@ -543,7 +553,9 @@ BytecodeGenerator::BytecodeGenerator(Isolate* isolate, Zone* zone)
execution_control_(nullptr),
execution_context_(nullptr),
execution_result_(nullptr),
register_allocator_(nullptr) {
register_allocator_(nullptr),
try_catch_nesting_level_(0),
try_finally_nesting_level_(0) {
InitializeAstVisitor(isolate);
}
@ -1153,7 +1165,7 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
TryFinallyBuilder try_control_builder(builder());
TryFinallyBuilder try_control_builder(builder(), IsInsideTryCatch());
Register no_reg;
// We keep a record of all paths that enter the finally-block to be able to

View File

@ -124,6 +124,10 @@ class BytecodeGenerator final : public AstVisitor {
void RecordStoreToRegister(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 BytecodeArrayBuilder* builder() const { return builder_; }
@ -170,6 +174,8 @@ class BytecodeGenerator final : public AstVisitor {
ContextScope* execution_context_;
ExpressionResultScope* execution_result_;
RegisterAllocationScope* register_allocator_;
int try_catch_nesting_level_;
int try_finally_nesting_level_;
};
} // namespace interpreter

View File

@ -172,7 +172,7 @@ void TryFinallyBuilder::EndTry() {
void TryFinallyBuilder::BeginHandler() {
builder()->Bind(&handler_);
builder()->MarkHandler(handler_id_, false);
builder()->MarkHandler(handler_id_, will_catch_);
}

View File

@ -165,10 +165,11 @@ class TryCatchBuilder final : public ControlFlowBuilder {
// A class to help with co-ordinating control flow in try-finally statements.
class TryFinallyBuilder final : public ControlFlowBuilder {
public:
explicit TryFinallyBuilder(BytecodeArrayBuilder* builder)
explicit TryFinallyBuilder(BytecodeArrayBuilder* builder, bool will_catch)
: ControlFlowBuilder(builder),
handler_id_(builder->NewHandlerEntry()),
finalization_sites_(builder->zone()) {}
finalization_sites_(builder->zone()),
will_catch_(will_catch) {}
void BeginTry(Register context);
void LeaveTry();
@ -183,6 +184,11 @@ class TryFinallyBuilder final : public ControlFlowBuilder {
// Unbound labels that identify jumps to the finally block in the code.
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

View File

@ -3411,6 +3411,12 @@ int HandlerTable::GetRangeData(int index) const {
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) {
set(index * kRangeEntrySize + kRangeStartIndex, Smi::FromInt(value));
}

View File

@ -4844,6 +4844,9 @@ class HandlerTable : public FixedArray {
// Lookup handler in a table based on return addresses.
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.
inline int NumberOfRangeEntries() const;

View File

@ -0,0 +1,143 @@
// 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);
})();