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
5 #include "src/compiler/property-access-builder.h"
6
7 #include "src/base/optional.h"
8 #include "src/compiler/access-builder.h"
9 #include "src/compiler/access-info.h"
10 #include "src/compiler/compilation-dependencies.h"
11 #include "src/compiler/js-graph.h"
12 #include "src/compiler/node-matchers.h"
13 #include "src/compiler/simplified-operator.h"
14 #include "src/execution/isolate-inl.h"
15 #include "src/objects/field-index-inl.h"
16 #include "src/objects/heap-number.h"
17 #include "src/objects/internal-index.h"
18 #include "src/objects/lookup.h"
19 #include "src/objects/property-details.h"
20
21 namespace v8 {
22 namespace internal {
23 namespace compiler {
24
graph() const25 Graph* PropertyAccessBuilder::graph() const { return jsgraph()->graph(); }
26
isolate() const27 Isolate* PropertyAccessBuilder::isolate() const { return jsgraph()->isolate(); }
28
common() const29 CommonOperatorBuilder* PropertyAccessBuilder::common() const {
30 return jsgraph()->common();
31 }
32
simplified() const33 SimplifiedOperatorBuilder* PropertyAccessBuilder::simplified() const {
34 return jsgraph()->simplified();
35 }
36
HasOnlyStringMaps(JSHeapBroker* broker, ZoneVector<MapRef> const& maps)37 bool HasOnlyStringMaps(JSHeapBroker* broker, ZoneVector<MapRef> const& maps) {
38 for (MapRef map : maps) {
39 if (!map.IsStringMap()) return false;
40 }
41 return true;
42 }
43
44 namespace {
45
HasOnlyNumberMaps(JSHeapBroker* broker, ZoneVector<MapRef> const& maps)46 bool HasOnlyNumberMaps(JSHeapBroker* broker, ZoneVector<MapRef> const& maps) {
47 for (MapRef map : maps) {
48 if (map.instance_type() != HEAP_NUMBER_TYPE) return false;
49 }
50 return true;
51 }
52
53 } // namespace
54
TryBuildStringCheck(JSHeapBroker* broker, ZoneVector<MapRef> const& maps, Node** receiver, Effect* effect, Control control)55 bool PropertyAccessBuilder::TryBuildStringCheck(JSHeapBroker* broker,
56 ZoneVector<MapRef> const& maps,
57 Node** receiver, Effect* effect,
58 Control control) {
59 if (HasOnlyStringMaps(broker, maps)) {
60 // Monormorphic string access (ignoring the fact that there are multiple
61 // String maps).
62 *receiver = *effect =
63 graph()->NewNode(simplified()->CheckString(FeedbackSource()), *receiver,
64 *effect, control);
65 return true;
66 }
67 return false;
68 }
69
TryBuildNumberCheck(JSHeapBroker* broker, ZoneVector<MapRef> const& maps, Node** receiver, Effect* effect, Control control)70 bool PropertyAccessBuilder::TryBuildNumberCheck(JSHeapBroker* broker,
71 ZoneVector<MapRef> const& maps,
72 Node** receiver, Effect* effect,
73 Control control) {
74 if (HasOnlyNumberMaps(broker, maps)) {
75 // Monomorphic number access (we also deal with Smis here).
76 *receiver = *effect =
77 graph()->NewNode(simplified()->CheckNumber(FeedbackSource()), *receiver,
78 *effect, control);
79 return true;
80 }
81 return false;
82 }
83
BuildCheckMaps(Node* object, Effect* effect, Control control, ZoneVector<MapRef> const& maps)84 void PropertyAccessBuilder::BuildCheckMaps(Node* object, Effect* effect,
85 Control control,
86 ZoneVector<MapRef> const& maps) {
87 HeapObjectMatcher m(object);
88 if (m.HasResolvedValue()) {
89 MapRef object_map = m.Ref(broker()).map();
90 if (object_map.is_stable()) {
91 for (MapRef map : maps) {
92 if (map.equals(object_map)) {
93 dependencies()->DependOnStableMap(object_map);
94 return;
95 }
96 }
97 }
98 }
99 ZoneHandleSet<Map> map_set;
100 CheckMapsFlags flags = CheckMapsFlag::kNone;
101 for (MapRef map : maps) {
102 map_set.insert(map.object(), graph()->zone());
103 if (map.is_migration_target()) {
104 flags |= CheckMapsFlag::kTryMigrateInstance;
105 }
106 }
107 *effect = graph()->NewNode(simplified()->CheckMaps(flags, map_set), object,
108 *effect, control);
109 }
110
BuildCheckValue(Node* receiver, Effect* effect, Control control, Handle<HeapObject> value)111 Node* PropertyAccessBuilder::BuildCheckValue(Node* receiver, Effect* effect,
112 Control control,
113 Handle<HeapObject> value) {
114 HeapObjectMatcher m(receiver);
115 if (m.Is(value)) return receiver;
116 Node* expected = jsgraph()->HeapConstant(value);
117 Node* check =
118 graph()->NewNode(simplified()->ReferenceEqual(), receiver, expected);
119 *effect =
120 graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongValue),
121 check, *effect, control);
122 return expected;
123 }
124
ResolveHolder( PropertyAccessInfo const& access_info, Node* lookup_start_object)125 Node* PropertyAccessBuilder::ResolveHolder(
126 PropertyAccessInfo const& access_info, Node* lookup_start_object) {
127 base::Optional<JSObjectRef> holder = access_info.holder();
128 if (holder.has_value()) {
129 return jsgraph()->Constant(holder.value());
130 }
131 return lookup_start_object;
132 }
133
ConvertRepresentation( Representation representation)134 MachineRepresentation PropertyAccessBuilder::ConvertRepresentation(
135 Representation representation) {
136 switch (representation.kind()) {
137 case Representation::kSmi:
138 return MachineRepresentation::kTaggedSigned;
139 case Representation::kDouble:
140 return MachineRepresentation::kFloat64;
141 case Representation::kHeapObject:
142 return MachineRepresentation::kTaggedPointer;
143 case Representation::kTagged:
144 return MachineRepresentation::kTagged;
145 default:
146 UNREACHABLE();
147 }
148 }
149
FoldLoadDictPrototypeConstant( PropertyAccessInfo const& access_info)150 base::Optional<Node*> PropertyAccessBuilder::FoldLoadDictPrototypeConstant(
151 PropertyAccessInfo const& access_info) {
152 DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL);
153 DCHECK(access_info.IsDictionaryProtoDataConstant());
154
155 InternalIndex index = access_info.dictionary_index();
156 base::Optional<ObjectRef> value =
157 access_info.holder()->GetOwnDictionaryProperty(index, dependencies());
158 if (!value) return {};
159
160 for (MapRef map : access_info.lookup_start_object_maps()) {
161 Handle<Map> map_handle = map.object();
162 // Non-JSReceivers that passed AccessInfoFactory::ComputePropertyAccessInfo
163 // must have different lookup start map.
164 if (!map_handle->IsJSReceiverMap()) {
165 // Perform the implicit ToObject for primitives here.
166 // Implemented according to ES6 section 7.3.2 GetV (V, P).
167 JSFunction constructor =
168 Map::GetConstructorFunction(
169 *map_handle, *broker()->target_native_context().object())
170 .value();
171 // {constructor.initial_map()} is loaded/stored with acquire-release
172 // semantics for constructors.
173 map = MakeRefAssumeMemoryFence(broker(), constructor.initial_map());
174 DCHECK(map.object()->IsJSObjectMap());
175 }
176 dependencies()->DependOnConstantInDictionaryPrototypeChain(
177 map, access_info.name(), value.value(), PropertyKind::kData);
178 }
179
180 return jsgraph()->Constant(value.value());
181 }
182
TryFoldLoadConstantDataField( NameRef const& name, PropertyAccessInfo const& access_info, Node* lookup_start_object)183 Node* PropertyAccessBuilder::TryFoldLoadConstantDataField(
184 NameRef const& name, PropertyAccessInfo const& access_info,
185 Node* lookup_start_object) {
186 if (!access_info.IsFastDataConstant()) return nullptr;
187
188 // First, determine if we have a constant holder to load from.
189 base::Optional<JSObjectRef> holder = access_info.holder();
190
191 // If {access_info} has a holder, just use it.
192 if (!holder.has_value()) {
193 // Otherwise, try to match the {lookup_start_object} as a constant.
194 HeapObjectMatcher m(lookup_start_object);
195 if (!m.HasResolvedValue() || !m.Ref(broker()).IsJSObject()) return nullptr;
196
197 // Let us make sure the actual map of the constant lookup_start_object is
198 // among the maps in {access_info}.
199 MapRef lookup_start_object_map = m.Ref(broker()).map();
200 if (std::find_if(access_info.lookup_start_object_maps().begin(),
201 access_info.lookup_start_object_maps().end(),
202 [&](MapRef map) {
203 return map.equals(lookup_start_object_map);
204 }) == access_info.lookup_start_object_maps().end()) {
205 // The map of the lookup_start_object is not in the feedback, let us bail
206 // out.
207 return nullptr;
208 }
209 holder = m.Ref(broker()).AsJSObject();
210 }
211
212 base::Optional<ObjectRef> value =
213 holder->GetOwnFastDataProperty(access_info.field_representation(),
214 access_info.field_index(), dependencies());
215 return value.has_value() ? jsgraph()->Constant(*value) : nullptr;
216 }
217
BuildLoadDataField(NameRef const& name, Node* holder, FieldAccess& field_access, bool is_inobject, Node** effect, Node** control)218 Node* PropertyAccessBuilder::BuildLoadDataField(NameRef const& name,
219 Node* holder,
220 FieldAccess& field_access,
221 bool is_inobject, Node** effect,
222 Node** control) {
223 Node* storage = holder;
224 if (!is_inobject) {
225 storage = *effect = graph()->NewNode(
226 simplified()->LoadField(
227 AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer()),
228 storage, *effect, *control);
229 }
230 if (field_access.machine_type.representation() ==
231 MachineRepresentation::kFloat64) {
232 if (dependencies() == nullptr) {
233 FieldAccess const storage_access = {kTaggedBase,
234 field_access.offset,
235 name.object(),
236 MaybeHandle<Map>(),
237 Type::Any(),
238 MachineType::AnyTagged(),
239 kPointerWriteBarrier,
240 field_access.const_field_info};
241 storage = *effect = graph()->NewNode(
242 simplified()->LoadField(storage_access), storage, *effect, *control);
243 // We expect the loaded value to be a heap number here. With
244 // in-place field representation changes it is possible this is a
245 // no longer a heap number without map transitions. If we haven't taken
246 // a dependency on field representation, we should verify the loaded
247 // value is a heap number.
248 storage = *effect = graph()->NewNode(simplified()->CheckHeapObject(),
249 storage, *effect, *control);
250 Node* map = *effect =
251 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
252 storage, *effect, *control);
253 Node* is_heap_number =
254 graph()->NewNode(simplified()->ReferenceEqual(), map,
255 jsgraph()->HeapNumberMapConstant());
256 *effect = graph()->NewNode(
257 simplified()->CheckIf(DeoptimizeReason::kNotAHeapNumber),
258 is_heap_number, *effect, *control);
259 } else {
260 FieldAccess const storage_access = {kTaggedBase,
261 field_access.offset,
262 name.object(),
263 MaybeHandle<Map>(),
264 Type::OtherInternal(),
265 MachineType::TaggedPointer(),
266 kPointerWriteBarrier,
267 field_access.const_field_info};
268 storage = *effect = graph()->NewNode(
269 simplified()->LoadField(storage_access), storage, *effect, *control);
270 }
271 field_access.offset = HeapNumber::kValueOffset;
272 field_access.name = MaybeHandle<Name>();
273 }
274 Node* value = *effect = graph()->NewNode(
275 simplified()->LoadField(field_access), storage, *effect, *control);
276 return value;
277 }
278
BuildLoadDataField( NameRef const& name, PropertyAccessInfo const& access_info, Node* lookup_start_object, Node** effect, Node** control)279 Node* PropertyAccessBuilder::BuildLoadDataField(
280 NameRef const& name, PropertyAccessInfo const& access_info,
281 Node* lookup_start_object, Node** effect, Node** control) {
282 DCHECK(access_info.IsDataField() || access_info.IsFastDataConstant());
283
284 if (Node* value = TryFoldLoadConstantDataField(name, access_info,
285 lookup_start_object)) {
286 return value;
287 }
288
289 MachineRepresentation const field_representation =
290 ConvertRepresentation(access_info.field_representation());
291 Node* storage = ResolveHolder(access_info, lookup_start_object);
292
293 FieldAccess field_access = {
294 kTaggedBase,
295 access_info.field_index().offset(),
296 name.object(),
297 MaybeHandle<Map>(),
298 access_info.field_type(),
299 MachineType::TypeForRepresentation(field_representation),
300 kFullWriteBarrier,
301 access_info.GetConstFieldInfo()};
302 if (field_representation == MachineRepresentation::kTaggedPointer ||
303 field_representation == MachineRepresentation::kCompressedPointer) {
304 // Remember the map of the field value, if its map is stable. This is
305 // used by the LoadElimination to eliminate map checks on the result.
306 base::Optional<MapRef> field_map = access_info.field_map();
307 if (field_map.has_value()) {
308 if (field_map->is_stable()) {
309 dependencies()->DependOnStableMap(field_map.value());
310 field_access.map = field_map->object();
311 }
312 }
313 }
314 return BuildLoadDataField(name, storage, field_access,
315 access_info.field_index().is_inobject(), effect,
316 control);
317 }
318
319 } // namespace compiler
320 } // namespace internal
321 } // namespace v8
322