[turbofan] Brokerize JSInliner

Bug: v8:7790
Change-Id: I9d1853a65ad5430e5bc38727ebf9fcd6aa40c819
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1599605
Commit-Queue: Maya Lekova <mslekova@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61386}
This commit is contained in:
Maya Lekova 2019-05-09 16:58:44 +02:00 committed by Commit Bot
parent 4170e328e0
commit db7f61d694
9 changed files with 143 additions and 124 deletions

View File

@ -901,7 +901,7 @@ Reduction JSCreateLowering::ReduceJSCreateClosure(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateClosure, node->opcode());
CreateClosureParameters const& p = CreateClosureParametersOf(node->op());
SharedFunctionInfoRef shared(broker(), p.shared_info());
HeapObjectRef feedback_cell(broker(), p.feedback_cell());
FeedbackCellRef feedback_cell(broker(), p.feedback_cell());
HeapObjectRef code(broker(), p.code());
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);

View File

@ -1107,6 +1107,22 @@ class DescriptorArrayData : public HeapObjectData {
ZoneVector<PropertyDescriptor> contents_;
};
class FeedbackCellData : public HeapObjectData {
public:
FeedbackCellData(JSHeapBroker* broker, ObjectData** storage,
Handle<FeedbackCell> object);
HeapObjectData* value() const { return value_; }
private:
HeapObjectData* const value_;
};
FeedbackCellData::FeedbackCellData(JSHeapBroker* broker, ObjectData** storage,
Handle<FeedbackCell> object)
: HeapObjectData(broker, storage, object),
value_(broker->GetOrCreateData(object->value())->AsHeapObject()) {}
class FeedbackVectorData : public HeapObjectData {
public:
const ZoneVector<ObjectData*>& feedback() { return feedback_; }
@ -2663,6 +2679,8 @@ BROKER_SFI_FIELDS(DEF_SFI_ACCESSOR)
BIMODAL_ACCESSOR_C(String, int, length)
BIMODAL_ACCESSOR(FeedbackCell, HeapObject, value)
void* JSTypedArrayRef::elements_external_pointer() const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference allow_handle_dereference;

View File

@ -78,6 +78,7 @@ enum class OddballType : uint8_t {
V(Cell) \
V(Code) \
V(DescriptorArray) \
V(FeedbackCell) \
V(FeedbackVector) \
V(FixedArrayBase) \
V(FunctionTemplateInfo) \
@ -405,6 +406,14 @@ class DescriptorArrayRef : public HeapObjectRef {
Handle<DescriptorArray> object() const;
};
class FeedbackCellRef : public HeapObjectRef {
public:
using HeapObjectRef::HeapObjectRef;
Handle<FeedbackCell> object() const;
HeapObjectRef value() const;
};
class FeedbackVectorRef : public HeapObjectRef {
public:
using HeapObjectRef::HeapObjectRef;
@ -595,7 +604,8 @@ class ScopeInfoRef : public HeapObjectRef {
V(bool, construct_as_builtin) \
V(bool, HasBytecodeArray) \
V(bool, is_safe_to_skip_arguments_adaptor) \
V(bool, IsInlineable)
V(bool, IsInlineable) \
V(bool, is_compiled)
class V8_EXPORT_PRIVATE SharedFunctionInfoRef : public HeapObjectRef {
public:

View File

@ -64,7 +64,7 @@ Reduction JSHeapCopyReducer::Reduce(Node* node) {
case IrOpcode::kJSCreateClosure: {
CreateClosureParameters const& p = CreateClosureParametersOf(node->op());
SharedFunctionInfoRef(broker(), p.shared_info());
HeapObjectRef(broker(), p.feedback_cell());
FeedbackCellRef(broker(), p.feedback_cell());
HeapObjectRef(broker(), p.code());
break;
}

View File

@ -31,12 +31,13 @@ namespace {
static const int kMaxDepthForInlining = 50;
} // namespace
#define TRACE(...) \
do { \
if (FLAG_trace_turbo_inlining) PrintF(__VA_ARGS__); \
#define TRACE(x) \
do { \
if (FLAG_trace_turbo_inlining) { \
StdoutStream() << x << "\n"; \
} \
} while (false)
// Provides convenience accessors for the common layout of nodes having either
// the {JSCall} or the {JSConstruct} operator.
class JSCallAccessor {
@ -147,10 +148,9 @@ Reduction JSInliner::InlineCall(Node* call, Node* new_target, Node* context,
// Link uncaught calls in the inlinee to {exception_target}
int subcall_count = static_cast<int>(uncaught_subcalls.size());
if (subcall_count > 0) {
TRACE(
"Inlinee contains %d calls without local exception handler; "
"linking to surrounding exception handler\n",
subcall_count);
TRACE("Inlinee contains " << subcall_count
<< " calls without local exception handler; "
<< "linking to surrounding exception handler.");
}
NodeVector on_exception_nodes(local_zone_);
for (Node* subcall : uncaught_subcalls) {
@ -235,11 +235,11 @@ Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state,
int parameter_count,
BailoutId bailout_id,
FrameStateType frame_state_type,
Handle<SharedFunctionInfo> shared,
SharedFunctionInfoRef shared,
Node* context) {
const FrameStateFunctionInfo* state_info =
common()->CreateFrameStateFunctionInfo(frame_state_type,
parameter_count + 1, 0, shared);
common()->CreateFrameStateFunctionInfo(
frame_state_type, parameter_count + 1, 0, shared.object());
const Operator* op = common()->FrameState(
bailout_id, OutputFrameStateCombine::Ignore(), state_info);
@ -263,13 +263,10 @@ Node* JSInliner::CreateArtificialFrameState(Node* node, Node* outer_frame_state,
namespace {
// TODO(mstarzinger,verwaest): Move this predicate onto SharedFunctionInfo?
bool NeedsImplicitReceiver(Handle<SharedFunctionInfo> shared_info) {
bool NeedsImplicitReceiver(SharedFunctionInfoRef shared_info) {
DisallowHeapAllocation no_gc;
if (!shared_info->construct_as_builtin()) {
return !IsDerivedConstructor(shared_info->kind());
} else {
return false;
}
return !shared_info.construct_as_builtin() &&
!IsDerivedConstructor(shared_info.kind());
}
} // namespace
@ -277,8 +274,8 @@ bool NeedsImplicitReceiver(Handle<SharedFunctionInfo> shared_info) {
// Determines whether the call target of the given call {node} is statically
// known and can be used as an inlining candidate. The {SharedFunctionInfo} of
// the call target is provided (the exact closure might be unknown).
bool JSInliner::DetermineCallTarget(
Node* node, Handle<SharedFunctionInfo>& shared_info_out) {
base::Optional<SharedFunctionInfoRef> JSInliner::DetermineCallTarget(
Node* node) {
DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
HeapObjectMatcher match(node->InputAt(0));
@ -286,11 +283,9 @@ bool JSInliner::DetermineCallTarget(
// calls whenever the target is a constant function object, as follows:
// - JSCall(target:constant, receiver, args...)
// - JSConstruct(target:constant, args..., new.target)
if (match.HasValue() && match.Value()->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());
// Don't inline if the function has never run.
if (!function->has_feedback_vector()) return false;
if (match.HasValue() && match.Ref(broker()).IsJSFunction()) {
JSFunctionRef function = match.Ref(broker()).AsJSFunction();
CHECK(function.has_feedback_vector());
// Disallow cross native-context inlining for now. This means that all parts
// of the resulting code will operate on the same global object. This also
@ -300,12 +295,11 @@ bool JSInliner::DetermineCallTarget(
// TODO(turbofan): We might want to revisit this restriction later when we
// have a need for this, and we know how to model different native contexts
// in the same graph in a compositional way.
if (function->native_context() != info_->native_context()) {
return false;
if (!function.native_context().equals(broker()->native_context())) {
return base::nullopt;
}
shared_info_out = handle(function->shared(), isolate());
return true;
return function.shared();
}
// This reducer can also handle calls where the target is statically known to
@ -315,19 +309,15 @@ bool JSInliner::DetermineCallTarget(
if (match.IsJSCreateClosure()) {
CreateClosureParameters const& p = CreateClosureParametersOf(match.op());
// Disallow inlining in case the instantiation site was never run and hence
// the vector cell does not contain a valid feedback vector for the call
// target.
// TODO(turbofan): We might consider to eagerly create the feedback vector
// in such a case (in {DetermineCallContext} below) eventually.
Handle<FeedbackCell> cell = p.feedback_cell();
if (!cell->value()->IsFeedbackVector()) return false;
FeedbackCellRef cell(FeedbackCellRef(broker(), p.feedback_cell()));
if (!cell.value().IsFeedbackVector()) return base::nullopt;
shared_info_out = p.shared_info();
return true;
return SharedFunctionInfoRef(broker(), p.shared_info());
}
return false;
return base::nullopt;
}
// Determines statically known information about the call target (assuming that
@ -335,20 +325,18 @@ bool JSInliner::DetermineCallTarget(
// following static information is provided:
// - context : The context (as SSA value) bound by the call target.
// - feedback_vector : The target is guaranteed to use this feedback vector.
void JSInliner::DetermineCallContext(
Node* node, Node*& context_out,
Handle<FeedbackVector>& feedback_vector_out) {
FeedbackVectorRef JSInliner::DetermineCallContext(Node* node,
Node*& context_out) {
DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
HeapObjectMatcher match(node->InputAt(0));
if (match.HasValue() && match.Value()->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(match.Value());
CHECK(function->has_feedback_vector());
if (match.HasValue() && match.Ref(broker()).IsJSFunction()) {
JSFunctionRef function = match.Ref(broker()).AsJSFunction();
CHECK(function.has_feedback_vector());
// The inlinee specializes to the context from the JSFunction object.
context_out = jsgraph()->Constant(handle(function->context(), isolate()));
feedback_vector_out = handle(function->feedback_vector(), isolate());
return;
context_out = jsgraph()->Constant(function.context());
return function.feedback_vector();
}
if (match.IsJSCreateClosure()) {
@ -356,46 +344,34 @@ void JSInliner::DetermineCallContext(
// Load the feedback vector of the target by looking up its vector cell at
// the instantiation site (we only decide to inline if it's populated).
Handle<FeedbackCell> cell = p.feedback_cell();
DCHECK(cell->value()->IsFeedbackVector());
FeedbackCellRef cell(FeedbackCellRef(broker(), p.feedback_cell()));
// The inlinee uses the locally provided context at instantiation.
context_out = NodeProperties::GetContextInput(match.node());
feedback_vector_out =
handle(FeedbackVector::cast(cell->value()), isolate());
return;
return cell.value().AsFeedbackVector();
}
// Must succeed.
UNREACHABLE();
}
Handle<Context> JSInliner::native_context() const {
return handle(info_->native_context(), isolate());
}
Reduction JSInliner::ReduceJSCall(Node* node) {
DCHECK(IrOpcode::IsInlineeOpcode(node->opcode()));
Handle<SharedFunctionInfo> shared_info;
JSCallAccessor call(node);
// TODO(mslekova): Remove those when inlining is brokerized.
AllowHandleDereference allow_handle_deref;
AllowHandleAllocation allow_handle_alloc;
AllowHeapAllocation allow_heap_alloc;
AllowCodeDependencyChange allow_code_dep_change;
// Determine the call target.
if (!DetermineCallTarget(node, shared_info)) return NoChange();
base::Optional<SharedFunctionInfoRef> shared_info(DetermineCallTarget(node));
if (!shared_info.has_value()) return NoChange();
DCHECK(shared_info->IsInlineable());
DCHECK(shared_info.value().IsInlineable());
// Constructor must be constructable.
if (node->opcode() == IrOpcode::kJSConstruct &&
!IsConstructable(shared_info->kind())) {
TRACE("Not inlining %s into %s because constructor is not constructable.\n",
shared_info->DebugName()->ToCString().get(),
info_->shared_info()->DebugName()->ToCString().get());
TRACE(
"Not inlining " << shared_info->object().address() << " into "
<< info_->shared_info()->DebugName()->ToCString().get()
<< " because constructor is not constructable.");
return NoChange();
}
@ -403,9 +379,10 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
// See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ).
if (node->opcode() == IrOpcode::kJSCall &&
IsClassConstructor(shared_info->kind())) {
TRACE("Not inlining %s into %s because callee is a class constructor.\n",
shared_info->DebugName()->ToCString().get(),
info_->shared_info()->DebugName()->ToCString().get());
TRACE(
"Not inlining " << shared_info->object().address() << " into "
<< info_->shared_info()->DebugName()->ToCString().get()
<< " because callee is a class constructor.");
return NoChange();
}
@ -417,56 +394,54 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
frame_state = frame_state->InputAt(kFrameStateOuterStateInput)) {
nesting_level++;
if (nesting_level > kMaxDepthForInlining) {
TRACE(
"Not inlining %s into %s because call has exceeded the maximum depth "
"for function inlining\n",
shared_info->DebugName()->ToCString().get(),
info_->shared_info()->DebugName()->ToCString().get());
TRACE("Not inlining "
<< shared_info->object().address() << " into "
<< info_->shared_info()->DebugName()->ToCString().get()
<< " because call has exceeded the maximum depth for function "
"inlining.");
return NoChange();
}
}
// Calls surrounded by a local try-block are only inlined if the appropriate
// flag is active. We also discover the {IfException} projection this way.
// Calls surrounded by a local try-block are only inlined if the
// appropriate flag is active. We also discover the {IfException}
// projection this way.
Node* exception_target = nullptr;
if (NodeProperties::IsExceptionalCall(node, &exception_target) &&
!FLAG_inline_into_try) {
TRACE(
"Try block surrounds #%d:%s and --no-inline-into-try active, so not "
"inlining %s into %s.\n",
exception_target->id(), exception_target->op()->mnemonic(),
shared_info->DebugName()->ToCString().get(),
info_->shared_info()->DebugName()->ToCString().get());
TRACE("Try block surrounds #"
<< exception_target->id() << ":" << exception_target->op()->mnemonic()
<< " and --no-inline-into-try active, so not inlining "
<< shared_info->object().address() << " into "
<< info_->shared_info()->DebugName()->ToCString().get());
return NoChange();
}
IsCompiledScope is_compiled_scope(shared_info->is_compiled_scope());
// JSInliningHeuristic should have already filtered candidates without
// a BytecodeArray by calling SharedFunctionInfo::IsInlineable. For the ones
// passing the check, a reference to the bytecode was retained to make sure
// it never gets flushed, so the following check should always hold true.
CHECK(is_compiled_scope.is_compiled());
// JSInliningHeuristic has already filtered candidates without a
// BytecodeArray by calling SharedFunctionInfoRef::IsInlineable. For the ones
// passing the IsInlineable check, The broker holds a reference to the
// bytecode array, which prevents it from getting flushed.
// Therefore, the following check should always hold true.
CHECK(shared_info.value().is_compiled());
if (!FLAG_concurrent_inlining && info_->is_source_positions_enabled()) {
SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate(), shared_info);
SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate(),
shared_info->object());
}
TRACE("Inlining %s into %s%s\n", shared_info->DebugName()->ToCString().get(),
info_->shared_info()->DebugName()->ToCString().get(),
(exception_target != nullptr) ? " (inside try-block)" : "");
TRACE("Inlining " << shared_info->object().address() << " into "
<< info_->shared_info()->DebugName()->ToCString().get()
<< ((exception_target != nullptr) ? " (inside try-block)"
: ""));
// Determine the targets feedback vector and its context.
Node* context;
Handle<FeedbackVector> feedback_vector;
DetermineCallContext(node, context, feedback_vector);
FeedbackVectorRef feedback_vector = DetermineCallContext(node, context);
if (FLAG_concurrent_inlining) {
SharedFunctionInfoRef sfi(broker(), shared_info);
FeedbackVectorRef feedback(broker(), feedback_vector);
if (!sfi.IsSerializedForCompilation(feedback)) {
TRACE_BROKER(broker(), "Missed opportunity to inline a function ("
<< Brief(*sfi.object()) << " with "
<< Brief(*feedback.object()) << ")");
if (!shared_info.value().IsSerializedForCompilation(feedback_vector)) {
TRACE("Missed opportunity to inline a function ("
<< Brief(*shared_info.value().object()) << " with "
<< Brief(*feedback_vector.object()) << ")");
return NoChange();
}
}
@ -475,12 +450,12 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
// After this point, we've made a decision to inline this function.
// We shall not bailout from inlining if we got here.
Handle<BytecodeArray> bytecode_array =
handle(shared_info->GetBytecodeArray(), isolate());
BytecodeArrayRef bytecode_array = shared_info.value().GetBytecodeArray();
// Remember that we inlined this function.
int inlining_id = info_->AddInlinedFunction(
shared_info, bytecode_array, source_positions_->GetSourcePosition(node));
shared_info.value().object(), bytecode_array.object(),
source_positions_->GetSourcePosition(node));
// Create the subgraph for the inlinee.
Node* start;
@ -496,10 +471,23 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
if (info_->is_bailout_on_uninitialized()) {
flags |= BytecodeGraphBuilderFlag::kBailoutOnUninitialized;
}
BuildGraphFromBytecode(broker(), zone(), bytecode_array, shared_info,
feedback_vector, BailoutId::None(), jsgraph(),
call.frequency(), source_positions_,
native_context(), inlining_id, flags);
{
// TODO(mslekova): Remove the following once bytecode graph builder
// is brokerized. Also, remove the context argument from
// BuildGraphFromBytecode and extract it from the broker there.
AllowHandleDereference allow_handle_deref;
AllowHandleAllocation allow_handle_alloc;
AllowHeapAllocation allow_heap_alloc;
AllowCodeDependencyChange allow_code_dep_change;
Handle<Context> native_context =
handle(info_->native_context(), isolate());
BuildGraphFromBytecode(broker(), zone(), bytecode_array.object(),
shared_info.value().object(),
feedback_vector.object(), BailoutId::None(),
jsgraph(), call.frequency(), source_positions_,
native_context, inlining_id, flags);
}
// Extract the inlinee start/end nodes.
start = graph()->start();
@ -546,13 +534,13 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
// where execution continues at {construct_stub_create_deopt_pc_offset}).
Node* receiver = jsgraph()->TheHoleConstant(); // Implicit receiver.
Node* context = NodeProperties::GetContextInput(node);
if (NeedsImplicitReceiver(shared_info)) {
if (NeedsImplicitReceiver(shared_info.value())) {
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* frame_state_inside = CreateArtificialFrameState(
node, frame_state, call.formal_arguments(),
BailoutId::ConstructStubCreate(), FrameStateType::kConstructStub,
shared_info, context);
shared_info.value(), context);
Node* create =
graph()->NewNode(javascript()->Create(), call.target(), new_target,
context, frame_state_inside, effect, control);
@ -607,7 +595,7 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
frame_state = CreateArtificialFrameState(
node, frame_state, call.formal_arguments(),
BailoutId::ConstructStubInvoke(), FrameStateType::kConstructStub,
shared_info, context);
shared_info.value(), context);
}
// Insert a JSConvertReceiver node for sloppy callees. Note that the context
@ -617,8 +605,8 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
Node* effect = NodeProperties::GetEffectInput(node);
if (NodeProperties::CanBePrimitive(broker(), call.receiver(), effect)) {
CallParameters const& p = CallParametersOf(node->op());
Node* global_proxy = jsgraph()->HeapConstant(
handle(info_->native_context()->global_proxy(), isolate()));
Node* global_proxy =
jsgraph()->Constant(broker()->native_context().global_proxy_object());
Node* receiver = effect =
graph()->NewNode(simplified()->ConvertReceiver(p.convert_mode()),
call.receiver(), global_proxy, effect, start);
@ -636,7 +624,7 @@ Reduction JSInliner::ReduceJSCall(Node* node) {
if (call.formal_arguments() != parameter_count) {
frame_state = CreateArtificialFrameState(
node, frame_state, call.formal_arguments(), BailoutId::None(),
FrameStateType::kArgumentsAdaptor, shared_info);
FrameStateType::kArgumentsAdaptor, shared_info.value());
}
return InlineCall(node, new_target, context, frame_state, start, end,

View File

@ -51,7 +51,6 @@ class JSInliner final : public AdvancedReducer {
// TODO(neis): Make heap broker a component of JSGraph?
JSHeapBroker* broker() const { return broker_; }
Isolate* isolate() const { return jsgraph_->isolate(); }
Handle<Context> native_context() const;
Zone* const local_zone_;
OptimizedCompilationInfo* info_;
@ -59,15 +58,13 @@ class JSInliner final : public AdvancedReducer {
JSHeapBroker* const broker_;
SourcePositionTable* const source_positions_;
bool DetermineCallTarget(Node* node,
Handle<SharedFunctionInfo>& shared_info_out);
void DetermineCallContext(Node* node, Node*& context_out,
Handle<FeedbackVector>& feedback_vector_out);
base::Optional<SharedFunctionInfoRef> DetermineCallTarget(Node* node);
FeedbackVectorRef DetermineCallContext(Node* node, Node*& context_out);
Node* CreateArtificialFrameState(Node* node, Node* outer_frame_state,
int parameter_count, BailoutId bailout_id,
FrameStateType frame_state_type,
Handle<SharedFunctionInfo> shared,
SharedFunctionInfoRef shared,
Node* context = nullptr);
Reduction InlineCall(Node* call, Node* new_target, Node* context,

View File

@ -65,14 +65,19 @@ bool MapInference::AllOfInstanceTypes(std::function<bool(InstanceType)> f) {
bool MapInference::AllOfInstanceTypesUnsafe(
std::function<bool(InstanceType)> f) const {
// TODO(neis): Brokerize the MapInference.
AllowHandleDereference allow_handle_deref;
CHECK(HaveMaps());
return std::all_of(maps_.begin(), maps_.end(),
[f](Handle<Map> map) { return f(map->instance_type()); });
}
bool MapInference::AnyOfInstanceTypesUnsafe(
std::function<bool(InstanceType)> f) const {
AllowHandleDereference allow_handle_deref;
CHECK(HaveMaps());
return std::any_of(maps_.begin(), maps_.end(),
[f](Handle<Map> map) { return f(map->instance_type()); });
}

View File

@ -438,7 +438,9 @@ void SerializerForBackgroundCompilation::VisitCreateClosure(
Handle<FeedbackCell> feedback_cell =
environment()->function().feedback_vector->GetClosureFeedbackCell(
iterator->GetIndexOperand(1));
FeedbackCellRef feedback_cell_ref(broker(), feedback_cell);
Handle<Object> cell_value(feedback_cell->value(), broker()->isolate());
ObjectRef cell_value_ref(broker(), cell_value);
environment()->accumulator_hints().Clear();
if (cell_value->IsFeedbackVector()) {

View File

@ -239,7 +239,6 @@ OptimizedCompilationInfo::InlinedFunctionHolder::InlinedFunctionHolder(
Handle<SharedFunctionInfo> inlined_shared_info,
Handle<BytecodeArray> inlined_bytecode, SourcePosition pos)
: shared_info(inlined_shared_info), bytecode_array(inlined_bytecode) {
DCHECK_EQ(shared_info->GetBytecodeArray(), *bytecode_array);
position.position = pos;
// initialized when generating the deoptimization literals
position.inlined_function_id = DeoptimizationData::kNotInlinedIndex;