Commit 3b253419 authored by Leszek Swirski's avatar Leszek Swirski Committed by V8 LUCI CQ

[maglev] Add support for constant-from-prototype loads

Add support for LoadHandlers with the kConstantFromPrototype kind. With
some dependency checks, this becomes a map check and constant load.

Bug: v8:7700
Change-Id: I865eee7be4df9bd0ba56943814f601e3e950ed80
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3675101Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80855}
parent ac1c4c6d
......@@ -1231,25 +1231,6 @@ bool CompilationDependencies::PrepareInstallPredictable() {
return true;
}
namespace {
// This function expects to never see a JSProxy.
void DependOnStablePrototypeChain(CompilationDependencies* deps, MapRef map,
base::Optional<JSObjectRef> last_prototype) {
while (true) {
HeapObjectRef proto = map.prototype();
if (!proto.IsJSObject()) {
CHECK_EQ(proto.map().oddball_type(), OddballType::kNull);
break;
}
map = proto.map();
deps->DependOnStableMap(map);
if (last_prototype.has_value() && proto.equals(*last_prototype)) break;
}
}
} // namespace
#define V(Name) \
const Name##Dependency* CompilationDependency::As##Name() const { \
DCHECK(Is##Name()); \
......@@ -1262,16 +1243,33 @@ void CompilationDependencies::DependOnStablePrototypeChains(
ZoneVector<MapRef> const& receiver_maps, WhereToStart start,
base::Optional<JSObjectRef> last_prototype) {
for (MapRef receiver_map : receiver_maps) {
if (receiver_map.IsPrimitiveMap()) {
// Perform the implicit ToObject for primitives here.
// Implemented according to ES6 section 7.3.2 GetV (V, P).
// Note: Keep sync'd with AccessInfoFactory::ComputePropertyAccessInfo.
base::Optional<JSFunctionRef> constructor =
broker_->target_native_context().GetConstructorFunction(receiver_map);
receiver_map = constructor.value().initial_map(this);
DependOnStablePrototypeChain(receiver_map, start, last_prototype);
}
}
void CompilationDependencies::DependOnStablePrototypeChain(
MapRef receiver_map, WhereToStart start,
base::Optional<JSObjectRef> last_prototype) {
if (receiver_map.IsPrimitiveMap()) {
// Perform the implicit ToObject for primitives here.
// Implemented according to ES6 section 7.3.2 GetV (V, P).
// Note: Keep sync'd with AccessInfoFactory::ComputePropertyAccessInfo.
base::Optional<JSFunctionRef> constructor =
broker_->target_native_context().GetConstructorFunction(receiver_map);
receiver_map = constructor.value().initial_map(this);
}
if (start == kStartAtReceiver) DependOnStableMap(receiver_map);
MapRef map = receiver_map;
while (true) {
HeapObjectRef proto = map.prototype();
if (!proto.IsJSObject()) {
CHECK_EQ(proto.map().oddball_type(), OddballType::kNull);
break;
}
if (start == kStartAtReceiver) DependOnStableMap(receiver_map);
DependOnStablePrototypeChain(this, receiver_map, last_prototype);
map = proto.map();
DependOnStableMap(map);
if (last_prototype.has_value() && proto.equals(*last_prototype)) break;
}
}
......
......@@ -118,6 +118,12 @@ class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject {
base::Optional<JSObjectRef> last_prototype =
base::Optional<JSObjectRef>());
// For the given map, depend on the stability of (the maps of) all prototypes
// up to (and including) the {last_prototype}.
void DependOnStablePrototypeChain(MapRef receiver_maps, WhereToStart start,
base::Optional<JSObjectRef> last_prototype =
base::Optional<JSObjectRef>());
// Like DependOnElementsKind but also applies to all nested allocation sites.
void DependOnElementsKinds(const AllocationSiteRef& site);
......
......@@ -681,6 +681,87 @@ MAGLEV_UNIMPLEMENTED_BYTECODE(LdaLookupContextSlotInsideTypeof)
MAGLEV_UNIMPLEMENTED_BYTECODE(LdaLookupGlobalSlotInsideTypeof)
MAGLEV_UNIMPLEMENTED_BYTECODE(StaLookupSlot)
bool MaglevGraphBuilder::TryBuildMonomorphicLoad(ValueNode* object,
const compiler::MapRef& map,
MaybeObjectHandle handler) {
if (handler.is_null()) return false;
if (handler->IsSmi()) {
return TryBuildMonomorphicLoadFromSmiHandler(object, map,
handler->ToSmi().value());
} else if (handler->GetHeapObject().IsCodeT()) {
// TODO(leszeks): Call the code object directly.
return false;
} else {
return TryBuildMonomorphicLoadFromLoadHandler(
object, map, LoadHandler::cast(handler->GetHeapObject()));
}
}
bool MaglevGraphBuilder::TryBuildMonomorphicLoadFromSmiHandler(
ValueNode* object, const compiler::MapRef& map, int32_t handler) {
// Smi handler, emit a map check and LoadField.
LoadHandler::Kind kind = LoadHandler::KindBits::decode(handler);
if (kind != LoadHandler::Kind::kField) return false;
if (LoadHandler::IsWasmStructBits::decode(handler)) return false;
AddNewNode<CheckMaps>({object}, map);
ValueNode* load_source;
if (LoadHandler::IsInobjectBits::decode(handler)) {
load_source = object;
} else {
// The field is in the property array, first load it from there.
load_source = AddNewNode<LoadTaggedField>(
{object}, JSReceiver::kPropertiesOrHashOffset);
}
if (LoadHandler::IsDoubleBits::decode(handler)) {
SetAccumulator(AddNewNode<LoadDoubleField>(
{load_source},
LoadHandler::FieldIndexBits::decode(handler) * kTaggedSize));
} else {
SetAccumulator(AddNewNode<LoadTaggedField>(
{load_source},
LoadHandler::FieldIndexBits::decode(handler) * kTaggedSize));
}
return true;
}
bool MaglevGraphBuilder::TryBuildMonomorphicLoadFromLoadHandler(
ValueNode* object, const compiler::MapRef& map, LoadHandler handler) {
Object maybe_smi_handler = handler.smi_handler(local_isolate_);
if (!maybe_smi_handler.IsSmi()) return false;
int smi_handler = Smi::cast(maybe_smi_handler).value();
LoadHandler::Kind kind = LoadHandler::KindBits::decode(smi_handler);
bool do_access_check_on_lookup_start_object =
LoadHandler::DoAccessCheckOnLookupStartObjectBits::decode(smi_handler);
bool lookup_on_lookup_start_object =
LoadHandler::LookupOnLookupStartObjectBits::decode(smi_handler);
if (do_access_check_on_lookup_start_object) return false;
if (lookup_on_lookup_start_object) return false;
if (kind != LoadHandler::Kind::kConstantFromPrototype) return false;
AddNewNode<CheckMaps>({object}, map);
Object validity_cell = handler.validity_cell(local_isolate_);
if (validity_cell.IsCell(local_isolate_)) {
broker()->dependencies()->DependOnStablePrototypeChain(
map, kStartAtPrototype, base::nullopt);
} else {
DCHECK_EQ(Smi::ToInt(validity_cell), Map::kPrototypeChainValid);
}
MaybeObject value = handler.data1(local_isolate_);
if (value.IsSmi()) {
SetAccumulator(AddNewNode<SmiConstant>({}, value.ToSmi()));
} else {
SetAccumulator(AddNewNode<Constant>(
{}, MakeRefAssumeMemoryFence(
broker(),
broker()->CanonicalPersistentHandle(value.GetHeapObject()))));
}
return true;
}
void MaglevGraphBuilder::VisitGetNamedProperty() {
// GetNamedProperty <object> <name_index> <slot>
ValueNode* object = LoadRegisterTagged(0);
......@@ -700,43 +781,15 @@ void MaglevGraphBuilder::VisitGetNamedProperty() {
case compiler::ProcessedFeedback::kNamedAccess: {
const compiler::NamedAccessFeedback& named_feedback =
processed_feedback.AsNamedAccess();
if (named_feedback.maps().size() == 1) {
// Monomorphic load, check the handler.
// TODO(leszeks): Make GetFeedbackForPropertyAccess read the handler.
MaybeObjectHandle handler =
FeedbackNexusForSlot(slot).FindHandlerForMap(
named_feedback.maps()[0].object());
if (!handler.is_null() && handler->IsSmi()) {
// Smi handler, emit a map check and LoadField.
int smi_handler = handler->ToSmi().value();
LoadHandler::Kind kind = LoadHandler::KindBits::decode(smi_handler);
if (kind == LoadHandler::Kind::kField &&
!LoadHandler::IsWasmStructBits::decode(smi_handler)) {
AddNewNode<CheckMaps>({object}, named_feedback.maps()[0]);
if (named_feedback.maps().size() != 1) break;
compiler::MapRef map = named_feedback.maps()[0];
ValueNode* load_source;
if (LoadHandler::IsInobjectBits::decode(smi_handler)) {
load_source = object;
} else {
// The field is in the property array, first load it from there.
load_source = AddNewNode<LoadTaggedField>(
{object}, JSReceiver::kPropertiesOrHashOffset);
}
if (LoadHandler::IsDoubleBits::decode(smi_handler)) {
SetAccumulator(AddNewNode<LoadDoubleField>(
{load_source},
LoadHandler::FieldIndexBits::decode(smi_handler) *
kTaggedSize));
} else {
SetAccumulator(AddNewNode<LoadTaggedField>(
{load_source},
LoadHandler::FieldIndexBits::decode(smi_handler) *
kTaggedSize));
}
return;
}
}
}
// Monomorphic load, check the handler.
// TODO(leszeks): Make GetFeedbackForPropertyAccess read the handler.
MaybeObjectHandle handler =
FeedbackNexusForSlot(slot).FindHandlerForMap(map.object());
if (TryBuildMonomorphicLoad(object, map, handler)) return;
} break;
default:
......
......@@ -570,6 +570,15 @@ class MaglevGraphBuilder {
void BuildPropertyCellAccess(const compiler::PropertyCellRef& property_cell);
bool TryBuildMonomorphicLoad(ValueNode* object, const compiler::MapRef& map,
MaybeObjectHandle handler);
bool TryBuildMonomorphicLoadFromSmiHandler(ValueNode* object,
const compiler::MapRef& map,
int32_t handler);
bool TryBuildMonomorphicLoadFromLoadHandler(ValueNode* object,
const compiler::MapRef& map,
LoadHandler handler);
template <Operation kOperation>
void BuildGenericUnaryOperationNode();
template <Operation kOperation>
......
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