Commit b7b42293 authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[turbofan] Further reduce dependence on HeapCopyReducer

- Add serialization for CallNoFeedback, which was missing.
- Extend serialization for CallJSRuntime.
- Serialize for calls to higher-order Array builtins.
- Serialize for calls to Function#apply and Function#call.
- Serialize for calls to Reflect.apply and Reflect.construct.
- Serialize for calls to Promise constructor.
- Fix ConvertReceiverMode in serialization for CallProperty.

Bug: v8:7790
Change-Id: I4bba6f45f9b7948ed2ba9c70bd423a23ec29ecf7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1763530Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Commit-Queue: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63497}
parent 6498f8bb
......@@ -2332,6 +2332,7 @@ void JSHeapBroker::InitializeRefsMap() {
DCHECK_EQ(current_zone_, broker_zone_);
current_zone_ = compiler_cache_->zone();
// Serialize various builtins.
Builtins* const b = isolate()->builtins();
{
Builtins::Name builtins[] = {
......@@ -2345,6 +2346,7 @@ void JSHeapBroker::InitializeRefsMap() {
Builtins::kCallFunction_ReceiverIsAny,
Builtins::kCallFunction_ReceiverIsNotNullOrUndefined,
Builtins::kCallFunction_ReceiverIsNullOrUndefined,
Builtins::kCompileLazy,
Builtins::kConstructFunctionForwardVarargs,
Builtins::kForInFilter,
Builtins::kJSBuiltinsConstructStub,
......@@ -2445,9 +2447,10 @@ void JSHeapBroker::InitializeAndStartSerializing(
CollectArrayAndObjectPrototypes();
SerializeTypedArrayStringTags();
// Serialize standard objects.
//
// - Maps, strings, oddballs
Factory* const f = isolate()->factory();
// Maps, strings, oddballs
GetOrCreateData(f->arguments_marker_map());
GetOrCreateData(f->bigint_string());
GetOrCreateData(f->block_context_map());
......@@ -2495,8 +2498,7 @@ void JSHeapBroker::InitializeAndStartSerializing(
GetOrCreateData(f->uninitialized_map());
GetOrCreateData(f->with_context_map());
GetOrCreateData(f->zero_string());
// Protector cells
// - Cells
GetOrCreateData(f->array_buffer_detaching_protector())
->AsPropertyCell()
->Serialize(this);
......@@ -2507,6 +2509,7 @@ void JSHeapBroker::InitializeAndStartSerializing(
GetOrCreateData(f->array_species_protector())
->AsPropertyCell()
->Serialize(this);
GetOrCreateData(f->many_closures_cell())->AsFeedbackCell();
GetOrCreateData(f->no_elements_protector())
->AsPropertyCell()
->Serialize(this);
......@@ -2520,8 +2523,7 @@ void JSHeapBroker::InitializeAndStartSerializing(
->AsPropertyCell()
->Serialize(this);
GetOrCreateData(f->string_length_protector())->AsCell()->Serialize(this);
// CEntry stub
// - CEntry stub
GetOrCreateData(
CodeFactory::CEntry(isolate(), 1, kDontSaveFPRegs, kArgvOnStack, true));
......@@ -4270,8 +4272,7 @@ bool JSHeapBroker::FeedbackIsInsufficient(FeedbackSource const& source) const {
}
namespace {
template <class MapContainer>
MapHandles GetRelevantReceiverMaps(Isolate* isolate, MapContainer const& maps) {
MapHandles GetRelevantReceiverMaps(Isolate* isolate, MapHandles const& maps) {
MapHandles result;
for (Handle<Map> map : maps) {
if (Map::TryUpdate(isolate, map).ToHandle(&map) &&
......@@ -4281,7 +4282,7 @@ MapHandles GetRelevantReceiverMaps(Isolate* isolate, MapContainer const& maps) {
}
}
return result;
}
} // namespace
} // namespace
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForPropertyAccess(
......
......@@ -28,8 +28,8 @@ Reduction JSHeapCopyReducer::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kHeapConstant: {
ObjectRef object(broker(), HeapConstantOf(node->op()));
if (object.IsJSFunction()) object.AsJSFunction().Serialize();
if (!FLAG_concurrent_inlining) {
if (object.IsJSFunction()) object.AsJSFunction().Serialize();
if (object.IsJSObject()) {
object.AsJSObject().SerializeObjectCreateMap();
}
......@@ -77,10 +77,13 @@ Reduction JSHeapCopyReducer::Reduce(Node* node) {
break;
}
case IrOpcode::kJSCreateClosure: {
CreateClosureParameters const& p = CreateClosureParametersOf(node->op());
SharedFunctionInfoRef(broker(), p.shared_info());
FeedbackCellRef(broker(), p.feedback_cell());
HeapObjectRef(broker(), p.code());
if (!FLAG_concurrent_inlining) {
CreateClosureParameters const& p =
CreateClosureParametersOf(node->op());
SharedFunctionInfoRef(broker(), p.shared_info());
FeedbackCellRef(broker(), p.feedback_cell());
HeapObjectRef(broker(), p.code());
}
break;
}
case IrOpcode::kJSCreateEmptyLiteralArray: {
......
......@@ -1554,21 +1554,21 @@ Reduction JSTypedLowering::ReduceJSConstruct(Node* node) {
if (target_type.IsHeapConstant() &&
target_type.AsHeapConstant()->Ref().IsJSFunction()) {
JSFunctionRef function = target_type.AsHeapConstant()->Ref().AsJSFunction();
SharedFunctionInfoRef shared = function.shared();
// Only optimize [[Construct]] here if {function} is a Constructor.
if (!function.map().is_constructor()) return NoChange();
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
if (!function.serialized()) {
TRACE_BROKER_MISSING(broker(), "data for function " << function);
return NoChange();
}
// Patch {node} to an indirect call via the {function}s construct stub.
bool use_builtin_construct_stub = shared.construct_as_builtin();
bool use_builtin_construct_stub = function.shared().construct_as_builtin();
CodeRef code(broker(),
use_builtin_construct_stub
? BUILTIN_CODE(isolate(), JSBuiltinsConstructStub)
: BUILTIN_CODE(isolate(), JSConstructStubGeneric));
node->RemoveInput(arity + 1);
node->InsertInput(graph()->zone(), 0, jsgraph()->Constant(code));
node->InsertInput(graph()->zone(), 2, new_target);
......@@ -1576,10 +1576,9 @@ Reduction JSTypedLowering::ReduceJSConstruct(Node* node) {
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
node->InsertInput(graph()->zone(), 5, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(
node,
common()->Call(Linkage::GetStubCallDescriptor(
graph()->zone(), ConstructStubDescriptor{}, 1 + arity, flags)));
node, common()->Call(Linkage::GetStubCallDescriptor(
graph()->zone(), ConstructStubDescriptor{}, 1 + arity,
CallDescriptor::kNeedsFrameState)));
return Changed(node);
}
......@@ -1637,12 +1636,15 @@ Reduction JSTypedLowering::ReduceJSCall(Node* node) {
if (target_type.IsHeapConstant() &&
target_type.AsHeapConstant()->Ref().IsJSFunction()) {
JSFunctionRef function = target_type.AsHeapConstant()->Ref().AsJSFunction();
SharedFunctionInfoRef shared = function.shared();
if (shared.HasBreakInfo()) {
// Do not inline the call if we need to check whether to break at entry.
if (!function.serialized()) {
TRACE_BROKER_MISSING(broker(), "data for function " << function);
return NoChange();
}
SharedFunctionInfoRef shared = function.shared();
// Do not inline the call if we need to check whether to break at entry.
if (shared.HasBreakInfo()) return NoChange();
// Class constructors are callable, but [[Call]] will raise an exception.
// See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ).
......
......@@ -91,7 +91,6 @@ namespace compiler {
V(JumpIfUndefinedOrNullConstant)
#define IGNORED_BYTECODE_LIST(V) \
V(CallNoFeedback) \
V(IncBlockCounter) \
V(StackCheck) \
V(ThrowSuperAlreadyCalledIfNotHole) \
......@@ -145,6 +144,7 @@ namespace compiler {
#define SUPPORTED_BYTECODE_LIST(V) \
V(CallAnyReceiver) \
V(CallJSRuntime) \
V(CallNoFeedback) \
V(CallProperty) \
V(CallProperty0) \
V(CallProperty1) \
......@@ -377,8 +377,9 @@ class SerializerForBackgroundCompilation {
void ProcessCallOrConstruct(Hints callee, base::Optional<Hints> new_target,
const HintsVector& arguments, FeedbackSlot slot,
bool with_spread = false);
void ProcessCallVarArgs(interpreter::BytecodeArrayIterator* iterator,
ConvertReceiverMode receiver_mode,
void ProcessCallVarArgs(ConvertReceiverMode receiver_mode,
Hints const& callee, interpreter::Register first_reg,
int reg_count, FeedbackSlot slot,
bool with_spread = false);
void ProcessApiCall(Handle<SharedFunctionInfo> target,
const HintsVector& arguments);
......@@ -1447,7 +1448,13 @@ void SerializerForBackgroundCompilation::VisitCreateClosure(
void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver(
BytecodeArrayIterator* iterator) {
ProcessCallVarArgs(iterator, ConvertReceiverMode::kNullOrUndefined);
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
ProcessCallVarArgs(ConvertReceiverMode::kNullOrUndefined, callee, first_reg,
reg_count, slot);
}
void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver0(
......@@ -1494,12 +1501,34 @@ void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver2(
void SerializerForBackgroundCompilation::VisitCallAnyReceiver(
BytecodeArrayIterator* iterator) {
ProcessCallVarArgs(iterator, ConvertReceiverMode::kAny);
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
ProcessCallVarArgs(ConvertReceiverMode::kAny, callee, first_reg, reg_count,
slot);
}
void SerializerForBackgroundCompilation::VisitCallNoFeedback(
BytecodeArrayIterator* iterator) {
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
ProcessCallVarArgs(ConvertReceiverMode::kAny, callee, first_reg, reg_count,
FeedbackSlot::Invalid());
}
void SerializerForBackgroundCompilation::VisitCallProperty(
BytecodeArrayIterator* iterator) {
ProcessCallVarArgs(iterator, ConvertReceiverMode::kNullOrUndefined);
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
ProcessCallVarArgs(ConvertReceiverMode::kNotNullOrUndefined, callee,
first_reg, reg_count, slot);
}
void SerializerForBackgroundCompilation::VisitCallProperty0(
......@@ -1546,17 +1575,28 @@ void SerializerForBackgroundCompilation::VisitCallProperty2(
void SerializerForBackgroundCompilation::VisitCallWithSpread(
BytecodeArrayIterator* iterator) {
ProcessCallVarArgs(iterator, ConvertReceiverMode::kAny, true);
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
ProcessCallVarArgs(ConvertReceiverMode::kAny, callee, first_reg, reg_count,
slot, true);
}
void SerializerForBackgroundCompilation::VisitCallJSRuntime(
BytecodeArrayIterator* iterator) {
// BytecodeGraphBuilder::VisitCallJSRuntime needs the {runtime_index}
// slot in the native context to be serialized.
const int runtime_index = iterator->GetNativeContextIndexOperand(0);
broker()->target_native_context().get(
runtime_index, SerializationPolicy::kSerializeIfNeeded);
environment()->accumulator_hints().Clear();
ObjectRef constant =
broker()
->target_native_context()
.get(runtime_index, SerializationPolicy::kSerializeIfNeeded)
.value();
Hints callee = Hints::SingleConstant(constant.object(), zone());
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
ProcessCallVarArgs(ConvertReceiverMode::kNullOrUndefined, callee, first_reg,
reg_count, FeedbackSlot::Invalid());
}
Hints SerializerForBackgroundCompilation::RunChildSerializer(
......@@ -1650,25 +1690,28 @@ MaybeHandle<JSFunction> UnrollBoundFunction(
void SerializerForBackgroundCompilation::ProcessCallOrConstruct(
Hints callee, base::Optional<Hints> new_target,
const HintsVector& arguments, FeedbackSlot slot, bool with_spread) {
FeedbackSource source(feedback_vector(), slot);
ProcessedFeedback const& feedback = broker()->ProcessFeedbackForCall(source);
if (BailoutOnUninitialized(feedback)) return;
// Incorporate feedback into hints copy to simplify processing.
SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation;
if (!feedback.IsInsufficient()) {
speculation_mode = feedback.AsCall().speculation_mode();
base::Optional<HeapObjectRef> target = feedback.AsCall().target();
if (target.has_value() && target->map().is_callable()) {
// TODO(mvstanton): if the map isn't callable then we have an allocation
// site, and it may make sense to add the Array JSFunction constant.
if (new_target.has_value()) {
// Construct; feedback is new_target, which often is also the callee.
new_target->AddConstant(target->object());
callee.AddConstant(target->object());
} else {
// Call; target is callee.
callee.AddConstant(target->object());
if (!slot.IsInvalid()) {
FeedbackSource source(feedback_vector(), slot);
ProcessedFeedback const& feedback =
broker()->ProcessFeedbackForCall(source);
if (BailoutOnUninitialized(feedback)) return;
// Incorporate feedback into hints copy to simplify processing.
if (!feedback.IsInsufficient()) {
speculation_mode = feedback.AsCall().speculation_mode();
base::Optional<HeapObjectRef> target = feedback.AsCall().target();
if (target.has_value() && target->map().is_callable()) {
// TODO(mvstanton): if the map isn't callable then we have an allocation
// site, and it may make sense to add the Array JSFunction constant.
if (new_target.has_value()) {
// Construct; feedback is new_target, which often is also the callee.
new_target->AddConstant(target->object());
callee.AddConstant(target->object());
} else {
// Call; target is callee.
callee.AddConstant(target->object());
}
}
}
}
......@@ -1717,14 +1760,9 @@ void SerializerForBackgroundCompilation::ProcessCallOrConstruct(
}
void SerializerForBackgroundCompilation::ProcessCallVarArgs(
BytecodeArrayIterator* iterator, ConvertReceiverMode receiver_mode,
ConvertReceiverMode receiver_mode, Hints const& callee,
interpreter::Register first_reg, int reg_count, FeedbackSlot slot,
bool with_spread) {
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
int reg_count = static_cast<int>(iterator->GetRegisterCountOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
HintsVector arguments(zone());
// The receiver is either given in the first register or it is implicitly
// the {undefined} value.
......@@ -1860,11 +1898,41 @@ void SerializerForBackgroundCompilation::ProcessBuiltinCall(
ProcessHintsForRegExpTest(regexp_hints);
}
break;
case Builtins::kFunctionPrototypeCall:
if (arguments.size() >= 1 &&
case Builtins::kArrayEvery:
case Builtins::kArrayFilter:
case Builtins::kArrayForEach:
case Builtins::kArrayPrototypeFind:
case Builtins::kArrayPrototypeFindIndex:
case Builtins::kArrayMap:
case Builtins::kArrayReduce:
case Builtins::kArrayReduceRight:
case Builtins::kArraySome:
if (arguments.size() >= 2 &&
speculation_mode != SpeculationMode::kDisallowSpeculation) {
Hints const& target_hints = arguments[0];
ProcessHintsForFunctionCall(target_hints);
Hints const& callback_hints = arguments[1];
ProcessHintsForFunctionCall(callback_hints);
}
break;
case Builtins::kFunctionPrototypeApply:
case Builtins::kFunctionPrototypeCall:
case Builtins::kPromiseConstructor:
// TODO(mslekova): Since the reducer for all these introduce a
// JSCall/JSConstruct that will again get optimized by the JSCallReducer,
// we basically might have to do all the serialization that we do for that
// here as well. The only difference is that the new JSCall/JSConstruct
// has speculation disabled, causing the JSCallReducer to do much less
// work. To account for that, ProcessCallOrConstruct should have a way of
// taking the speculation mode as an argument rather than getting that
// from the feedback. (Also applies to Reflect.apply and
// Reflect.construct.)
if (arguments.size() >= 1) {
ProcessHintsForFunctionCall(arguments[0]);
}
break;
case Builtins::kReflectApply:
case Builtins::kReflectConstruct:
if (arguments.size() >= 2) {
ProcessHintsForFunctionCall(arguments[1]);
}
break;
case Builtins::kObjectPrototypeIsPrototypeOf:
......@@ -2018,9 +2086,7 @@ void SerializerForBackgroundCompilation::ProcessHintsForRegExpTest(
void SerializerForBackgroundCompilation::ProcessHintsForFunctionCall(
Hints const& target_hints) {
for (auto constant : target_hints.constants()) {
if (!constant->IsJSFunction()) continue;
JSFunctionRef func(broker(), constant);
func.Serialize();
if (constant->IsJSFunction()) JSFunctionRef(broker(), constant).Serialize();
}
}
......@@ -2573,6 +2639,8 @@ void SerializerForBackgroundCompilation::VisitLdaNamedProperty(
ProcessNamedPropertyAccess(receiver, name, slot, AccessMode::kLoad);
}
// TODO(neis): Do feedback-independent serialization also for *NoFeedback
// bytecodes.
void SerializerForBackgroundCompilation::VisitLdaNamedPropertyNoFeedback(
BytecodeArrayIterator* iterator) {
NameRef(broker(),
......
......@@ -10,6 +10,7 @@
#include "src/codegen/tick-counter.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/js-heap-broker.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/linkage.h"
#include "src/compiler/loop-variable-optimizer.h"
......@@ -1526,6 +1527,10 @@ Type Typer::Visitor::JSCallTyper(Type fun, Typer* t) {
return Type::NonInternal();
}
JSFunctionRef function = fun.AsHeapConstant()->Ref().AsJSFunction();
if (!function.serialized()) {
TRACE_BROKER_MISSING(t->broker(), "data for function " << function);
return Type::NonInternal();
}
if (!function.shared().HasBuiltinId()) {
return Type::NonInternal();
}
......
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