[maglev] Use PropertyAccessInfo to generate property loads
Using PropertyAccessInfo will allows us to encapsulate property monomorphic/polymorphic load/store logic similar to TF. Bug: v8:7700 Change-Id: I63099e39c7696e85adea801f953717a30786783d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3948622 Reviewed-by: Leszek Swirski <leszeks@chromium.org> Commit-Queue: Victor Gomes <victorgomes@chromium.org> Cr-Commit-Position: refs/heads/main@{#83646}
This commit is contained in:
parent
a8f64a54ee
commit
316dd0326c
@ -1174,159 +1174,196 @@ void MaglevGraphBuilder::BuildMapCheck(ValueNode* object,
|
|||||||
known_info->type = NodeType::kHeapObjectWithKnownMap;
|
known_info->type = NodeType::kHeapObjectWithKnownMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MaglevGraphBuilder::TryBuildMonomorphicLoad(ValueNode* receiver,
|
bool MaglevGraphBuilder::TryFoldLoadDictPrototypeConstant(
|
||||||
ValueNode* lookup_start_object,
|
compiler::PropertyAccessInfo access_info) {
|
||||||
const compiler::NameRef& name,
|
DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL);
|
||||||
const compiler::MapRef& map,
|
DCHECK(access_info.IsDictionaryProtoDataConstant());
|
||||||
MaybeObjectHandle handler) {
|
DCHECK(access_info.holder().has_value());
|
||||||
if (handler.is_null()) return false;
|
|
||||||
|
|
||||||
if (handler->IsSmi()) {
|
base::Optional<compiler::ObjectRef> constant =
|
||||||
return TryBuildMonomorphicLoadFromSmiHandler(receiver, lookup_start_object,
|
access_info.holder()->GetOwnDictionaryProperty(
|
||||||
map, handler->ToSmi().value());
|
access_info.dictionary_index(), broker()->dependencies());
|
||||||
|
if (!constant.has_value()) return false;
|
||||||
|
|
||||||
|
for (compiler::MapRef map : access_info.lookup_start_object_maps()) {
|
||||||
|
Handle<Map> map_handle = map.object();
|
||||||
|
// Non-JSReceivers that passed AccessInfoFactory::ComputePropertyAccessInfo
|
||||||
|
// must have different lookup start map.
|
||||||
|
if (!map_handle->IsJSReceiverMap()) {
|
||||||
|
// Perform the implicit ToObject for primitives here.
|
||||||
|
// Implemented according to ES6 section 7.3.2 GetV (V, P).
|
||||||
|
JSFunction constructor =
|
||||||
|
Map::GetConstructorFunction(
|
||||||
|
*map_handle, *broker()->target_native_context().object())
|
||||||
|
.value();
|
||||||
|
// {constructor.initial_map()} is loaded/stored with acquire-release
|
||||||
|
// semantics for constructors.
|
||||||
|
map = MakeRefAssumeMemoryFence(broker(), constructor.initial_map());
|
||||||
|
DCHECK(map.object()->IsJSObjectMap());
|
||||||
|
}
|
||||||
|
broker()->dependencies()->DependOnConstantInDictionaryPrototypeChain(
|
||||||
|
map, access_info.name(), constant.value(), PropertyKind::kData);
|
||||||
}
|
}
|
||||||
HeapObject ho_handler;
|
|
||||||
if (!handler->GetHeapObject(&ho_handler)) return false;
|
|
||||||
|
|
||||||
if (ho_handler.IsCodeT()) {
|
SetAccumulator(GetConstant(constant.value()));
|
||||||
// TODO(leszeks): Call the code object directly.
|
return true;
|
||||||
return false;
|
}
|
||||||
} else if (ho_handler.IsAccessorPair()) {
|
|
||||||
// TODO(leszeks): Call the getter directly.
|
bool MaglevGraphBuilder::TryFoldLoadConstantDataField(
|
||||||
return false;
|
compiler::PropertyAccessInfo access_info) {
|
||||||
|
if (access_info.holder().has_value()) {
|
||||||
|
base::Optional<compiler::ObjectRef> constant =
|
||||||
|
access_info.holder()->GetOwnFastDataProperty(
|
||||||
|
access_info.field_representation(), access_info.field_index(),
|
||||||
|
broker()->dependencies());
|
||||||
|
if (constant.has_value()) {
|
||||||
|
SetAccumulator(GetConstant(constant.value()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO(victorgomes): Check if lookup_start_object is a constant object and
|
||||||
|
// unfold the load.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaglevGraphBuilder::TryBuildPropertyGetterCall(
|
||||||
|
compiler::PropertyAccessInfo access_info, ValueNode* receiver) {
|
||||||
|
compiler::ObjectRef constant = access_info.constant().value();
|
||||||
|
|
||||||
|
if (access_info.IsDictionaryProtoAccessorConstant()) {
|
||||||
|
// For fast mode holders we recorded dependencies in BuildPropertyLoad.
|
||||||
|
for (const compiler::MapRef map : access_info.lookup_start_object_maps()) {
|
||||||
|
broker()->dependencies()->DependOnConstantInDictionaryPrototypeChain(
|
||||||
|
map, access_info.name(), constant, PropertyKind::kAccessor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Introduce the call to the getter function.
|
||||||
|
if (constant.IsJSFunction()) {
|
||||||
|
Call* call = CreateNewNode<Call>(Call::kFixedInputCount + 1,
|
||||||
|
ConvertReceiverMode::kNotNullOrUndefined,
|
||||||
|
GetConstant(constant), GetContext());
|
||||||
|
call->set_arg(0, receiver);
|
||||||
|
SetAccumulator(AddNode(call));
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return TryBuildMonomorphicLoadFromLoadHandler(
|
// TODO(victorgomes): API calls.
|
||||||
receiver, lookup_start_object, name, map,
|
return false;
|
||||||
LoadHandler::cast(ho_handler));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MaglevGraphBuilder::TryBuildMonomorphicLoadFromSmiHandler(
|
void MaglevGraphBuilder::BuildLoadField(
|
||||||
ValueNode* receiver, ValueNode* lookup_start_object,
|
compiler::PropertyAccessInfo access_info, ValueNode* lookup_start_object) {
|
||||||
const compiler::MapRef& map, int32_t handler) {
|
if (TryFoldLoadConstantDataField(access_info)) return;
|
||||||
// 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;
|
|
||||||
|
|
||||||
BuildMapCheck(lookup_start_object, map);
|
|
||||||
|
|
||||||
|
// Resolve property holder.
|
||||||
ValueNode* load_source;
|
ValueNode* load_source;
|
||||||
if (LoadHandler::IsInobjectBits::decode(handler)) {
|
if (access_info.holder().has_value()) {
|
||||||
load_source = lookup_start_object;
|
load_source = GetConstant(access_info.holder().value());
|
||||||
} else {
|
} else {
|
||||||
|
load_source = lookup_start_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldIndex field_index = access_info.field_index();
|
||||||
|
if (!field_index.is_inobject()) {
|
||||||
// The field is in the property array, first load it from there.
|
// The field is in the property array, first load it from there.
|
||||||
load_source = AddNewNode<LoadTaggedField>(
|
load_source = AddNewNode<LoadTaggedField>(
|
||||||
{lookup_start_object}, JSReceiver::kPropertiesOrHashOffset);
|
{load_source}, JSReceiver::kPropertiesOrHashOffset);
|
||||||
}
|
}
|
||||||
int field_index = LoadHandler::FieldIndexBits::decode(handler);
|
|
||||||
if (LoadHandler::IsDoubleBits::decode(handler)) {
|
|
||||||
FieldIndex field = FieldIndex::ForSmiLoadHandler(*map.object(), handler);
|
|
||||||
DescriptorArray descriptors = *map.instance_descriptors().object();
|
|
||||||
InternalIndex index =
|
|
||||||
descriptors.Search(field.property_index(), *map.object());
|
|
||||||
DCHECK(index.is_found());
|
|
||||||
DCHECK(Representation::Double().CanBeInPlaceChangedTo(
|
|
||||||
descriptors.GetDetails(index).representation()));
|
|
||||||
const compiler::CompilationDependency* dep =
|
|
||||||
broker()->dependencies()->FieldRepresentationDependencyOffTheRecord(
|
|
||||||
map, index, Representation::Double());
|
|
||||||
broker()->dependencies()->RecordDependency(dep);
|
|
||||||
|
|
||||||
|
// Do the load.
|
||||||
|
if (field_index.is_double()) {
|
||||||
SetAccumulator(
|
SetAccumulator(
|
||||||
AddNewNode<LoadDoubleField>({load_source}, field_index * kTaggedSize));
|
AddNewNode<LoadDoubleField>({load_source}, field_index.offset()));
|
||||||
} else {
|
} else {
|
||||||
SetAccumulator(
|
SetAccumulator(
|
||||||
AddNewNode<LoadTaggedField>({load_source}, field_index * kTaggedSize));
|
AddNewNode<LoadTaggedField>({load_source}, field_index.offset()));
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MaglevGraphBuilder::TryBuildMonomorphicLoadFromLoadHandler(
|
bool MaglevGraphBuilder::TryBuildPropertyLoad(
|
||||||
ValueNode* receiver, ValueNode* lookup_start_object,
|
ValueNode* receiver, ValueNode* lookup_start_object,
|
||||||
const compiler::NameRef& name, const compiler::MapRef& map,
|
compiler::PropertyAccessInfo const& access_info) {
|
||||||
LoadHandler handler) {
|
if (access_info.holder().has_value() && !access_info.HasDictionaryHolder()) {
|
||||||
Object maybe_smi_handler = handler.smi_handler(local_isolate_);
|
broker()->dependencies()->DependOnStablePrototypeChains(
|
||||||
if (!maybe_smi_handler.IsSmi()) return false;
|
access_info.lookup_start_object_maps(), kStartAtPrototype,
|
||||||
|
access_info.holder().value());
|
||||||
const int smi_handler = Smi::ToInt(maybe_smi_handler);
|
|
||||||
LoadHandler::Kind kind = LoadHandler::KindBits::decode(smi_handler);
|
|
||||||
if (kind != LoadHandler::Kind::kConstantFromPrototype &&
|
|
||||||
kind != LoadHandler::Kind::kAccessorFromPrototype) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LoadHandler::LookupOnLookupStartObjectBits::decode(smi_handler)) {
|
if (access_info.IsNotFound()) {
|
||||||
return false;
|
SetAccumulator(GetRootConstant(RootIndex::kUndefinedValue));
|
||||||
}
|
|
||||||
|
|
||||||
// This fiddly early return is necessary because we can't return `false` any
|
|
||||||
// more once we've started emitting code.
|
|
||||||
if (!map.IsStringMap() &&
|
|
||||||
LoadHandler::DoAccessCheckOnLookupStartObjectBits::decode(smi_handler)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MaybeObject maybe_data1 = handler.data1(local_isolate_);
|
|
||||||
if (maybe_data1.IsCleared()) {
|
|
||||||
EmitUnconditionalDeopt(
|
|
||||||
DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
} else if (access_info.IsFastAccessorConstant() ||
|
||||||
const Object data1 = maybe_data1.GetHeapObjectOrSmi();
|
access_info.IsDictionaryProtoAccessorConstant()) {
|
||||||
|
return TryBuildPropertyGetterCall(access_info, receiver);
|
||||||
if (map.IsStringMap()) {
|
} else if (access_info.IsDataField() || access_info.IsFastDataConstant()) {
|
||||||
// Check for string maps before checking if we need to do an access check.
|
BuildLoadField(access_info, lookup_start_object);
|
||||||
// Primitive strings always get the prototype from the native context
|
return true;
|
||||||
// they're operated on, so they don't need the access check.
|
} else if (access_info.IsDictionaryProtoDataConstant()) {
|
||||||
BuildCheckString(lookup_start_object);
|
return TryFoldLoadDictPrototypeConstant(access_info);
|
||||||
} else {
|
} else {
|
||||||
DCHECK(!LoadHandler::DoAccessCheckOnLookupStartObjectBits::decode(
|
// TODO(victorgomes): Add other property access implementation.
|
||||||
smi_handler));
|
return false;
|
||||||
BuildMapCheck(lookup_start_object, map);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create compilation dependencies as needed.
|
bool MaglevGraphBuilder::TryBuildPropertyAccess(
|
||||||
// TODO(v8:7700): We only use the PropertyAccessInfo in order to create the
|
ValueNode* receiver, ValueNode* lookup_start_object,
|
||||||
// proper dependencies. We should consider either using more of the PAI
|
compiler::PropertyAccessInfo const& access_info,
|
||||||
// (instead of relying on handlers), or duplicate dependency creation logic
|
compiler::AccessMode access_mode) {
|
||||||
// here (which is a bit involved since it requires e.g. a prototype walk).
|
switch (access_mode) {
|
||||||
{
|
case compiler::AccessMode::kLoad:
|
||||||
compiler::PropertyAccessInfo info = broker()->GetPropertyAccessInfo(
|
return TryBuildPropertyLoad(receiver, lookup_start_object, access_info);
|
||||||
map, name, compiler::AccessMode::kLoad, broker()->dependencies());
|
|
||||||
if (!info.IsInvalid()) {
|
|
||||||
DCHECK(!info.HasDictionaryHolder());
|
|
||||||
info.RecordDependencies(broker()->dependencies());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (kind) {
|
|
||||||
case LoadHandler::Kind::kConstantFromPrototype: {
|
|
||||||
if (data1.IsSmi()) {
|
|
||||||
// Functionally, the else branch does the same - but we avoid the
|
|
||||||
// broker overhead by dispatching here.
|
|
||||||
SetAccumulator(GetSmiConstant(Smi::ToInt(data1)));
|
|
||||||
} else {
|
|
||||||
SetAccumulator(GetConstant(MakeRefAssumeMemoryFence(
|
|
||||||
broker(), broker()->CanonicalPersistentHandle(data1))));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LoadHandler::Kind::kAccessorFromPrototype: {
|
|
||||||
compiler::ObjectRef getter_ref = MakeRefAssumeMemoryFence(
|
|
||||||
broker(), broker()->CanonicalPersistentHandle(data1));
|
|
||||||
|
|
||||||
Call* call = CreateNewNode<Call>(Call::kFixedInputCount + 1,
|
|
||||||
ConvertReceiverMode::kNotNullOrUndefined,
|
|
||||||
GetConstant(getter_ref), GetContext());
|
|
||||||
call->set_arg(0, receiver);
|
|
||||||
SetAccumulator(AddNode(call));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
// TODO(victorgomes): BuildPropertyStore and BuildPropertyTest.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaglevGraphBuilder::TryBuildNamedAccess(
|
||||||
|
ValueNode* receiver, ValueNode* lookup_start_object,
|
||||||
|
compiler::NamedAccessFeedback const& feedback,
|
||||||
|
compiler::AccessMode access_mode) {
|
||||||
|
ZoneVector<compiler::PropertyAccessInfo> access_infos(zone());
|
||||||
|
{
|
||||||
|
ZoneVector<compiler::PropertyAccessInfo> access_infos_for_feedback(zone());
|
||||||
|
for (const compiler::MapRef& map : feedback.maps()) {
|
||||||
|
if (map.is_deprecated()) continue;
|
||||||
|
compiler::PropertyAccessInfo access_info =
|
||||||
|
broker()->GetPropertyAccessInfo(map, feedback.name(), access_mode,
|
||||||
|
broker()->dependencies());
|
||||||
|
access_infos_for_feedback.push_back(access_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
compiler::AccessInfoFactory access_info_factory(
|
||||||
|
broker(), broker()->dependencies(), zone());
|
||||||
|
if (!access_info_factory.FinalizePropertyAccessInfos(
|
||||||
|
access_infos_for_feedback, access_mode, &access_infos)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for monomorphic case.
|
||||||
|
if (access_infos.size() == 1) {
|
||||||
|
compiler::PropertyAccessInfo access_info = access_infos.front();
|
||||||
|
const compiler::MapRef& map =
|
||||||
|
access_info.lookup_start_object_maps().front();
|
||||||
|
if (map.IsStringMap()) {
|
||||||
|
// Check for string maps before checking if we need to do an access
|
||||||
|
// check. Primitive strings always get the prototype from the native
|
||||||
|
// context they're operated on, so they don't need the access check.
|
||||||
|
BuildCheckString(lookup_start_object);
|
||||||
|
} else {
|
||||||
|
BuildMapCheck(lookup_start_object, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the actual property access.
|
||||||
|
return TryBuildPropertyAccess(receiver, lookup_start_object, access_info,
|
||||||
|
access_mode);
|
||||||
|
} else {
|
||||||
|
// TODO(victorgomes): polymorphic case.
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MaglevGraphBuilder::TryBuildMonomorphicElementLoad(
|
bool MaglevGraphBuilder::TryBuildMonomorphicElementLoad(
|
||||||
@ -1434,20 +1471,11 @@ void MaglevGraphBuilder::VisitGetNamedProperty() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case compiler::ProcessedFeedback::kNamedAccess: {
|
case compiler::ProcessedFeedback::kNamedAccess: {
|
||||||
const compiler::NamedAccessFeedback& named_feedback =
|
if (TryBuildNamedAccess(object, object,
|
||||||
processed_feedback.AsNamedAccess();
|
processed_feedback.AsNamedAccess(),
|
||||||
if (named_feedback.maps().size() != 1) break;
|
compiler::AccessMode::kLoad)) {
|
||||||
compiler::MapRef map = named_feedback.maps()[0];
|
|
||||||
|
|
||||||
// Monomorphic load, check the handler.
|
|
||||||
// TODO(leszeks): Make GetFeedbackForPropertyAccess read the handler.
|
|
||||||
MaybeObjectHandle handler =
|
|
||||||
FeedbackNexusForSlot(slot).FindHandlerForMap(map.object());
|
|
||||||
|
|
||||||
if (TryBuildMonomorphicLoad(object, object, name, map, handler)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1486,21 +1514,11 @@ void MaglevGraphBuilder::VisitGetNamedPropertyFromSuper() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case compiler::ProcessedFeedback::kNamedAccess: {
|
case compiler::ProcessedFeedback::kNamedAccess: {
|
||||||
const compiler::NamedAccessFeedback& named_feedback =
|
if (TryBuildNamedAccess(receiver, lookup_start_object,
|
||||||
processed_feedback.AsNamedAccess();
|
processed_feedback.AsNamedAccess(),
|
||||||
if (named_feedback.maps().size() != 1) break;
|
compiler::AccessMode::kLoad)) {
|
||||||
compiler::MapRef map = named_feedback.maps()[0];
|
|
||||||
|
|
||||||
// Monomorphic load, check the handler.
|
|
||||||
// TODO(leszeks): Make GetFeedbackForPropertyAccess read the handler.
|
|
||||||
MaybeObjectHandle handler =
|
|
||||||
FeedbackNexusForSlot(slot).FindHandlerForMap(map.object());
|
|
||||||
|
|
||||||
if (TryBuildMonomorphicLoad(receiver, lookup_start_object, name, map,
|
|
||||||
handler)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "src/compiler/bytecode-liveness-map.h"
|
#include "src/compiler/bytecode-liveness-map.h"
|
||||||
#include "src/compiler/heap-refs.h"
|
#include "src/compiler/heap-refs.h"
|
||||||
#include "src/compiler/js-heap-broker.h"
|
#include "src/compiler/js-heap-broker.h"
|
||||||
|
#include "src/compiler/processed-feedback.h"
|
||||||
#include "src/deoptimizer/deoptimize-reason.h"
|
#include "src/deoptimizer/deoptimize-reason.h"
|
||||||
#include "src/flags/flags.h"
|
#include "src/flags/flags.h"
|
||||||
#include "src/interpreter/bytecode-array-iterator.h"
|
#include "src/interpreter/bytecode-array-iterator.h"
|
||||||
@ -922,20 +923,25 @@ class MaglevGraphBuilder {
|
|||||||
void BuildCheckSymbol(ValueNode* object);
|
void BuildCheckSymbol(ValueNode* object);
|
||||||
void BuildMapCheck(ValueNode* object, const compiler::MapRef& map);
|
void BuildMapCheck(ValueNode* object, const compiler::MapRef& map);
|
||||||
|
|
||||||
bool TryBuildMonomorphicLoad(ValueNode* receiver,
|
bool TryFoldLoadDictPrototypeConstant(
|
||||||
ValueNode* lookup_start_object,
|
compiler::PropertyAccessInfo access_info);
|
||||||
const compiler::NameRef& name,
|
bool TryFoldLoadConstantDataField(compiler::PropertyAccessInfo access_info);
|
||||||
const compiler::MapRef& map,
|
|
||||||
MaybeObjectHandle handler);
|
void BuildLoadField(compiler::PropertyAccessInfo access_info,
|
||||||
bool TryBuildMonomorphicLoadFromSmiHandler(ValueNode* receiver,
|
ValueNode* lookup_start_object);
|
||||||
ValueNode* lookup_start_object,
|
bool TryBuildPropertyGetterCall(compiler::PropertyAccessInfo access_info,
|
||||||
const compiler::MapRef& map,
|
ValueNode* receiver);
|
||||||
int32_t handler);
|
|
||||||
bool TryBuildMonomorphicLoadFromLoadHandler(ValueNode* receiver,
|
bool TryBuildPropertyLoad(ValueNode* receiver, ValueNode* lookup_start_object,
|
||||||
ValueNode* lookup_start_object,
|
compiler::PropertyAccessInfo const& access_info);
|
||||||
const compiler::NameRef& name,
|
bool TryBuildPropertyAccess(ValueNode* receiver,
|
||||||
const compiler::MapRef& map,
|
ValueNode* lookup_start_object,
|
||||||
LoadHandler handler);
|
compiler::PropertyAccessInfo const& access_info,
|
||||||
|
compiler::AccessMode access_mode);
|
||||||
|
|
||||||
|
bool TryBuildNamedAccess(ValueNode* receiver, ValueNode* lookup_start_object,
|
||||||
|
compiler::NamedAccessFeedback const& feedback,
|
||||||
|
compiler::AccessMode access_mode);
|
||||||
|
|
||||||
bool TryBuildMonomorphicElementLoad(ValueNode* object, ValueNode* index,
|
bool TryBuildMonomorphicElementLoad(ValueNode* object, ValueNode* index,
|
||||||
const compiler::MapRef& map,
|
const compiler::MapRef& map,
|
||||||
|
Loading…
Reference in New Issue
Block a user