Commit f78392d5 authored by Mike Stanton's avatar Mike Stanton Committed by Commit Bot

[Turbofan] Improve serializer environment handling for catch blocks

The serializer doesn't correctly propagate environment information
from try blocks into their catch handlers, and this impedes
optimizations that fire when we compile concurrently.

function bar(x) {
  try {
    boom(); // throws
  } catch(_) {
    return x.a;
  }
}

function foo() { return bar({a: 42}); }

When foo is optimized, we can normally return the constant 42
directly. This CL makes that work for concurrent inlining.

Bug: v8:7790
Change-Id: Id1c5fd06d51ec6fe69ab10fbd65afd6fa7e76820
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1863193Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Commit-Queue: Michael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64352}
parent ec580709
......@@ -747,12 +747,6 @@ class SerializerForBackgroundCompilation::Environment : public ZoneObject {
DCHECK(IsDead());
}
void Revive() {
DCHECK(IsDead());
ephemeral_hints_.resize(ephemeral_hints_size(), Hints());
DCHECK(!IsDead());
}
// Merge {other} into {this} environment (leaving {other} unmodified).
void Merge(Environment* other);
......@@ -1038,33 +1032,38 @@ Hints SerializerForBackgroundCompilation::Run() {
return environment()->return_value_hints();
}
class ExceptionHandlerMatcher {
class HandlerRangeMatcher {
public:
explicit ExceptionHandlerMatcher(
BytecodeArrayIterator const& bytecode_iterator,
Handle<BytecodeArray> bytecode_array)
HandlerRangeMatcher(BytecodeArrayIterator const& bytecode_iterator,
Handle<BytecodeArray> bytecode_array)
: bytecode_iterator_(bytecode_iterator) {
HandlerTable table(*bytecode_array);
for (int i = 0, n = table.NumberOfRangeEntries(); i < n; ++i) {
handlers_.insert(table.GetRangeHandler(i));
ranges_.insert(
std::make_pair(table.GetRangeStart(i), table.GetRangeHandler(i)));
}
handlers_iterator_ = handlers_.cbegin();
ranges_iterator_ = ranges_.cbegin();
}
bool CurrentBytecodeIsExceptionHandlerStart() {
int NextHandlerOffsetForRangeStart() {
CHECK(!bytecode_iterator_.done());
while (handlers_iterator_ != handlers_.cend() &&
*handlers_iterator_ < bytecode_iterator_.current_offset()) {
handlers_iterator_++;
int handler_offset = -1;
while (ranges_iterator_ != ranges_.cend() &&
ranges_iterator_->first < bytecode_iterator_.current_offset()) {
ranges_iterator_++;
}
if (ranges_iterator_ != ranges_.cend() &&
ranges_iterator_->first == bytecode_iterator_.current_offset()) {
handler_offset = ranges_iterator_->second;
ranges_iterator_++;
}
return handlers_iterator_ != handlers_.cend() &&
*handlers_iterator_ == bytecode_iterator_.current_offset();
return handler_offset;
}
private:
BytecodeArrayIterator const& bytecode_iterator_;
std::set<int> handlers_;
std::set<int>::const_iterator handlers_iterator_;
std::multimap<int, int> ranges_;
std::multimap<int, int>::const_iterator ranges_iterator_;
};
Handle<FeedbackVector> SerializerForBackgroundCompilation::feedback_vector()
......@@ -1093,7 +1092,7 @@ void SerializerForBackgroundCompilation::TraverseBytecode() {
BytecodeArrayRef(broker(), bytecode_array()).SerializeForCompilation();
BytecodeArrayIterator iterator(bytecode_array());
ExceptionHandlerMatcher handler_matcher(iterator, bytecode_array());
HandlerRangeMatcher try_start_matcher(iterator, bytecode_array());
bool has_one_shot_bytecode = false;
for (; !iterator.done(); iterator.Advance()) {
......@@ -1102,6 +1101,9 @@ void SerializerForBackgroundCompilation::TraverseBytecode() {
interpreter::Bytecodes::IsOneShotBytecode(iterator.current_bytecode());
int const current_offset = iterator.current_offset();
// TODO(mvstanton): we might want to ignore the current environment if we
// are at the start of a catch handler.
IncorporateJumpTargetEnvironment(current_offset);
TRACE_BROKER(broker(),
......@@ -1110,11 +1112,18 @@ void SerializerForBackgroundCompilation::TraverseBytecode() {
TRACE_BROKER(broker(), "Current environment: " << *environment());
if (environment()->IsDead()) {
if (handler_matcher.CurrentBytecodeIsExceptionHandlerStart()) {
environment()->Revive();
} else {
continue; // Skip this bytecode since TF won't generate code for it.
}
continue; // Skip this bytecode since TF won't generate code for it.
}
int handler_offset = try_start_matcher.NextHandlerOffsetForRangeStart();
while (handler_offset >= 0) {
// We may have nested try ranges that nonetheless start at the same
// offset. We loop here in order to save the environment for each catch
// handler.
TRACE_BROKER(broker(),
"Entered try block. Handler at offset " << handler_offset);
ContributeToJumpTargetEnvironment(handler_offset);
handler_offset = try_start_matcher.NextHandlerOffsetForRangeStart();
}
if (bytecode_analysis.IsLoopHeader(current_offset)) {
......
// Copyright 2019 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: --allow-natives-syntax
const obj = { a: 42 };
function boom() {
throw "boom";
}
// Ensure that we optimize the monomorphic case.
(function() {
function bar(x) {
try {
boom();
++i;
} catch(_) {
%TurbofanStaticAssert(x.a == 42);
return x.a;
}
}
function foo() {
return bar(obj);
}
%PrepareFunctionForOptimization(foo);
%PrepareFunctionForOptimization(bar);
foo();
foo();
%OptimizeFunctionOnNextCall(foo);
foo();
})();
// And the megamorphic case.
(function() {
function bar(x) {
try {
boom();
++i;
} catch(_) {
%TurbofanStaticAssert(x.a == 42);
return x.a;
}
}
function foo() {
return bar(obj);
}
%PrepareFunctionForOptimization(foo);
%PrepareFunctionForOptimization(bar);
bar({b: 42});
bar({c: 42});
bar({d: 42});
bar({e: 42});
bar({f: 42});
foo();
foo();
%OptimizeFunctionOnNextCall(foo);
foo();
})();
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