Line data Source code
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/lookup.h"
14 : #include "src/objects/heap-number.h"
15 :
16 : #include "src/field-index-inl.h"
17 : #include "src/isolate-inl.h"
18 :
19 : namespace v8 {
20 : namespace internal {
21 : namespace compiler {
22 :
23 0 : Graph* PropertyAccessBuilder::graph() const { return jsgraph()->graph(); }
24 :
25 0 : Isolate* PropertyAccessBuilder::isolate() const { return jsgraph()->isolate(); }
26 :
27 0 : CommonOperatorBuilder* PropertyAccessBuilder::common() const {
28 0 : return jsgraph()->common();
29 : }
30 :
31 0 : SimplifiedOperatorBuilder* PropertyAccessBuilder::simplified() const {
32 0 : return jsgraph()->simplified();
33 : }
34 :
35 155435 : bool HasOnlyStringMaps(JSHeapBroker* broker, MapHandles const& maps) {
36 163831 : for (auto map : maps) {
37 : MapRef map_ref(broker, map);
38 155993 : if (!map_ref.IsStringMap()) return false;
39 : }
40 : return true;
41 : }
42 :
43 : namespace {
44 :
45 113007 : bool HasOnlyNumberMaps(JSHeapBroker* broker, MapHandles const& maps) {
46 114300 : for (auto map : maps) {
47 : MapRef map_ref(broker, map);
48 113007 : if (map_ref.instance_type() != HEAP_NUMBER_TYPE) return false;
49 : }
50 : return true;
51 : }
52 :
53 : } // namespace
54 :
55 120374 : bool PropertyAccessBuilder::TryBuildStringCheck(JSHeapBroker* broker,
56 : MapHandles const& maps,
57 : Node** receiver, Node** effect,
58 : Node* control) {
59 120374 : if (HasOnlyStringMaps(broker, maps)) {
60 : // Monormorphic string access (ignoring the fact that there are multiple
61 : // String maps).
62 : *receiver = *effect =
63 14734 : graph()->NewNode(simplified()->CheckString(VectorSlotPair()), *receiver,
64 7367 : *effect, control);
65 7367 : return true;
66 : }
67 : return false;
68 : }
69 :
70 113007 : bool PropertyAccessBuilder::TryBuildNumberCheck(JSHeapBroker* broker,
71 : MapHandles const& maps,
72 : Node** receiver, Node** effect,
73 : Node* control) {
74 113007 : if (HasOnlyNumberMaps(broker, maps)) {
75 : // Monomorphic number access (we also deal with Smis here).
76 : *receiver = *effect =
77 2586 : graph()->NewNode(simplified()->CheckNumber(VectorSlotPair()), *receiver,
78 1293 : *effect, control);
79 1293 : return true;
80 : }
81 : return false;
82 : }
83 :
84 : namespace {
85 :
86 145012 : bool NeedsCheckHeapObject(Node* receiver) {
87 145012 : switch (receiver->opcode()) {
88 : case IrOpcode::kConvertReceiver:
89 : case IrOpcode::kHeapConstant:
90 : case IrOpcode::kJSCloneObject:
91 : case IrOpcode::kJSConstruct:
92 : case IrOpcode::kJSConstructForwardVarargs:
93 : case IrOpcode::kJSConstructWithArrayLike:
94 : case IrOpcode::kJSConstructWithSpread:
95 : case IrOpcode::kJSCreate:
96 : case IrOpcode::kJSCreateArguments:
97 : case IrOpcode::kJSCreateArray:
98 : case IrOpcode::kJSCreateArrayFromIterable:
99 : case IrOpcode::kJSCreateArrayIterator:
100 : case IrOpcode::kJSCreateAsyncFunctionObject:
101 : case IrOpcode::kJSCreateBoundFunction:
102 : case IrOpcode::kJSCreateClosure:
103 : case IrOpcode::kJSCreateCollectionIterator:
104 : case IrOpcode::kJSCreateEmptyLiteralArray:
105 : case IrOpcode::kJSCreateEmptyLiteralObject:
106 : case IrOpcode::kJSCreateGeneratorObject:
107 : case IrOpcode::kJSCreateIterResultObject:
108 : case IrOpcode::kJSCreateKeyValueArray:
109 : case IrOpcode::kJSCreateLiteralArray:
110 : case IrOpcode::kJSCreateLiteralObject:
111 : case IrOpcode::kJSCreateLiteralRegExp:
112 : case IrOpcode::kJSCreateObject:
113 : case IrOpcode::kJSCreatePromise:
114 : case IrOpcode::kJSCreateStringIterator:
115 : case IrOpcode::kJSCreateTypedArray:
116 : case IrOpcode::kJSGetSuperConstructor:
117 : case IrOpcode::kJSToName:
118 : case IrOpcode::kJSToObject:
119 : case IrOpcode::kJSToString:
120 : case IrOpcode::kTypeOf:
121 : return false;
122 : case IrOpcode::kPhi: {
123 1938 : Node* control = NodeProperties::GetControlInput(receiver);
124 1938 : if (control->opcode() != IrOpcode::kMerge) return true;
125 3774 : for (int i = 0; i < receiver->InputCount() - 1; ++i) {
126 2503 : if (NeedsCheckHeapObject(receiver->InputAt(i))) return true;
127 : }
128 : return false;
129 : }
130 : default:
131 47924 : return true;
132 : }
133 : }
134 :
135 : } // namespace
136 :
137 142509 : Node* PropertyAccessBuilder::BuildCheckHeapObject(Node* receiver, Node** effect,
138 : Node* control) {
139 142509 : if (NeedsCheckHeapObject(receiver)) {
140 48316 : receiver = *effect = graph()->NewNode(simplified()->CheckHeapObject(),
141 48316 : receiver, *effect, control);
142 : }
143 142509 : return receiver;
144 : }
145 :
146 140245 : void PropertyAccessBuilder::BuildCheckMaps(Node* receiver, Node** effect,
147 : Node* control,
148 : MapHandles const& receiver_maps) {
149 : HeapObjectMatcher m(receiver);
150 140245 : if (m.HasValue()) {
151 60183 : MapRef receiver_map = m.Ref(broker()).map();
152 60183 : if (receiver_map.is_stable()) {
153 55118 : for (Handle<Map> map : receiver_maps) {
154 55118 : if (MapRef(broker(), map).equals(receiver_map)) {
155 55118 : dependencies()->DependOnStableMap(receiver_map);
156 : return;
157 : }
158 : }
159 : }
160 : }
161 : ZoneHandleSet<Map> maps;
162 : CheckMapsFlags flags = CheckMapsFlag::kNone;
163 177651 : for (Handle<Map> map : receiver_maps) {
164 : MapRef receiver_map(broker(), map);
165 92524 : maps.insert(receiver_map.object(), graph()->zone());
166 92524 : if (receiver_map.is_migration_target()) {
167 : flags |= CheckMapsFlag::kTryMigrateInstance;
168 : }
169 : }
170 170254 : *effect = graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver,
171 85127 : *effect, control);
172 : }
173 :
174 954 : Node* PropertyAccessBuilder::BuildCheckValue(Node* receiver, Node** effect,
175 : Node* control,
176 : Handle<HeapObject> value) {
177 : HeapObjectMatcher m(receiver);
178 954 : if (m.Is(value)) return receiver;
179 29 : Node* expected = jsgraph()->HeapConstant(value);
180 : Node* check =
181 29 : graph()->NewNode(simplified()->ReferenceEqual(), receiver, expected);
182 : *effect =
183 58 : graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongValue),
184 29 : check, *effect, control);
185 29 : return expected;
186 : }
187 :
188 0 : Node* PropertyAccessBuilder::ResolveHolder(
189 : PropertyAccessInfo const& access_info, Node* receiver) {
190 : Handle<JSObject> holder;
191 25062 : if (access_info.holder().ToHandle(&holder)) {
192 1452 : return jsgraph()->Constant(holder);
193 : }
194 : return receiver;
195 : }
196 :
197 95773 : Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
198 : NameRef const& name, PropertyAccessInfo const& access_info,
199 : Node* receiver) {
200 : // Optimize immutable property loads.
201 :
202 : // First, determine if we have a constant holder to load from.
203 : Handle<JSObject> holder;
204 : // If {access_info} has a holder, just use it.
205 95773 : if (!access_info.holder().ToHandle(&holder)) {
206 : // Otherwise, try to match the {receiver} as a constant.
207 : HeapObjectMatcher m(receiver);
208 144635 : if (!m.HasValue() || !m.Ref(broker()).IsJSObject()) return nullptr;
209 :
210 : // Let us make sure the actual map of the constant receiver is among
211 : // the maps in {access_info}.
212 48970 : MapRef receiver_map = m.Ref(broker()).map();
213 48970 : if (std::find_if(access_info.receiver_maps().begin(),
214 : access_info.receiver_maps().end(), [&](Handle<Map> map) {
215 48970 : return map.address() == receiver_map.object().address();
216 : }) == access_info.receiver_maps().end()) {
217 : // The map of the receiver is not in the feedback, let us bail out.
218 : return nullptr;
219 : }
220 : holder = Handle<JSObject>::cast(m.Value());
221 : }
222 :
223 : // TODO(ishell): Use something simpler like
224 : //
225 : // Handle<Object> value =
226 : // JSObject::FastPropertyAt(Handle<JSObject>::cast(m.Value()),
227 : // Representation::Tagged(), field_index);
228 : //
229 : // here, once we have the immutable bit in the access_info.
230 :
231 : // TODO(turbofan): Given that we already have the field_index here, we
232 : // might be smarter in the future and not rely on the LookupIterator.
233 : LookupIterator it(isolate(), holder, name.object(),
234 144844 : LookupIterator::OWN_SKIP_INTERCEPTOR);
235 72422 : if (it.state() == LookupIterator::DATA) {
236 73045 : bool is_readonly_non_configurable = it.IsReadOnly() && !it.IsConfigurable();
237 72290 : if (is_readonly_non_configurable ||
238 : (FLAG_track_constant_fields && access_info.IsDataConstantField())) {
239 70711 : Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
240 70711 : if (!is_readonly_non_configurable) {
241 : // It's necessary to add dependency on the map that introduced
242 : // the field.
243 : DCHECK(access_info.IsDataConstantField());
244 : DCHECK(!it.is_dictionary_holder());
245 : MapRef map(broker(),
246 : handle(it.GetHolder<HeapObject>()->map(), isolate()));
247 69970 : map.SerializeOwnDescriptors(); // TODO(neis): Remove later.
248 69970 : if (dependencies()->DependOnFieldConstness(
249 : map, it.GetFieldDescriptorIndex()) !=
250 : PropertyConstness::kConst) {
251 0 : return nullptr;
252 : }
253 : }
254 : return value;
255 : }
256 : }
257 : return nullptr;
258 : }
259 :
260 95773 : Node* PropertyAccessBuilder::BuildLoadDataField(
261 : NameRef const& name, PropertyAccessInfo const& access_info, Node* receiver,
262 : Node** effect, Node** control) {
263 : DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
264 95773 : if (Node* value =
265 95773 : TryBuildLoadConstantDataField(name, access_info, receiver)) {
266 : return value;
267 : }
268 :
269 : FieldIndex const field_index = access_info.field_index();
270 : Type const field_type = access_info.field_type();
271 : MachineRepresentation const field_representation =
272 25062 : access_info.field_representation();
273 : Node* storage = ResolveHolder(access_info, receiver);
274 25062 : if (!field_index.is_inobject()) {
275 1296 : storage = *effect = graph()->NewNode(
276 1296 : simplified()->LoadField(AccessBuilder::ForJSObjectPropertiesOrHash()),
277 648 : storage, *effect, *control);
278 : }
279 : FieldAccess field_access = {
280 : kTaggedBase,
281 : field_index.offset(),
282 : name.object(),
283 : MaybeHandle<Map>(),
284 : field_type,
285 : MachineType::TypeForRepresentation(field_representation),
286 : kFullWriteBarrier,
287 25062 : LoadSensitivity::kCritical};
288 25062 : if (field_representation == MachineRepresentation::kFloat64) {
289 663 : if (!field_index.is_inobject() || field_index.is_hidden_field() ||
290 : !FLAG_unbox_double_fields) {
291 : FieldAccess const storage_access = {
292 : kTaggedBase, field_index.offset(),
293 : name.object(), MaybeHandle<Map>(),
294 : Type::OtherInternal(), MachineType::TaggedPointer(),
295 37 : kPointerWriteBarrier, LoadSensitivity::kCritical};
296 37 : storage = *effect = graph()->NewNode(
297 37 : simplified()->LoadField(storage_access), storage, *effect, *control);
298 37 : field_access.offset = HeapNumber::kValueOffset;
299 37 : field_access.name = MaybeHandle<Name>();
300 : }
301 24712 : } else if (field_representation == MachineRepresentation::kTaggedPointer ||
302 : field_representation ==
303 : 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 : Handle<Map> field_map;
307 7580 : if (access_info.field_map().ToHandle(&field_map)) {
308 : MapRef field_map_ref(broker(), field_map);
309 2386 : if (field_map_ref.is_stable()) {
310 2382 : dependencies()->DependOnStableMap(field_map_ref);
311 2382 : field_access.map = field_map;
312 : }
313 : }
314 : }
315 25062 : Node* value = *effect = graph()->NewNode(
316 25062 : simplified()->LoadField(field_access), storage, *effect, *control);
317 25062 : return value;
318 : }
319 :
320 : } // namespace compiler
321 : } // namespace internal
322 122036 : } // namespace v8
|