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:
parent
14fa0fa831
commit
5bbcdfe680
@ -1629,8 +1629,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_++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1688,9 +1690,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 {
|
|||||||
// 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 {
|
|||||||
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
|
|||||||
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
|
|||||||
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)
|
|||||||
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) {
|
|||||||
|
|
||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
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() {
|
|||||||
|
|
||||||
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 {
|
|||||||
// 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 {
|
|||||||
|
|
||||||
// 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
|
||||||
|
@ -3411,6 +3411,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));
|
||||||
}
|
}
|
||||||
|
@ -4844,6 +4844,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;
|
||||||
|
|
||||||
|
143
test/mjsunit/compiler/debug-catch-prediction.js
Normal file
143
test/mjsunit/compiler/debug-catch-prediction.js
Normal 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);
|
||||||
|
})();
|
Loading…
Reference in New Issue
Block a user