blob: 2a74d7932df727168fbb7352a1b0f2b56358e61b [file] [log] [blame]
// Copyright 2018 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.
#include "src/compiler/serializer-for-background-compilation.h"
#include <sstream>
#include "src/compiler/js-heap-broker.h"
#include "src/handles-inl.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/objects/code.h"
#include "src/objects/shared-function-info-inl.h"
#include "src/vector-slot-pair.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
namespace compiler {
using BytecodeArrayIterator = interpreter::BytecodeArrayIterator;
CompilationSubject::CompilationSubject(Handle<JSFunction> closure,
Isolate* isolate)
: blueprint_{handle(closure->shared(), isolate),
handle(closure->feedback_vector(), isolate)},
closure_(closure) {
CHECK(closure->has_feedback_vector());
}
Hints::Hints(Zone* zone)
: constants_(zone), maps_(zone), function_blueprints_(zone) {}
const ConstantsSet& Hints::constants() const { return constants_; }
const MapsSet& Hints::maps() const { return maps_; }
const BlueprintsSet& Hints::function_blueprints() const {
return function_blueprints_;
}
void Hints::AddConstant(Handle<Object> constant) {
constants_.insert(constant);
}
void Hints::AddMap(Handle<Map> map) { maps_.insert(map); }
void Hints::AddFunctionBlueprint(FunctionBlueprint function_blueprint) {
function_blueprints_.insert(function_blueprint);
}
void Hints::Add(const Hints& other) {
for (auto x : other.constants()) AddConstant(x);
for (auto x : other.maps()) AddMap(x);
for (auto x : other.function_blueprints()) AddFunctionBlueprint(x);
}
bool Hints::IsEmpty() const {
return constants().empty() && maps().empty() && function_blueprints().empty();
}
std::ostream& operator<<(std::ostream& out,
const FunctionBlueprint& blueprint) {
out << Brief(*blueprint.shared) << std::endl;
out << Brief(*blueprint.feedback_vector) << std::endl;
return out;
}
std::ostream& operator<<(std::ostream& out, const Hints& hints) {
!hints.constants().empty() &&
out << "\t\tConstants (" << hints.constants().size() << "):" << std::endl;
for (auto x : hints.constants()) out << Brief(*x) << std::endl;
!hints.maps().empty() && out << "\t\tMaps (" << hints.maps().size()
<< "):" << std::endl;
for (auto x : hints.maps()) out << Brief(*x) << std::endl;
!hints.function_blueprints().empty() &&
out << "\t\tBlueprints (" << hints.function_blueprints().size()
<< "):" << std::endl;
for (auto x : hints.function_blueprints()) out << x;
return out;
}
void Hints::Clear() {
constants_.clear();
maps_.clear();
function_blueprints_.clear();
DCHECK(IsEmpty());
}
class SerializerForBackgroundCompilation::Environment : public ZoneObject {
public:
Environment(Zone* zone, CompilationSubject function);
Environment(Zone* zone, Isolate* isolate, CompilationSubject function,
base::Optional<Hints> new_target, const HintsVector& arguments);
// When control flow bytecodes are encountered, e.g. a conditional jump,
// the current environment needs to be stashed together with the target jump
// address. Later, when this target bytecode is handled, the stashed
// environment will be merged into the current one.
void Merge(Environment* other);
friend std::ostream& operator<<(std::ostream& out, const Environment& env);
FunctionBlueprint function() const { return function_; }
Hints& accumulator_hints() { return environment_hints_[accumulator_index()]; }
Hints& register_hints(interpreter::Register reg) {
int local_index = RegisterToLocalIndex(reg);
DCHECK_LT(local_index, environment_hints_.size());
return environment_hints_[local_index];
}
Hints& return_value_hints() { return return_value_hints_; }
// Clears all hints except those for the return value and the closure.
void ClearEphemeralHints() {
DCHECK_EQ(environment_hints_.size(), function_closure_index() + 1);
for (int i = 0; i < function_closure_index(); ++i) {
environment_hints_[i].Clear();
}
}
// Appends the hints for the given register range to {dst} (in order).
void ExportRegisterHints(interpreter::Register first, size_t count,
HintsVector& dst);
private:
int RegisterToLocalIndex(interpreter::Register reg) const;
Zone* zone() const { return zone_; }
int parameter_count() const { return parameter_count_; }
int register_count() const { return register_count_; }
Zone* const zone_;
// Instead of storing the blueprint here, we could extract it from the
// (closure) hints but that would be cumbersome.
FunctionBlueprint const function_;
int const parameter_count_;
int const register_count_;
// environment_hints_ contains hints for the contents of the registers,
// the accumulator and the parameters. The layout is as follows:
// [ parameters | registers | accumulator | context | closure ]
// The first parameter is the receiver.
HintsVector environment_hints_;
int accumulator_index() const { return parameter_count() + register_count(); }
int current_context_index() const { return accumulator_index() + 1; }
int function_closure_index() const { return current_context_index() + 1; }
int environment_hints_size() const { return function_closure_index() + 1; }
Hints return_value_hints_;
};
SerializerForBackgroundCompilation::Environment::Environment(
Zone* zone, CompilationSubject function)
: zone_(zone),
function_(function.blueprint()),
parameter_count_(function_.shared->GetBytecodeArray()->parameter_count()),
register_count_(function_.shared->GetBytecodeArray()->register_count()),
environment_hints_(environment_hints_size(), Hints(zone), zone),
return_value_hints_(zone) {
Handle<JSFunction> closure;
if (function.closure().ToHandle(&closure)) {
environment_hints_[function_closure_index()].AddConstant(closure);
} else {
environment_hints_[function_closure_index()].AddFunctionBlueprint(
function.blueprint());
}
}
SerializerForBackgroundCompilation::Environment::Environment(
Zone* zone, Isolate* isolate, CompilationSubject function,
base::Optional<Hints> new_target, const HintsVector& arguments)
: Environment(zone, function) {
// Copy the hints for the actually passed arguments, at most up to
// the parameter_count.
size_t param_count = static_cast<size_t>(parameter_count());
for (size_t i = 0; i < std::min(arguments.size(), param_count); ++i) {
environment_hints_[i] = arguments[i];
}
// Pad the rest with "undefined".
Hints undefined_hint(zone);
undefined_hint.AddConstant(isolate->factory()->undefined_value());
for (size_t i = arguments.size(); i < param_count; ++i) {
environment_hints_[i] = undefined_hint;
}
interpreter::Register new_target_reg =
function_.shared->GetBytecodeArray()
->incoming_new_target_or_generator_register();
if (new_target_reg.is_valid()) {
DCHECK(register_hints(new_target_reg).IsEmpty());
if (new_target.has_value()) {
register_hints(new_target_reg).Add(*new_target);
}
}
}
void SerializerForBackgroundCompilation::Environment::Merge(
Environment* other) {
// Presumably the source and the target would have the same layout
// so this is enforced here.
CHECK_EQ(parameter_count(), other->parameter_count());
CHECK_EQ(register_count(), other->register_count());
CHECK_EQ(environment_hints_size(), other->environment_hints_size());
for (size_t i = 0; i < environment_hints_.size(); ++i) {
environment_hints_[i].Add(other->environment_hints_[i]);
}
return_value_hints_.Add(other->return_value_hints_);
}
std::ostream& operator<<(
std::ostream& out,
const SerializerForBackgroundCompilation::Environment& env) {
std::ostringstream output_stream;
output_stream << "Function ";
env.function_.shared->Name()->Print(output_stream);
output_stream << "Parameter count: " << env.parameter_count() << std::endl;
output_stream << "Register count: " << env.register_count() << std::endl;
output_stream << "Hints (" << env.environment_hints_.size() << "):\n";
for (size_t i = 0; i < env.environment_hints_.size(); ++i) {
if (env.environment_hints_[i].IsEmpty()) continue;
output_stream << "\tSlot " << i << std::endl;
output_stream << env.environment_hints_[i];
}
output_stream << "Return value:\n";
output_stream << env.return_value_hints_
<< "===========================================\n";
out << output_stream.str();
return out;
}
int SerializerForBackgroundCompilation::Environment::RegisterToLocalIndex(
interpreter::Register reg) const {
// TODO(mslekova): We also want to gather hints for the context.
if (reg.is_current_context()) return current_context_index();
if (reg.is_function_closure()) return function_closure_index();
if (reg.is_parameter()) {
return reg.ToParameterIndex(parameter_count());
} else {
return parameter_count() + reg.index();
}
}
SerializerForBackgroundCompilation::SerializerForBackgroundCompilation(
JSHeapBroker* broker, Zone* zone, Handle<JSFunction> closure)
: broker_(broker),
zone_(zone),
environment_(new (zone) Environment(zone, {closure, broker_->isolate()})),
stashed_environments_(zone) {
JSFunctionRef(broker, closure).Serialize();
}
SerializerForBackgroundCompilation::SerializerForBackgroundCompilation(
JSHeapBroker* broker, Zone* zone, CompilationSubject function,
base::Optional<Hints> new_target, const HintsVector& arguments)
: broker_(broker),
zone_(zone),
environment_(new (zone) Environment(zone, broker_->isolate(), function,
new_target, arguments)),
stashed_environments_(zone) {
Handle<JSFunction> closure;
if (function.closure().ToHandle(&closure)) {
JSFunctionRef(broker, closure).Serialize();
}
}
Hints SerializerForBackgroundCompilation::Run() {
SharedFunctionInfoRef shared(broker(), environment()->function().shared);
FeedbackVectorRef feedback_vector(broker(),
environment()->function().feedback_vector);
if (shared.IsSerializedForCompilation(feedback_vector)) {
return Hints(zone());
}
shared.SetSerializedForCompilation(feedback_vector);
feedback_vector.SerializeSlots();
TraverseBytecode();
return environment()->return_value_hints();
}
void SerializerForBackgroundCompilation::TraverseBytecode() {
BytecodeArrayRef bytecode_array(
broker(), handle(environment()->function().shared->GetBytecodeArray(),
broker()->isolate()));
BytecodeArrayIterator iterator(bytecode_array.object());
for (; !iterator.done(); iterator.Advance()) {
MergeAfterJump(&iterator);
switch (iterator.current_bytecode()) {
#define DEFINE_BYTECODE_CASE(name) \
case interpreter::Bytecode::k##name: \
Visit##name(&iterator); \
break;
SUPPORTED_BYTECODE_LIST(DEFINE_BYTECODE_CASE)
#undef DEFINE_BYTECODE_CASE
default: {
environment()->ClearEphemeralHints();
break;
}
}
}
}
void SerializerForBackgroundCompilation::VisitIllegal(
BytecodeArrayIterator* iterator) {
UNREACHABLE();
}
void SerializerForBackgroundCompilation::VisitWide(
BytecodeArrayIterator* iterator) {
UNREACHABLE();
}
void SerializerForBackgroundCompilation::VisitExtraWide(
BytecodeArrayIterator* iterator) {
UNREACHABLE();
}
void SerializerForBackgroundCompilation::VisitStackCheck(
BytecodeArrayIterator* iterator) {}
void SerializerForBackgroundCompilation::VisitLdaUndefined(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints().Clear();
environment()->accumulator_hints().AddConstant(
broker()->isolate()->factory()->undefined_value());
}
void SerializerForBackgroundCompilation::VisitLdaNull(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints().Clear();
environment()->accumulator_hints().AddConstant(
broker()->isolate()->factory()->null_value());
}
void SerializerForBackgroundCompilation::VisitLdaZero(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints().Clear();
environment()->accumulator_hints().AddConstant(
handle(Smi::FromInt(0), broker()->isolate()));
}
void SerializerForBackgroundCompilation::VisitLdaSmi(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints().Clear();
environment()->accumulator_hints().AddConstant(handle(
Smi::FromInt(iterator->GetImmediateOperand(0)), broker()->isolate()));
}
void SerializerForBackgroundCompilation::VisitLdaConstant(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints().Clear();
environment()->accumulator_hints().AddConstant(
handle(iterator->GetConstantForIndexOperand(0), broker()->isolate()));
}
void SerializerForBackgroundCompilation::VisitLdar(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints().Clear();
environment()->accumulator_hints().Add(
environment()->register_hints(iterator->GetRegisterOperand(0)));
}
void SerializerForBackgroundCompilation::VisitStar(
BytecodeArrayIterator* iterator) {
interpreter::Register reg = iterator->GetRegisterOperand(0);
environment()->register_hints(reg).Clear();
environment()->register_hints(reg).Add(environment()->accumulator_hints());
}
void SerializerForBackgroundCompilation::VisitMov(
BytecodeArrayIterator* iterator) {
interpreter::Register src = iterator->GetRegisterOperand(0);
interpreter::Register dst = iterator->GetRegisterOperand(1);
environment()->register_hints(dst).Clear();
environment()->register_hints(dst).Add(environment()->register_hints(src));
}
void SerializerForBackgroundCompilation::VisitCreateClosure(
BytecodeArrayIterator* iterator) {
Handle<SharedFunctionInfo> shared(
SharedFunctionInfo::cast(iterator->GetConstantForIndexOperand(0)),
broker()->isolate());
Handle<FeedbackCell> feedback_cell =
environment()->function().feedback_vector->GetClosureFeedbackCell(
iterator->GetIndexOperand(1));
Handle<Object> cell_value(feedback_cell->value(), broker()->isolate());
environment()->accumulator_hints().Clear();
if (cell_value->IsFeedbackVector()) {
environment()->accumulator_hints().AddFunctionBlueprint(
{shared, Handle<FeedbackVector>::cast(cell_value)});
}
}
void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver(
BytecodeArrayIterator* iterator) {
ProcessCallVarArgs(iterator, ConvertReceiverMode::kNullOrUndefined);
}
void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver0(
BytecodeArrayIterator* iterator) {
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
FeedbackSlot slot = iterator->GetSlotOperand(1);
Hints receiver(zone());
receiver.AddConstant(broker()->isolate()->factory()->undefined_value());
HintsVector parameters({receiver}, zone());
ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}
void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver1(
BytecodeArrayIterator* iterator) {
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
const Hints& arg0 =
environment()->register_hints(iterator->GetRegisterOperand(1));
FeedbackSlot slot = iterator->GetSlotOperand(2);
Hints receiver(zone());
receiver.AddConstant(broker()->isolate()->factory()->undefined_value());
HintsVector parameters({receiver, arg0}, zone());
ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}
void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver2(
BytecodeArrayIterator* iterator) {
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
const Hints& arg0 =
environment()->register_hints(iterator->GetRegisterOperand(1));
const Hints& arg1 =
environment()->register_hints(iterator->GetRegisterOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
Hints receiver(zone());
receiver.AddConstant(broker()->isolate()->factory()->undefined_value());
HintsVector parameters({receiver, arg0, arg1}, zone());
ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}
void SerializerForBackgroundCompilation::VisitCallAnyReceiver(
BytecodeArrayIterator* iterator) {
ProcessCallVarArgs(iterator, ConvertReceiverMode::kAny);
}
void SerializerForBackgroundCompilation::VisitCallNoFeedback(
BytecodeArrayIterator* iterator) {
ProcessCallVarArgs(iterator, ConvertReceiverMode::kNullOrUndefined);
}
void SerializerForBackgroundCompilation::VisitCallProperty(
BytecodeArrayIterator* iterator) {
ProcessCallVarArgs(iterator, ConvertReceiverMode::kNullOrUndefined);
}
void SerializerForBackgroundCompilation::VisitCallProperty0(
BytecodeArrayIterator* iterator) {
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
const Hints& receiver =
environment()->register_hints(iterator->GetRegisterOperand(1));
FeedbackSlot slot = iterator->GetSlotOperand(2);
HintsVector parameters({receiver}, zone());
ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}
void SerializerForBackgroundCompilation::VisitCallProperty1(
BytecodeArrayIterator* iterator) {
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
const Hints& receiver =
environment()->register_hints(iterator->GetRegisterOperand(1));
const Hints& arg0 =
environment()->register_hints(iterator->GetRegisterOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
HintsVector parameters({receiver, arg0}, zone());
ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}
void SerializerForBackgroundCompilation::VisitCallProperty2(
BytecodeArrayIterator* iterator) {
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
const Hints& receiver =
environment()->register_hints(iterator->GetRegisterOperand(1));
const Hints& arg0 =
environment()->register_hints(iterator->GetRegisterOperand(2));
const Hints& arg1 =
environment()->register_hints(iterator->GetRegisterOperand(3));
FeedbackSlot slot = iterator->GetSlotOperand(4);
HintsVector parameters({receiver, arg0, arg1}, zone());
ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}
void SerializerForBackgroundCompilation::VisitCallWithSpread(
BytecodeArrayIterator* iterator) {
ProcessCallVarArgs(iterator, ConvertReceiverMode::kAny, true);
}
Hints SerializerForBackgroundCompilation::RunChildSerializer(
CompilationSubject function, base::Optional<Hints> new_target,
const HintsVector& arguments, bool with_spread) {
if (with_spread) {
DCHECK_LT(0, arguments.size());
// Pad the missing arguments in case we were called with spread operator.
// Drop the last actually passed argument, which contains the spread.
// We don't know what the spread element produces. Therefore we pretend
// that the function is called with the maximal number of parameters and
// that we have no information about the parameters that were not
// explicitly provided.
HintsVector padded = arguments;
padded.pop_back(); // Remove the spread element.
// Fill the rest with empty hints.
padded.resize(
function.blueprint().shared->GetBytecodeArray()->parameter_count(),
Hints(zone()));
return RunChildSerializer(function, new_target, padded, false);
}
if (FLAG_trace_heap_broker) {
std::ostream& out = broker()->Trace();
out << "\nWill run child serializer with environment:\n"
<< "===========================================\n"
<< *environment();
}
SerializerForBackgroundCompilation child_serializer(
broker(), zone(), function, new_target, arguments);
return child_serializer.Run();
}
namespace {
base::Optional<HeapObjectRef> GetHeapObjectFeedback(
JSHeapBroker* broker, Handle<FeedbackVector> feedback_vector,
FeedbackSlot slot) {
if (slot.IsInvalid()) return base::nullopt;
FeedbackNexus nexus(feedback_vector, slot);
VectorSlotPair feedback(feedback_vector, slot, nexus.ic_state());
DCHECK(feedback.IsValid());
if (nexus.IsUninitialized()) return base::nullopt;
HeapObject object;
if (!nexus.GetFeedback()->GetHeapObject(&object)) return base::nullopt;
return HeapObjectRef(broker, handle(object, broker->isolate()));
}
} // namespace
void SerializerForBackgroundCompilation::ProcessCallOrConstruct(
Hints callee, base::Optional<Hints> new_target,
const HintsVector& arguments, FeedbackSlot slot, bool with_spread) {
// Incorporate feedback into hints.
base::Optional<HeapObjectRef> feedback = GetHeapObjectFeedback(
broker(), environment()->function().feedback_vector, slot);
if (feedback.has_value() && feedback->map().is_callable()) {
if (new_target.has_value()) {
// Construct; feedback is new_target, which often is also the callee.
new_target->AddConstant(feedback->object());
callee.AddConstant(feedback->object());
} else {
// Call; feedback is callee.
callee.AddConstant(feedback->object());
}
}
environment()->accumulator_hints().Clear();
for (auto hint : callee.constants()) {
if (!hint->IsJSFunction()) continue;
Handle<JSFunction> function = Handle<JSFunction>::cast(hint);
if (!function->shared()->IsInlineable() || !function->has_feedback_vector())
continue;
environment()->accumulator_hints().Add(RunChildSerializer(
{function, broker()->isolate()}, new_target, arguments, with_spread));
}
for (auto hint : callee.function_blueprints()) {
if (!hint.shared->IsInlineable()) continue;
environment()->accumulator_hints().Add(RunChildSerializer(
CompilationSubject(hint), new_target, arguments, with_spread));
}
}
void SerializerForBackgroundCompilation::ProcessCallVarArgs(
BytecodeArrayIterator* iterator, ConvertReceiverMode receiver_mode,
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;
if (iterator->current_bytecode() != interpreter::Bytecode::kCallNoFeedback) {
slot = iterator->GetSlotOperand(3);
}
HintsVector arguments(zone());
// The receiver is either given in the first register or it is implicitly
// the {undefined} value.
if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) {
Hints receiver(zone());
receiver.AddConstant(broker()->isolate()->factory()->undefined_value());
arguments.push_back(receiver);
}
environment()->ExportRegisterHints(first_reg, reg_count, arguments);
ProcessCallOrConstruct(callee, base::nullopt, arguments, slot);
}
void SerializerForBackgroundCompilation::ProcessJump(
interpreter::BytecodeArrayIterator* iterator) {
int jump_target = iterator->GetJumpTargetOffset();
int current_offset = iterator->current_offset();
if (current_offset >= jump_target) return;
stashed_environments_[jump_target] = new (zone()) Environment(*environment());
}
void SerializerForBackgroundCompilation::MergeAfterJump(
interpreter::BytecodeArrayIterator* iterator) {
int current_offset = iterator->current_offset();
auto stash = stashed_environments_.find(current_offset);
if (stash != stashed_environments_.end()) {
environment()->Merge(stash->second);
stashed_environments_.erase(stash);
}
}
void SerializerForBackgroundCompilation::VisitReturn(
BytecodeArrayIterator* iterator) {
environment()->return_value_hints().Add(environment()->accumulator_hints());
environment()->ClearEphemeralHints();
}
void SerializerForBackgroundCompilation::Environment::ExportRegisterHints(
interpreter::Register first, size_t count, HintsVector& dst) {
dst.resize(dst.size() + count, Hints(zone()));
int reg_base = first.index();
for (int i = 0; i < static_cast<int>(count); ++i) {
dst.push_back(register_hints(interpreter::Register(reg_base + i)));
}
}
void SerializerForBackgroundCompilation::VisitConstruct(
BytecodeArrayIterator* iterator) {
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
size_t reg_count = iterator->GetRegisterCountOperand(2);
FeedbackSlot slot = iterator->GetSlotOperand(3);
const Hints& new_target = environment()->accumulator_hints();
HintsVector arguments(zone());
environment()->ExportRegisterHints(first_reg, reg_count, arguments);
ProcessCallOrConstruct(callee, new_target, arguments, slot);
}
void SerializerForBackgroundCompilation::VisitConstructWithSpread(
BytecodeArrayIterator* iterator) {
const Hints& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
size_t reg_count = iterator->GetRegisterCountOperand(2);
FeedbackSlot slot = iterator->GetSlotOperand(3);
const Hints& new_target = environment()->accumulator_hints();
HintsVector arguments(zone());
environment()->ExportRegisterHints(first_reg, reg_count, arguments);
ProcessCallOrConstruct(callee, new_target, arguments, slot, true);
}
GlobalAccessFeedback const*
SerializerForBackgroundCompilation::ProcessFeedbackForGlobalAccess(
FeedbackSlot slot) {
if (slot.IsInvalid()) return nullptr;
if (environment()->function().feedback_vector.is_null()) return nullptr;
FeedbackSource source(environment()->function().feedback_vector, slot);
if (!broker()->HasFeedback(source)) {
const GlobalAccessFeedback* feedback =
broker()->ProcessFeedbackForGlobalAccess(source);
broker()->SetFeedback(source, feedback);
return feedback;
}
return nullptr;
}
void SerializerForBackgroundCompilation::VisitLdaGlobal(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(1);
environment()->accumulator_hints().Clear();
GlobalAccessFeedback const* feedback = ProcessFeedbackForGlobalAccess(slot);
if (feedback != nullptr) {
// We may be able to contribute to accumulator constant hints.
base::Optional<ObjectRef> value = feedback->GetConstantHint();
if (value.has_value()) {
environment()->accumulator_hints().AddConstant(value->object());
}
}
}
void SerializerForBackgroundCompilation::VisitLdaGlobalInsideTypeof(
BytecodeArrayIterator* iterator) {
VisitLdaGlobal(iterator);
}
void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlot(
BytecodeArrayIterator* iterator) {
VisitLdaGlobal(iterator);
}
void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlotInsideTypeof(
BytecodeArrayIterator* iterator) {
VisitLdaGlobal(iterator);
}
void SerializerForBackgroundCompilation::VisitStaGlobal(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(1);
ProcessFeedbackForGlobalAccess(slot);
}
namespace {
template <class MapContainer>
MapHandles GetRelevantReceiverMaps(Isolate* isolate, MapContainer const& maps) {
MapHandles result;
for (Handle<Map> map : maps) {
if (Map::TryUpdate(isolate, map).ToHandle(&map) &&
!map->is_abandoned_prototype_map()) {
DCHECK(!map->is_deprecated());
result.push_back(map);
}
}
return result;
}
} // namespace
// Note: We never use the same feedback slot for multiple access modes.
void SerializerForBackgroundCompilation::ProcessFeedbackForKeyedPropertyAccess(
FeedbackSlot slot, AccessMode mode) {
if (slot.IsInvalid()) return;
if (environment()->function().feedback_vector.is_null()) return;
FeedbackNexus nexus(environment()->function().feedback_vector, slot);
FeedbackSource source(nexus);
if (broker()->HasFeedback(source)) return;
if (nexus.GetKeyType() == PROPERTY) {
CHECK_NE(mode, AccessMode::kStoreInLiteral);
return; // TODO(neis): Support named access.
}
DCHECK_EQ(nexus.GetKeyType(), ELEMENT);
CHECK(nexus.GetName().is_null());
MapHandles maps;
nexus.ExtractMaps(&maps);
ElementAccessFeedback const* processed =
broker()->ProcessFeedbackMapsForElementAccess(
GetRelevantReceiverMaps(broker()->isolate(), maps));
broker()->SetFeedback(source, processed);
if (processed == nullptr) return;
for (ElementAccessFeedback::MapIterator it = processed->all_maps(broker());
!it.done(); it.advance()) {
switch (mode) {
case AccessMode::kHas:
case AccessMode::kLoad:
it.current().SerializeForElementLoad();
break;
case AccessMode::kStore:
it.current().SerializeForElementStore();
break;
case AccessMode::kStoreInLiteral:
// This operation is fairly local and simple, nothing to serialize.
break;
}
}
}
void SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess(
MapRef const& map, NameRef const& name) {
// For JSNativeContextSpecialization::ReduceNamedAccess.
if (map.IsMapOfCurrentGlobalProxy()) {
broker()->native_context().global_proxy_object().GetPropertyCell(name,
true);
}
}
// Note: We never use the same feedback slot for multiple names.
void SerializerForBackgroundCompilation::ProcessFeedbackForNamedPropertyAccess(
FeedbackSlot slot, NameRef const& name) {
if (slot.IsInvalid()) return;
if (environment()->function().feedback_vector.is_null()) return;
FeedbackNexus nexus(environment()->function().feedback_vector, slot);
FeedbackSource source(nexus);
if (broker()->HasFeedback(source)) return;
MapHandles maps;
nexus.ExtractMaps(&maps);
for (Handle<Map> map : GetRelevantReceiverMaps(broker()->isolate(), maps)) {
ProcessMapForNamedPropertyAccess(MapRef(broker(), map), name);
}
// NamedProperty support is still WIP. For now we don't have any actual data
// to store, so use nullptr to at least record that we processed the feedback.
broker()->SetFeedback(source, nullptr);
}
void SerializerForBackgroundCompilation::VisitLdaKeyedProperty(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(1);
ProcessFeedbackForKeyedPropertyAccess(slot, AccessMode::kLoad);
environment()->accumulator_hints().Clear();
}
void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess(
Hints const& receiver, NameRef const& name, FeedbackSlot slot) {
if (!slot.IsInvalid()) ProcessFeedbackForNamedPropertyAccess(slot, name);
for (Handle<Map> map :
GetRelevantReceiverMaps(broker()->isolate(), receiver.maps())) {
ProcessMapForNamedPropertyAccess(MapRef(broker(), map), name);
}
JSGlobalProxyRef global_proxy =
broker()->native_context().global_proxy_object();
for (Handle<Object> object : receiver.constants()) {
// For JSNativeContextSpecialization::ReduceNamedAccessFromNexus.
if (object.equals(global_proxy.object())) {
global_proxy.GetPropertyCell(name, true);
}
}
environment()->accumulator_hints().Clear();
}
void SerializerForBackgroundCompilation::VisitLdaNamedProperty(
BytecodeArrayIterator* iterator) {
Hints const& receiver =
environment()->register_hints(iterator->GetRegisterOperand(0));
Handle<Name> name(Name::cast(iterator->GetConstantForIndexOperand(1)),
broker()->isolate());
FeedbackSlot slot = iterator->GetSlotOperand(2);
ProcessNamedPropertyAccess(receiver, NameRef(broker(), name), slot);
}
void SerializerForBackgroundCompilation::VisitStaNamedProperty(
BytecodeArrayIterator* iterator) {
VisitLdaNamedProperty(iterator);
}
void SerializerForBackgroundCompilation::VisitTestIn(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(1);
ProcessFeedbackForKeyedPropertyAccess(slot, AccessMode::kHas);
environment()->accumulator_hints().Clear();
}
void SerializerForBackgroundCompilation::VisitStaKeyedProperty(
BytecodeArrayIterator* iterator) {
const Hints& receiver =
environment()->register_hints(iterator->GetRegisterOperand(0));
FeedbackSlot slot = iterator->GetSlotOperand(2);
ProcessFeedbackForKeyedPropertyAccess(slot, AccessMode::kStore);
for (Handle<Object> object : receiver.constants()) {
// For JSNativeContextSpecialization::ReduceElementAccess.
if (object->IsJSTypedArray()) JSTypedArrayRef(broker(), object).Serialize();
}
environment()->accumulator_hints().Clear();
}
void SerializerForBackgroundCompilation::VisitStaInArrayLiteral(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(2);
ProcessFeedbackForKeyedPropertyAccess(slot, AccessMode::kStoreInLiteral);
environment()->accumulator_hints().Clear();
}
#define DEFINE_CLEAR_ENVIRONMENT(name, ...) \
void SerializerForBackgroundCompilation::Visit##name( \
BytecodeArrayIterator* iterator) { \
environment()->ClearEphemeralHints(); \
}
CLEAR_ENVIRONMENT_LIST(DEFINE_CLEAR_ENVIRONMENT)
#undef DEFINE_CLEAR_ENVIRONMENT
#define DEFINE_CLEAR_ACCUMULATOR(name, ...) \
void SerializerForBackgroundCompilation::Visit##name( \
BytecodeArrayIterator* iterator) { \
environment()->accumulator_hints().Clear(); \
}
CLEAR_ACCUMULATOR_LIST(DEFINE_CLEAR_ACCUMULATOR)
#undef DEFINE_CLEAR_ACCUMULATOR
#define DEFINE_CONDITIONAL_JUMP(name, ...) \
void SerializerForBackgroundCompilation::Visit##name( \
BytecodeArrayIterator* iterator) { \
ProcessJump(iterator); \
}
CONDITIONAL_JUMPS_LIST(DEFINE_CONDITIONAL_JUMP)
#undef DEFINE_CONDITIONAL_JUMP
#define DEFINE_UNCONDITIONAL_JUMP(name, ...) \
void SerializerForBackgroundCompilation::Visit##name( \
BytecodeArrayIterator* iterator) { \
ProcessJump(iterator); \
environment()->ClearEphemeralHints(); \
}
UNCONDITIONAL_JUMPS_LIST(DEFINE_UNCONDITIONAL_JUMP)
#undef DEFINE_UNCONDITIONAL_JUMP
#define DEFINE_IGNORE(name, ...) \
void SerializerForBackgroundCompilation::Visit##name( \
BytecodeArrayIterator* iterator) {}
INGORED_BYTECODE_LIST(DEFINE_IGNORE)
#undef DEFINE_IGNORE
} // namespace compiler
} // namespace internal
} // namespace v8