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 273478 : Graph* PropertyAccessBuilder::graph() const { return jsgraph()->graph(); }
24 :
25 201817 : Isolate* PropertyAccessBuilder::isolate() const { return jsgraph()->isolate(); }
26 :
27 0 : CommonOperatorBuilder* PropertyAccessBuilder::common() const {
28 0 : return jsgraph()->common();
29 : }
30 :
31 175886 : SimplifiedOperatorBuilder* PropertyAccessBuilder::simplified() const {
32 175886 : return jsgraph()->simplified();
33 : }
34 :
35 156824 : bool HasOnlyStringMaps(MapHandles const& maps) {
36 322263 : for (auto map : maps) {
37 157581 : if (!map->IsStringMap()) return false;
38 : }
39 : return true;
40 : }
41 :
42 : namespace {
43 :
44 114869 : bool HasOnlyNumberMaps(MapHandles const& maps) {
45 231028 : for (auto map : maps) {
46 114869 : if (map->instance_type() != HEAP_NUMBER_TYPE) return false;
47 : }
48 : return true;
49 : }
50 :
51 : } // namespace
52 :
53 122265 : bool PropertyAccessBuilder::TryBuildStringCheck(MapHandles const& maps,
54 : Node** receiver, Node** effect,
55 : Node* control) {
56 122265 : 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 22188 : *effect, control);
62 7396 : return true;
63 : }
64 : return false;
65 : }
66 :
67 114869 : bool PropertyAccessBuilder::TryBuildNumberCheck(MapHandles const& maps,
68 : Node** receiver, Node** effect,
69 : Node* control) {
70 114869 : if (HasOnlyNumberMaps(maps)) {
71 : // Monomorphic number access (we also deal with Smis here).
72 : *receiver = *effect =
73 : graph()->NewNode(simplified()->CheckNumber(VectorSlotPair()), *receiver,
74 3870 : *effect, control);
75 1290 : return true;
76 : }
77 : return false;
78 : }
79 :
80 : namespace {
81 :
82 146994 : bool NeedsCheckHeapObject(Node* receiver) {
83 146994 : 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 2114 : Node* control = NodeProperties::GetControlInput(receiver);
120 2114 : if (control->opcode() != IrOpcode::kMerge) return true;
121 4167 : for (int i = 0; i < receiver->InputCount() - 1; ++i) {
122 2758 : if (NeedsCheckHeapObject(receiver->InputAt(i))) return true;
123 : }
124 : return false;
125 : }
126 : default:
127 49371 : return true;
128 : }
129 : }
130 :
131 : } // namespace
132 :
133 144236 : Node* PropertyAccessBuilder::BuildCheckHeapObject(Node* receiver, Node** effect,
134 : Node* control) {
135 144236 : if (NeedsCheckHeapObject(receiver)) {
136 : receiver = *effect = graph()->NewNode(simplified()->CheckHeapObject(),
137 149436 : receiver, *effect, control);
138 : }
139 144236 : return receiver;
140 : }
141 :
142 142054 : void PropertyAccessBuilder::BuildCheckMaps(
143 : Node* receiver, Node** effect, Node* control,
144 105562 : std::vector<Handle<Map>> const& receiver_maps) {
145 : HeapObjectMatcher m(receiver);
146 142054 : if (m.HasValue()) {
147 : Handle<Map> receiver_map(m.Value()->map(), isolate());
148 57812 : if (receiver_map->is_stable()) {
149 105562 : for (Handle<Map> map : receiver_maps) {
150 52781 : if (map.is_identical_to(receiver_map)) {
151 52781 : dependencies()->DependOnStableMap(MapRef(broker(), receiver_map));
152 142054 : return;
153 : }
154 : }
155 : }
156 : }
157 : ZoneHandleSet<Map> maps;
158 : CheckMapsFlags flags = CheckMapsFlag::kNone;
159 276138 : for (Handle<Map> map : receiver_maps) {
160 97592 : maps.insert(map, graph()->zone());
161 97592 : if (map->is_migration_target()) {
162 : flags |= CheckMapsFlag::kTryMigrateInstance;
163 : }
164 : }
165 : *effect = graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver,
166 267819 : *effect, control);
167 : }
168 :
169 1035 : Node* PropertyAccessBuilder::BuildCheckValue(Node* receiver, Node** effect,
170 : Node* control,
171 26 : Handle<HeapObject> value) {
172 : HeapObjectMatcher m(receiver);
173 1035 : if (m.Is(value)) return receiver;
174 26 : Node* expected = jsgraph()->HeapConstant(value);
175 : Node* check =
176 26 : graph()->NewNode(simplified()->ReferenceEqual(), receiver, expected);
177 : *effect =
178 : graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongValue),
179 78 : check, *effect, control);
180 26 : return expected;
181 : }
182 :
183 98622 : Node* PropertyAccessBuilder::ResolveHolder(
184 26688 : PropertyAccessInfo const& access_info, Node* receiver) {
185 : Handle<JSObject> holder;
186 98622 : if (access_info.holder().ToHandle(&holder)) {
187 26688 : return jsgraph()->Constant(holder);
188 : }
189 : return receiver;
190 : }
191 :
192 98622 : Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
193 212314 : Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver) {
194 : // Optimize immutable property loads.
195 : HeapObjectMatcher m(receiver);
196 245584 : 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 73481 : LookupIterator::OWN_SKIP_INTERCEPTOR);
209 73481 : if (it.state() == LookupIterator::DATA) {
210 : bool is_readonly_non_configurable =
211 74101 : it.IsReadOnly() && !it.IsConfigurable();
212 145948 : if (is_readonly_non_configurable ||
213 : (FLAG_track_constant_fields && access_info.IsDataConstantField())) {
214 142532 : Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
215 71266 : 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 70524 : map.SerializeOwnDescriptors(); // TODO(neis): Remove later.
223 70524 : if (dependencies()->DependOnFieldConstness(
224 141048 : map, it.GetFieldDescriptorIndex()) !=
225 : PropertyConstness::kConst) {
226 0 : return nullptr;
227 : }
228 : }
229 71266 : return value;
230 : }
231 : }
232 : }
233 : return nullptr;
234 : }
235 :
236 98622 : Node* PropertyAccessBuilder::BuildLoadDataField(
237 27356 : Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver,
238 3252 : Node** effect, Node** control) {
239 : DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
240 98622 : receiver = ResolveHolder(access_info, receiver);
241 98622 : if (Node* value =
242 98622 : TryBuildLoadConstantDataField(name, access_info, receiver)) {
243 : return value;
244 : }
245 :
246 : FieldIndex const field_index = access_info.field_index();
247 : Type const field_type = access_info.field_type();
248 : MachineRepresentation const field_representation =
249 27356 : access_info.field_representation();
250 : Node* storage = receiver;
251 27356 : if (!field_index.is_inobject()) {
252 : storage = *effect = graph()->NewNode(
253 : simplified()->LoadField(AccessBuilder::ForJSObjectPropertiesOrHash()),
254 2010 : storage, *effect, *control);
255 : }
256 : FieldAccess field_access = {
257 : kTaggedBase,
258 : field_index.offset(),
259 : name,
260 : MaybeHandle<Map>(),
261 : field_type,
262 : MachineType::TypeForRepresentation(field_representation),
263 : kFullWriteBarrier,
264 27356 : LoadSensitivity::kCritical};
265 27356 : if (field_representation == MachineRepresentation::kFloat64) {
266 665 : if (!field_index.is_inobject() || field_index.is_hidden_field() ||
267 : !FLAG_unbox_double_fields) {
268 : FieldAccess const storage_access = {kTaggedBase,
269 : field_index.offset(),
270 : name,
271 : MaybeHandle<Map>(),
272 : Type::OtherInternal(),
273 : MachineType::TaggedPointer(),
274 : kPointerWriteBarrier,
275 : LoadSensitivity::kCritical};
276 : storage = *effect = graph()->NewNode(
277 111 : simplified()->LoadField(storage_access), storage, *effect, *control);
278 37 : field_access.offset = HeapNumber::kValueOffset;
279 37 : field_access.name = MaybeHandle<Name>();
280 : }
281 27005 : } else if (field_representation == MachineRepresentation::kTaggedPointer) {
282 : // Remember the map of the field value, if its map is stable. This is
283 : // used by the LoadElimination to eliminate map checks on the result.
284 : Handle<Map> field_map;
285 7798 : if (access_info.field_map().ToHandle(&field_map)) {
286 1630 : if (field_map->is_stable()) {
287 1626 : dependencies()->DependOnStableMap(MapRef(broker(), field_map));
288 1626 : field_access.map = field_map;
289 : }
290 : }
291 : }
292 : Node* value = *effect = graph()->NewNode(
293 82068 : simplified()->LoadField(field_access), storage, *effect, *control);
294 27356 : return value;
295 : }
296 :
297 : } // namespace compiler
298 : } // namespace internal
299 178779 : } // namespace v8
|