v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
property-access-builder.cc
Go to the documentation of this file.
1// Copyright 2017 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <optional>
8
18#include "src/objects/map-inl.h"
20
21namespace v8 {
22namespace internal {
23namespace compiler {
24
26
28
32
36
38 for (MapRef map : maps) {
39 if (!map.IsStringMap()) return false;
40 }
41 return true;
42}
43
45 ZoneVector<MapRef> const& maps) {
46 for (MapRef map : maps) {
47 if (!map.IsJSPrimitiveWrapperMap()) return false;
48 auto elements_kind = map.elements_kind();
49 if (elements_kind != FAST_STRING_WRAPPER_ELEMENTS &&
50 elements_kind != SLOW_STRING_WRAPPER_ELEMENTS) {
51 return false;
52 }
53 }
54 return true;
55}
56
58 ZoneVector<MapRef> const& maps) {
59 for (MapRef map : maps) {
60 if (!map.IsJSTypedArrayMap()) return false;
61 if (IsRabGsabTypedArrayElementsKind(map.elements_kind())) {
62 return false;
63 }
64 }
65 return true;
66}
67
68namespace {
69
70bool HasOnlyNumberMaps(JSHeapBroker* broker, ZoneVector<MapRef> const& maps) {
71 for (MapRef map : maps) {
72 if (map.instance_type() != HEAP_NUMBER_TYPE) return false;
73 }
74 return true;
75}
76
77} // namespace
78
80 ZoneVector<MapRef> const& maps,
81 Node** receiver, Effect* effect,
82 Control control) {
83 if (HasOnlyStringMaps(broker, maps)) {
84 // Monormorphic string access (ignoring the fact that there are multiple
85 // String maps).
86 *receiver = *effect =
87 graph()->NewNode(simplified()->CheckString(FeedbackSource()), *receiver,
88 *effect, control);
89 return true;
90 }
91 return false;
92}
93
95 ZoneVector<MapRef> const& maps,
96 Node** receiver, Effect* effect,
97 Control control) {
98 if (HasOnlyNumberMaps(broker, maps)) {
99 // Monomorphic number access (we also deal with Smis here).
100 *receiver = *effect =
101 graph()->NewNode(simplified()->CheckNumber(FeedbackSource()), *receiver,
102 *effect, control);
103 return true;
104 }
105 return false;
106}
107
109 Node* object, Effect* effect, Control control,
110 ZoneVector<MapRef> const& maps,
111 bool has_deprecated_map_without_migration_target) {
112 HeapObjectMatcher m(object);
113 if (m.HasResolvedValue()) {
114 MapRef object_map = m.Ref(broker()).map(broker());
115 if (object_map.is_stable()) {
116 for (MapRef map : maps) {
117 if (map.equals(object_map)) {
118 dependencies()->DependOnStableMap(object_map);
119 return;
120 }
121 }
122 }
123 }
124 ZoneRefSet<Map> map_set;
125 bool has_migration_target = false;
126 for (MapRef map : maps) {
127 map_set.insert(map, graph()->zone());
128 if (map.is_migration_target()) {
129 has_migration_target = true;
130 }
131 }
133 if (has_migration_target) {
135 } else if (has_deprecated_map_without_migration_target) {
137 }
138 *effect = graph()->NewNode(simplified()->CheckMaps(flags, map_set), object,
139 *effect, control);
140}
141
143 Control control, ObjectRef value) {
144 if (value.IsHeapObject()) {
146 if (m.Is(value.AsHeapObject().object())) return receiver;
147 }
148 Node* expected = jsgraph()->ConstantNoHole(value, broker());
149 Node* check =
150 graph()->NewNode(simplified()->ReferenceEqual(), receiver, expected);
151 *effect =
152 graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongValue),
153 check, *effect, control);
154 return expected;
155}
156
158 Control control,
159 FeedbackSource feedback_source) {
160 Node* smi_value = *effect = graph()->NewNode(
161 simplified()->CheckSmi(feedback_source), value, *effect, control);
162 return smi_value;
163}
164
166 Control control,
167 FeedbackSource feedback_source) {
168 Node* number = *effect = graph()->NewNode(
169 simplified()->CheckNumber(feedback_source), value, *effect, control);
170 return number;
171}
172
174 Node* value, Effect* effect, Control control,
175 FeedbackSource feedback_source) {
176 Node* number = *effect =
177 graph()->NewNode(simplified()->CheckNumberFitsInt32(feedback_source),
178 value, *effect, control);
179 return number;
180}
181
183 PropertyAccessInfo const& access_info, Node* lookup_start_object) {
184 OptionalJSObjectRef holder = access_info.holder();
185 if (holder.has_value()) {
186 return jsgraph()->ConstantNoHole(holder.value(), broker());
187 }
188 return lookup_start_object;
189}
190
206
208 PropertyAccessInfo const& access_info) {
211
212 InternalIndex index = access_info.dictionary_index();
213 OptionalObjectRef value = access_info.holder()->GetOwnDictionaryProperty(
214 broker(), index, dependencies());
215 if (!value) return {};
216
217 for (MapRef map : access_info.lookup_start_object_maps()) {
218 DirectHandle<Map> map_handle = map.object();
219 // Non-JSReceivers that passed AccessInfoFactory::ComputePropertyAccessInfo
220 // must have different lookup start map.
221 if (!IsJSReceiverMap(*map_handle)) {
222 // Perform the implicit ToObject for primitives here.
223 // Implemented according to ES6 section 7.3.2 GetV (V, P).
224 Tagged<JSFunction> constructor =
226 *map_handle, *broker()->target_native_context().object())
227 .value();
228 // {constructor.initial_map()} is loaded/stored with acquire-release
229 // semantics for constructors.
230 map = MakeRefAssumeMemoryFence(broker(), constructor->initial_map());
231 DCHECK(IsJSObjectMap(*map.object()));
232 }
234 map, access_info.name(), value.value(), PropertyKind::kData);
235 }
236
237 return jsgraph()->ConstantNoHole(value.value(), broker());
238}
239
241 NameRef name, PropertyAccessInfo const& access_info,
242 Node* lookup_start_object) {
243 if (!access_info.IsFastDataConstant()) return nullptr;
244
245 // First, determine if we have a constant holder to load from.
246 OptionalJSObjectRef holder = access_info.holder();
247
248 // If {access_info} has a holder, just use it.
249 if (!holder.has_value()) {
250 // Otherwise, try to match the {lookup_start_object} as a constant.
251 if (lookup_start_object->opcode() == IrOpcode::kCheckString ||
252 lookup_start_object->opcode() ==
253 IrOpcode::kCheckStringOrStringWrapper) {
254 // Bypassing Check inputs in order to allow constant folding.
255 lookup_start_object = lookup_start_object->InputAt(0);
256 }
257 HeapObjectMatcher m(lookup_start_object);
258 if (!m.HasResolvedValue() || !m.Ref(broker()).IsJSObject()) return nullptr;
259
260 // Let us make sure the actual map of the constant lookup_start_object is
261 // among the maps in {access_info}.
262 MapRef lookup_start_object_map = m.Ref(broker()).map(broker());
263 if (std::find_if(access_info.lookup_start_object_maps().begin(),
264 access_info.lookup_start_object_maps().end(),
265 [&](MapRef map) {
266 return map.equals(lookup_start_object_map);
267 }) == access_info.lookup_start_object_maps().end()) {
268 // The map of the lookup_start_object is not in the feedback, let us bail
269 // out.
270 return nullptr;
271 }
272 holder = m.Ref(broker()).AsJSObject();
273 }
274
275 if (access_info.field_representation().IsDouble()) {
276 std::optional<Float64> value = holder->GetOwnFastConstantDoubleProperty(
277 broker(), access_info.field_index(), dependencies());
278 return value.has_value() ? jsgraph()->ConstantNoHole(value->get_scalar())
279 : nullptr;
280 }
281 OptionalObjectRef value = holder->GetOwnFastConstantDataProperty(
282 broker(), access_info.field_representation(), access_info.field_index(),
283 dependencies());
284 return value.has_value() ? jsgraph()->ConstantNoHole(*value, broker())
285 : nullptr;
286}
287
289 FieldAccess&& field_access,
290 bool is_inobject, Node** effect,
291 Node** control) {
292 Node* storage = holder;
293 if (!is_inobject) {
294 storage = *effect = graph()->NewNode(
295 simplified()->LoadField(
297 storage, *effect, *control);
298 }
299 if (field_access.machine_type.representation() ==
301 if (dependencies() == nullptr) {
302 FieldAccess const storage_access = {kTaggedBase,
303 field_access.offset,
304 name.object(),
305 OptionalMapRef(),
306 Type::Any(),
309 "BuildLoadDataField",
310 field_access.const_field_info};
311 storage = *effect = graph()->NewNode(
312 simplified()->LoadField(storage_access), storage, *effect, *control);
313 // We expect the loaded value to be a heap number here. With
314 // in-place field representation changes it is possible this is a
315 // no longer a heap number without map transitions. If we haven't taken
316 // a dependency on field representation, we should verify the loaded
317 // value is a heap number.
318 storage = *effect = graph()->NewNode(simplified()->CheckHeapObject(),
319 storage, *effect, *control);
320 Node* map = *effect =
322 storage, *effect, *control);
323 Node* is_heap_number =
324 graph()->NewNode(simplified()->ReferenceEqual(), map,
325 jsgraph()->HeapNumberMapConstant());
326 *effect = graph()->NewNode(
327 simplified()->CheckIf(DeoptimizeReason::kNotAHeapNumber),
328 is_heap_number, *effect, *control);
329 } else {
330 FieldAccess const storage_access = {kTaggedBase,
331 field_access.offset,
332 name.object(),
333 OptionalMapRef(),
334 Type::OtherInternal(),
337 "BuildLoadDataField",
338 field_access.const_field_info};
339 storage = *effect = graph()->NewNode(
340 simplified()->LoadField(storage_access), storage, *effect, *control);
341 }
342 FieldAccess value_field_access = AccessBuilder::ForHeapNumberValue();
343 value_field_access.const_field_info = field_access.const_field_info;
344 field_access = value_field_access;
345 }
346 Node* value = *effect = graph()->NewNode(
347 simplified()->LoadField(field_access), storage, *effect, *control);
348 return value;
349}
350
352 NameRef name, PropertyAccessInfo const& access_info,
353 Node* lookup_start_object, Node** effect, Node** control) {
354 DCHECK(access_info.IsDataField() || access_info.IsFastDataConstant());
355
356 if (Node* value = TryFoldLoadConstantDataField(name, access_info,
357 lookup_start_object)) {
358 return value;
359 }
360
361 MachineRepresentation const field_representation =
363 Node* storage = ResolveHolder(access_info, lookup_start_object);
364
365 FieldAccess field_access = {
367 access_info.field_index().offset(),
368 name.object(),
369 OptionalMapRef(),
370 access_info.field_type(),
371 MachineType::TypeForRepresentation(field_representation),
373 "BuildLoadDataField",
374 access_info.GetConstFieldInfo()};
375 if (field_representation == MachineRepresentation::kTaggedPointer ||
376 field_representation == MachineRepresentation::kCompressedPointer) {
377 // Remember the map of the field value, if its map is stable. This is
378 // used by the LoadElimination to eliminate map checks on the result.
379 OptionalMapRef field_map = access_info.field_map();
380 if (field_map.has_value()) {
381 if (field_map->is_stable()) {
382 dependencies()->DependOnStableMap(field_map.value());
383 field_access.map = field_map;
384 field_access.type = Type::For(*field_map, broker());
385 }
386 }
387 }
388 return BuildLoadDataField(name, storage, std::move(field_access),
389 access_info.field_index().is_inobject(), effect,
390 control);
391}
392
393} // namespace compiler
394} // namespace internal
395} // namespace v8
static MachineType TypeForRepresentation(const MachineRepresentation &rep, bool isSigned=true)
static constexpr MachineType AnyTagged()
static constexpr MachineType TaggedPointer()
static std::optional< Tagged< JSFunction > > GetConstructorFunction(Tagged< Map > map, Tagged< Context > native_context)
Definition map.cc:53
constexpr Kind kind() const
constexpr bool IsDouble() const
void insert(T handle, Zone *zone)
static FieldAccess ForMap(WriteBarrierKind write_barrier=kMapWriteBarrier)
static FieldAccess ForJSObjectPropertiesOrHashKnownPointer()
void DependOnConstantInDictionaryPrototypeChain(MapRef receiver_map, NameRef property_name, ObjectRef constant, PropertyKind kind)
V8_EXPORT_PRIVATE MapRef map(JSHeapBroker *broker) const
SimplifiedOperatorBuilder * simplified() const
Definition js-graph.h:105
Isolate * isolate() const
Definition js-graph.h:106
Node * ConstantNoHole(ObjectRef ref, JSHeapBroker *broker)
Definition js-graph.cc:51
CommonOperatorBuilder * common() const
constexpr IrOpcode::Value opcode() const
Definition node.h:52
Node * InputAt(int index) const
Definition node.h:70
Node * TryFoldLoadConstantDataField(NameRef name, PropertyAccessInfo const &access_info, Node *lookup_start_object)
static MachineRepresentation ConvertRepresentation(Representation representation)
Node * ResolveHolder(PropertyAccessInfo const &access_info, Node *lookup_start_object)
Node * BuildCheckNumberFitsInt32(Node *value, Effect *effect, Control control, FeedbackSource feedback_source=FeedbackSource())
bool TryBuildNumberCheck(JSHeapBroker *broker, ZoneVector< MapRef > const &maps, Node **receiver, Effect *effect, Control control)
Node * BuildCheckValue(Node *receiver, Effect *effect, Control control, ObjectRef value)
Node * BuildCheckSmi(Node *value, Effect *effect, Control control, FeedbackSource feedback_source=FeedbackSource())
Node * BuildCheckNumber(Node *value, Effect *effect, Control control, FeedbackSource feedback_source=FeedbackSource())
bool TryBuildStringCheck(JSHeapBroker *broker, ZoneVector< MapRef > const &maps, Node **receiver, Effect *effect, Control control)
void BuildCheckMaps(Node *object, Effect *effect, Control control, ZoneVector< MapRef > const &maps, bool has_deprecated_map_without_migration_target=false)
std::optional< Node * > FoldLoadDictPrototypeConstant(PropertyAccessInfo const &access_info)
Node * BuildLoadDataField(NameRef name, PropertyAccessInfo const &access_info, Node *lookup_start_object, Node **effect, Node **control)
Representation field_representation() const
OptionalJSObjectRef holder() const
ZoneVector< MapRef > const & lookup_start_object_maps() const
Node * NewNode(const Operator *op, int input_count, Node *const *inputs, bool incomplete=false)
static Type For(MapRef type, JSHeapBroker *broker)
#define V8_DICT_PROPERTY_CONST_TRACKING_BOOL
Definition globals.h:249
JSHeapBroker * broker
TNode< Object > receiver
int m
Definition mul-fft.cc:294
bool HasOnlyNonResizableTypedArrayMaps(JSHeapBroker *broker, ZoneVector< MapRef > const &maps)
bool HasOnlyStringMaps(JSHeapBroker *broker, ZoneVector< MapRef > const &maps)
bool HasOnlyStringWrapperMaps(JSHeapBroker *broker, ZoneVector< MapRef > const &maps)
ref_traits< T >::ref_type MakeRefAssumeMemoryFence(JSHeapBroker *broker, Tagged< T > object)
bool IsRabGsabTypedArrayElementsKind(ElementsKind kind)
@ SLOW_STRING_WRAPPER_ELEMENTS
@ FAST_STRING_WRAPPER_ELEMENTS
return value
Definition map-inl.h:893
#define DCHECK(condition)
Definition logging.h:482