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/compilation-dependencies.h"
8 #include "src/compiler/access-builder.h"
9 #include "src/compiler/access-info.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
15 #include "src/field-index-inl.h"
16 #include "src/isolate-inl.h"
17
18 namespace v8 {
19 namespace internal {
20 namespace compiler {
21
graph() const22 Graph* PropertyAccessBuilder::graph() const { return jsgraph()->graph(); }
23
isolate() const24 Isolate* PropertyAccessBuilder::isolate() const { return jsgraph()->isolate(); }
25
common() const26 CommonOperatorBuilder* PropertyAccessBuilder::common() const {
27 return jsgraph()->common();
28 }
29
simplified() const30 SimplifiedOperatorBuilder* PropertyAccessBuilder::simplified() const {
31 return jsgraph()->simplified();
32 }
33
HasOnlyStringMaps(MapHandles const & maps)34 bool HasOnlyStringMaps(MapHandles const& maps) {
35 for (auto map : maps) {
36 if (!map->IsStringMap()) return false;
37 }
38 return true;
39 }
40
41 namespace {
42
HasOnlyNumberMaps(MapHandles const & maps)43 bool HasOnlyNumberMaps(MapHandles const& maps) {
44 for (auto map : maps) {
45 if (map->instance_type() != HEAP_NUMBER_TYPE) return false;
46 }
47 return true;
48 }
49
50 } // namespace
51
TryBuildStringCheck(MapHandles const & maps,Node ** receiver,Node ** effect,Node * control)52 bool PropertyAccessBuilder::TryBuildStringCheck(MapHandles const& maps,
53 Node** receiver, Node** effect,
54 Node* control) {
55 if (HasOnlyStringMaps(maps)) {
56 // Monormorphic string access (ignoring the fact that there are multiple
57 // String maps).
58 *receiver = *effect =
59 graph()->NewNode(simplified()->CheckString(VectorSlotPair()), *receiver,
60 *effect, control);
61 return true;
62 }
63 return false;
64 }
65
TryBuildNumberCheck(MapHandles const & maps,Node ** receiver,Node ** effect,Node * control)66 bool PropertyAccessBuilder::TryBuildNumberCheck(MapHandles const& maps,
67 Node** receiver, Node** effect,
68 Node* control) {
69 if (HasOnlyNumberMaps(maps)) {
70 // Monomorphic number access (we also deal with Smis here).
71 *receiver = *effect =
72 graph()->NewNode(simplified()->CheckNumber(VectorSlotPair()), *receiver,
73 *effect, control);
74 return true;
75 }
76 return false;
77 }
78
79 namespace {
80
NeedsCheckHeapObject(Node * receiver)81 bool NeedsCheckHeapObject(Node* receiver) {
82 switch (receiver->opcode()) {
83 case IrOpcode::kConvertReceiver:
84 case IrOpcode::kHeapConstant:
85 case IrOpcode::kJSCreate:
86 case IrOpcode::kJSCreateArguments:
87 case IrOpcode::kJSCreateArray:
88 case IrOpcode::kJSCreateClosure:
89 case IrOpcode::kJSCreateIterResultObject:
90 case IrOpcode::kJSCreateLiteralArray:
91 case IrOpcode::kJSCreateEmptyLiteralArray:
92 case IrOpcode::kJSCreateLiteralObject:
93 case IrOpcode::kJSCreateEmptyLiteralObject:
94 case IrOpcode::kJSCreateLiteralRegExp:
95 case IrOpcode::kJSCreateGeneratorObject:
96 case IrOpcode::kJSConstructForwardVarargs:
97 case IrOpcode::kJSConstruct:
98 case IrOpcode::kJSConstructWithArrayLike:
99 case IrOpcode::kJSConstructWithSpread:
100 case IrOpcode::kJSToName:
101 case IrOpcode::kJSToString:
102 case IrOpcode::kJSToObject:
103 case IrOpcode::kTypeOf:
104 case IrOpcode::kJSGetSuperConstructor:
105 return false;
106 case IrOpcode::kPhi: {
107 Node* control = NodeProperties::GetControlInput(receiver);
108 if (control->opcode() != IrOpcode::kMerge) return true;
109 for (int i = 0; i < receiver->InputCount() - 1; ++i) {
110 if (NeedsCheckHeapObject(receiver->InputAt(i))) return true;
111 }
112 return false;
113 }
114 default:
115 return true;
116 }
117 }
118
119 } // namespace
120
BuildCheckHeapObject(Node * receiver,Node ** effect,Node * control)121 Node* PropertyAccessBuilder::BuildCheckHeapObject(Node* receiver, Node** effect,
122 Node* control) {
123 if (NeedsCheckHeapObject(receiver)) {
124 receiver = *effect = graph()->NewNode(simplified()->CheckHeapObject(),
125 receiver, *effect, control);
126 }
127 return receiver;
128 }
129
BuildCheckMaps(Node * receiver,Node ** effect,Node * control,std::vector<Handle<Map>> const & receiver_maps)130 void PropertyAccessBuilder::BuildCheckMaps(
131 Node* receiver, Node** effect, Node* control,
132 std::vector<Handle<Map>> const& receiver_maps) {
133 HeapObjectMatcher m(receiver);
134 if (m.HasValue()) {
135 Handle<Map> receiver_map(m.Value()->map(), isolate());
136 if (receiver_map->is_stable()) {
137 for (Handle<Map> map : receiver_maps) {
138 if (map.is_identical_to(receiver_map)) {
139 dependencies()->AssumeMapStable(receiver_map);
140 return;
141 }
142 }
143 }
144 }
145 ZoneHandleSet<Map> maps;
146 CheckMapsFlags flags = CheckMapsFlag::kNone;
147 for (Handle<Map> map : receiver_maps) {
148 maps.insert(map, graph()->zone());
149 if (map->is_migration_target()) {
150 flags |= CheckMapsFlag::kTryMigrateInstance;
151 }
152 }
153 *effect = graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver,
154 *effect, control);
155 }
156
BuildCheckValue(Node * receiver,Node ** effect,Node * control,Handle<HeapObject> value)157 Node* PropertyAccessBuilder::BuildCheckValue(Node* receiver, Node** effect,
158 Node* control,
159 Handle<HeapObject> value) {
160 HeapObjectMatcher m(receiver);
161 if (m.Is(value)) return receiver;
162 Node* expected = jsgraph()->HeapConstant(value);
163 Node* check =
164 graph()->NewNode(simplified()->ReferenceEqual(), receiver, expected);
165 *effect =
166 graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongValue),
167 check, *effect, control);
168 return expected;
169 }
170
AssumePrototypesStable(Handle<Context> native_context,std::vector<Handle<Map>> const & receiver_maps,Handle<JSObject> holder)171 void PropertyAccessBuilder::AssumePrototypesStable(
172 Handle<Context> native_context,
173 std::vector<Handle<Map>> const& receiver_maps, Handle<JSObject> holder) {
174 // Determine actual holder and perform prototype chain checks.
175 for (auto map : receiver_maps) {
176 // Perform the implicit ToObject for primitives here.
177 // Implemented according to ES6 section 7.3.2 GetV (V, P).
178 Handle<JSFunction> constructor;
179 if (Map::GetConstructorFunction(map, native_context)
180 .ToHandle(&constructor)) {
181 map = handle(constructor->initial_map(), holder->GetIsolate());
182 }
183 dependencies()->AssumePrototypeMapsStable(map, holder);
184 }
185 }
186
ResolveHolder(PropertyAccessInfo const & access_info,Node * receiver)187 Node* PropertyAccessBuilder::ResolveHolder(
188 PropertyAccessInfo const& access_info, Node* receiver) {
189 Handle<JSObject> holder;
190 if (access_info.holder().ToHandle(&holder)) {
191 return jsgraph()->Constant(holder);
192 }
193 return receiver;
194 }
195
TryBuildLoadConstantDataField(Handle<Name> name,PropertyAccessInfo const & access_info,Node * receiver)196 Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
197 Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver) {
198 // Optimize immutable property loads.
199 HeapObjectMatcher m(receiver);
200 if (m.HasValue() && m.Value()->IsJSObject()) {
201 // TODO(ishell): Use something simpler like
202 //
203 // Handle<Object> value =
204 // JSObject::FastPropertyAt(Handle<JSObject>::cast(m.Value()),
205 // Representation::Tagged(), field_index);
206 //
207 // here, once we have the immutable bit in the access_info.
208
209 // TODO(turbofan): Given that we already have the field_index here, we
210 // might be smarter in the future and not rely on the LookupIterator,
211 // but for now let's just do what Crankshaft does.
212 LookupIterator it(m.Value(), name, LookupIterator::OWN_SKIP_INTERCEPTOR);
213 if (it.state() == LookupIterator::DATA) {
214 bool is_reaonly_non_configurable =
215 it.IsReadOnly() && !it.IsConfigurable();
216 if (is_reaonly_non_configurable ||
217 (FLAG_track_constant_fields && access_info.IsDataConstantField())) {
218 Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
219 if (!is_reaonly_non_configurable) {
220 // It's necessary to add dependency on the map that introduced
221 // the field.
222 DCHECK(access_info.IsDataConstantField());
223 DCHECK(!it.is_dictionary_holder());
224 Handle<Map> field_owner_map = it.GetFieldOwnerMap();
225 dependencies()->AssumeFieldOwner(field_owner_map);
226 }
227 return value;
228 }
229 }
230 }
231 return nullptr;
232 }
233
BuildLoadDataField(Handle<Name> name,PropertyAccessInfo const & access_info,Node * receiver,Node ** effect,Node ** control)234 Node* PropertyAccessBuilder::BuildLoadDataField(
235 Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver,
236 Node** effect, Node** control) {
237 DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
238 receiver = ResolveHolder(access_info, receiver);
239 if (Node* value =
240 TryBuildLoadConstantDataField(name, access_info, receiver)) {
241 return value;
242 }
243
244 FieldIndex const field_index = access_info.field_index();
245 Type const field_type = access_info.field_type();
246 MachineRepresentation const field_representation =
247 access_info.field_representation();
248 Node* storage = receiver;
249 if (!field_index.is_inobject()) {
250 storage = *effect = graph()->NewNode(
251 simplified()->LoadField(AccessBuilder::ForJSObjectPropertiesOrHash()),
252 storage, *effect, *control);
253 }
254 FieldAccess field_access = {
255 kTaggedBase,
256 field_index.offset(),
257 name,
258 MaybeHandle<Map>(),
259 field_type,
260 MachineType::TypeForRepresentation(field_representation),
261 kFullWriteBarrier};
262 if (field_representation == MachineRepresentation::kFloat64) {
263 if (!field_index.is_inobject() || field_index.is_hidden_field() ||
264 !FLAG_unbox_double_fields) {
265 FieldAccess const storage_access = {kTaggedBase,
266 field_index.offset(),
267 name,
268 MaybeHandle<Map>(),
269 Type::OtherInternal(),
270 MachineType::TaggedPointer(),
271 kPointerWriteBarrier};
272 storage = *effect = graph()->NewNode(
273 simplified()->LoadField(storage_access), storage, *effect, *control);
274 field_access.offset = HeapNumber::kValueOffset;
275 field_access.name = MaybeHandle<Name>();
276 }
277 } 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 if (access_info.field_map().ToHandle(&field_map)) {
282 if (field_map->is_stable()) {
283 dependencies()->AssumeMapStable(field_map);
284 field_access.map = field_map;
285 }
286 }
287 }
288 Node* value = *effect = graph()->NewNode(
289 simplified()->LoadField(field_access), storage, *effect, *control);
290 return value;
291 }
292
293 } // namespace compiler
294 } // namespace internal
295 } // namespace v8
296