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 265717 : Graph* PropertyAccessBuilder::graph() const { return jsgraph()->graph(); }
24 :
25 60515 : Isolate* PropertyAccessBuilder::isolate() const { return jsgraph()->isolate(); }
26 :
27 0 : CommonOperatorBuilder* PropertyAccessBuilder::common() const {
28 0 : return jsgraph()->common();
29 : }
30 :
31 170729 : SimplifiedOperatorBuilder* PropertyAccessBuilder::simplified() const {
32 170729 : return jsgraph()->simplified();
33 : }
34 :
35 154128 : bool HasOnlyStringMaps(MapHandles const& maps) {
36 317039 : for (auto map : maps) {
37 154891 : if (!map->IsStringMap()) return false;
38 : }
39 : return true;
40 : }
41 :
42 : namespace {
43 :
44 115726 : bool HasOnlyNumberMaps(MapHandles const& maps) {
45 233018 : for (auto map : maps) {
46 115734 : if (map->instance_type() != HEAP_NUMBER_TYPE) return false;
47 : }
48 : return true;
49 : }
50 :
51 : } // namespace
52 :
53 123277 : bool PropertyAccessBuilder::TryBuildStringCheck(MapHandles const& maps,
54 : Node** receiver, Node** effect,
55 : Node* control) {
56 123277 : if (HasOnlyStringMaps(maps)) {
57 : // Monormorphic string access (ignoring the fact that there are multiple
58 : // String maps).
59 : *receiver = *effect =
60 : graph()->NewNode(simplified()->CheckString(VectorSlotPair()), *receiver,
61 22653 : *effect, control);
62 7551 : return true;
63 : }
64 : return false;
65 : }
66 :
67 115726 : bool PropertyAccessBuilder::TryBuildNumberCheck(MapHandles const& maps,
68 : Node** receiver, Node** effect,
69 : Node* control) {
70 115726 : if (HasOnlyNumberMaps(maps)) {
71 : // Monomorphic number access (we also deal with Smis here).
72 : *receiver = *effect =
73 : graph()->NewNode(simplified()->CheckNumber(VectorSlotPair()), *receiver,
74 4674 : *effect, control);
75 1558 : return true;
76 : }
77 : return false;
78 : }
79 :
80 : namespace {
81 :
82 145561 : bool NeedsCheckHeapObject(Node* receiver) {
83 145561 : switch (receiver->opcode()) {
84 : case IrOpcode::kConvertReceiver:
85 : case IrOpcode::kHeapConstant:
86 : case IrOpcode::kJSCloneObject:
87 : case IrOpcode::kJSConstruct:
88 : case IrOpcode::kJSConstructForwardVarargs:
89 : case IrOpcode::kJSConstructWithArrayLike:
90 : case IrOpcode::kJSConstructWithSpread:
91 : case IrOpcode::kJSCreate:
92 : case IrOpcode::kJSCreateArguments:
93 : case IrOpcode::kJSCreateArray:
94 : case IrOpcode::kJSCreateArrayFromIterable:
95 : case IrOpcode::kJSCreateArrayIterator:
96 : case IrOpcode::kJSCreateAsyncFunctionObject:
97 : case IrOpcode::kJSCreateBoundFunction:
98 : case IrOpcode::kJSCreateClosure:
99 : case IrOpcode::kJSCreateCollectionIterator:
100 : case IrOpcode::kJSCreateEmptyLiteralArray:
101 : case IrOpcode::kJSCreateEmptyLiteralObject:
102 : case IrOpcode::kJSCreateGeneratorObject:
103 : case IrOpcode::kJSCreateIterResultObject:
104 : case IrOpcode::kJSCreateKeyValueArray:
105 : case IrOpcode::kJSCreateLiteralArray:
106 : case IrOpcode::kJSCreateLiteralObject:
107 : case IrOpcode::kJSCreateLiteralRegExp:
108 : case IrOpcode::kJSCreateObject:
109 : case IrOpcode::kJSCreatePromise:
110 : case IrOpcode::kJSCreateStringIterator:
111 : case IrOpcode::kJSCreateTypedArray:
112 : case IrOpcode::kJSGetSuperConstructor:
113 : case IrOpcode::kJSToName:
114 : case IrOpcode::kJSToObject:
115 : case IrOpcode::kJSToString:
116 : case IrOpcode::kTypeOf:
117 : return false;
118 : case IrOpcode::kPhi: {
119 2058 : Node* control = NodeProperties::GetControlInput(receiver);
120 2058 : if (control->opcode() != IrOpcode::kMerge) return true;
121 3696 : for (int i = 0; i < receiver->InputCount() - 1; ++i) {
122 2498 : if (NeedsCheckHeapObject(receiver->InputAt(i))) return true;
123 : }
124 : return false;
125 : }
126 : default:
127 48716 : return true;
128 : }
129 : }
130 :
131 : } // namespace
132 :
133 143063 : Node* PropertyAccessBuilder::BuildCheckHeapObject(Node* receiver, Node** effect,
134 : Node* control) {
135 143063 : if (NeedsCheckHeapObject(receiver)) {
136 : receiver = *effect = graph()->NewNode(simplified()->CheckHeapObject(),
137 147465 : receiver, *effect, control);
138 : }
139 143064 : return receiver;
140 : }
141 :
142 141241 : void PropertyAccessBuilder::BuildCheckMaps(
143 : Node* receiver, Node** effect, Node* control,
144 107482 : std::vector<Handle<Map>> const& receiver_maps) {
145 : HeapObjectMatcher m(receiver);
146 141241 : if (m.HasValue()) {
147 : Handle<Map> receiver_map(m.Value()->map(), isolate());
148 58919 : if (receiver_map->is_stable()) {
149 107482 : for (Handle<Map> map : receiver_maps) {
150 53741 : if (map.is_identical_to(receiver_map)) {
151 53741 : dependencies()->DependOnStableMap(MapRef(broker(), receiver_map));
152 141242 : return;
153 : }
154 : }
155 : }
156 : }
157 : ZoneHandleSet<Map> maps;
158 : CheckMapsFlags flags = CheckMapsFlag::kNone;
159 269988 : for (Handle<Map> map : receiver_maps) {
160 94988 : maps.insert(map, graph()->zone());
161 94988 : if (map->is_migration_target()) {
162 : flags |= CheckMapsFlag::kTryMigrateInstance;
163 : }
164 : }
165 : *effect = graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver,
166 262502 : *effect, control);
167 : }
168 :
169 1585 : Node* PropertyAccessBuilder::BuildCheckValue(Node* receiver, Node** effect,
170 : Node* control,
171 121 : Handle<HeapObject> value) {
172 : HeapObjectMatcher m(receiver);
173 1585 : if (m.Is(value)) return receiver;
174 121 : Node* expected = jsgraph()->HeapConstant(value);
175 : Node* check =
176 121 : graph()->NewNode(simplified()->ReferenceEqual(), receiver, expected);
177 : *effect =
178 : graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongValue),
179 363 : check, *effect, control);
180 121 : return expected;
181 : }
182 :
183 25169 : Node* PropertyAccessBuilder::ResolveHolder(
184 221 : PropertyAccessInfo const& access_info, Node* receiver) {
185 : Handle<JSObject> holder;
186 25169 : if (access_info.holder().ToHandle(&holder)) {
187 221 : return jsgraph()->Constant(holder);
188 : }
189 : return receiver;
190 : }
191 :
192 25168 : Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
193 727 : Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver) {
194 : // Optimize immutable property loads.
195 : HeapObjectMatcher m(receiver);
196 28360 : if (m.HasValue() && m.Value()->IsJSObject()) {
197 : // TODO(ishell): Use something simpler like
198 : //
199 : // Handle<Object> value =
200 : // JSObject::FastPropertyAt(Handle<JSObject>::cast(m.Value()),
201 : // Representation::Tagged(), field_index);
202 : //
203 : // here, once we have the immutable bit in the access_info.
204 :
205 : // TODO(turbofan): Given that we already have the field_index here, we
206 : // might be smarter in the future and not rely on the LookupIterator.
207 : LookupIterator it(isolate(), m.Value(), name,
208 1596 : LookupIterator::OWN_SKIP_INTERCEPTOR);
209 1596 : if (it.state() == LookupIterator::DATA) {
210 : bool is_readonly_non_configurable =
211 2202 : it.IsReadOnly() && !it.IsConfigurable();
212 1461 : if (is_readonly_non_configurable ||
213 : (FLAG_track_constant_fields && access_info.IsDataConstantField())) {
214 1454 : Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
215 727 : if (!is_readonly_non_configurable) {
216 : // It's necessary to add dependency on the map that introduced
217 : // the field.
218 : DCHECK(access_info.IsDataConstantField());
219 : DCHECK(!it.is_dictionary_holder());
220 : MapRef map(broker(),
221 : handle(it.GetHolder<HeapObject>()->map(), isolate()));
222 0 : map.SerializeOwnDescriptors(); // TODO(neis): Remove later.
223 0 : dependencies()->DependOnFieldType(map, it.GetFieldDescriptorIndex());
224 : }
225 727 : return value;
226 : }
227 : }
228 : }
229 : return nullptr;
230 : }
231 :
232 25169 : Node* PropertyAccessBuilder::BuildLoadDataField(
233 24441 : Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver,
234 2250 : Node** effect, Node** control) {
235 : DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
236 25169 : receiver = ResolveHolder(access_info, receiver);
237 25168 : if (Node* value =
238 25168 : TryBuildLoadConstantDataField(name, access_info, receiver)) {
239 : return value;
240 : }
241 :
242 : FieldIndex const field_index = access_info.field_index();
243 : Type const field_type = access_info.field_type();
244 : MachineRepresentation const field_representation =
245 24441 : access_info.field_representation();
246 : Node* storage = receiver;
247 24441 : if (!field_index.is_inobject()) {
248 : storage = *effect = graph()->NewNode(
249 : simplified()->LoadField(AccessBuilder::ForJSObjectPropertiesOrHash()),
250 732 : storage, *effect, *control);
251 : }
252 : FieldAccess field_access = {
253 : kTaggedBase,
254 : field_index.offset(),
255 : name,
256 : MaybeHandle<Map>(),
257 : field_type,
258 : MachineType::TypeForRepresentation(field_representation),
259 : kFullWriteBarrier,
260 24441 : LoadSensitivity::kCritical};
261 24441 : if (field_representation == MachineRepresentation::kFloat64) {
262 659 : if (!field_index.is_inobject() || field_index.is_hidden_field() ||
263 : !FLAG_unbox_double_fields) {
264 : FieldAccess const storage_access = {kTaggedBase,
265 : field_index.offset(),
266 : name,
267 : MaybeHandle<Map>(),
268 : Type::OtherInternal(),
269 : MachineType::TaggedPointer(),
270 : kPointerWriteBarrier,
271 : LoadSensitivity::kCritical};
272 : storage = *effect = graph()->NewNode(
273 111 : simplified()->LoadField(storage_access), storage, *effect, *control);
274 37 : field_access.offset = HeapNumber::kValueOffset;
275 37 : field_access.name = MaybeHandle<Name>();
276 : }
277 24093 : } else if (field_representation == MachineRepresentation::kTaggedPointer) {
278 : // Remember the map of the field value, if its map is stable. This is
279 : // used by the LoadElimination to eliminate map checks on the result.
280 : Handle<Map> field_map;
281 6743 : if (access_info.field_map().ToHandle(&field_map)) {
282 1132 : if (field_map->is_stable()) {
283 1125 : dependencies()->DependOnStableMap(MapRef(broker(), field_map));
284 1125 : field_access.map = field_map;
285 : }
286 : }
287 : }
288 : Node* value = *effect = graph()->NewNode(
289 73324 : simplified()->LoadField(field_access), storage, *effect, *control);
290 24442 : return value;
291 : }
292 :
293 : } // namespace compiler
294 : } // namespace internal
295 183867 : } // namespace v8
|