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