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
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()->DependOnStableMap(
140 MapRef(js_heap_broker(), receiver_map));
141 return;
142 }
143 }
144 }
145 }
146 ZoneHandleSet<Map> maps;
147 CheckMapsFlags flags = CheckMapsFlag::kNone;
148 for (Handle<Map> map : receiver_maps) {
149 maps.insert(map, graph()->zone());
150 if (map->is_migration_target()) {
151 flags |= CheckMapsFlag::kTryMigrateInstance;
152 }
153 }
154 *effect = graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver,
155 *effect, control);
156 }
157
BuildCheckValue(Node * receiver,Node ** effect,Node * control,Handle<HeapObject> value)158 Node* PropertyAccessBuilder::BuildCheckValue(Node* receiver, Node** effect,
159 Node* control,
160 Handle<HeapObject> value) {
161 HeapObjectMatcher m(receiver);
162 if (m.Is(value)) return receiver;
163 Node* expected = jsgraph()->HeapConstant(value);
164 Node* check =
165 graph()->NewNode(simplified()->ReferenceEqual(), receiver, expected);
166 *effect =
167 graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongValue),
168 check, *effect, control);
169 return expected;
170 }
171
ResolveHolder(PropertyAccessInfo const & access_info,Node * receiver)172 Node* PropertyAccessBuilder::ResolveHolder(
173 PropertyAccessInfo const& access_info, Node* receiver) {
174 Handle<JSObject> holder;
175 if (access_info.holder().ToHandle(&holder)) {
176 return jsgraph()->Constant(holder);
177 }
178 return receiver;
179 }
180
TryBuildLoadConstantDataField(Handle<Name> name,PropertyAccessInfo const & access_info,Node * receiver)181 Node* PropertyAccessBuilder::TryBuildLoadConstantDataField(
182 Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver) {
183 // Optimize immutable property loads.
184 HeapObjectMatcher m(receiver);
185 if (m.HasValue() && m.Value()->IsJSObject()) {
186 // TODO(ishell): Use something simpler like
187 //
188 // Handle<Object> value =
189 // JSObject::FastPropertyAt(Handle<JSObject>::cast(m.Value()),
190 // Representation::Tagged(), field_index);
191 //
192 // here, once we have the immutable bit in the access_info.
193
194 // TODO(turbofan): Given that we already have the field_index here, we
195 // might be smarter in the future and not rely on the LookupIterator.
196 LookupIterator it(isolate(), m.Value(), name,
197 LookupIterator::OWN_SKIP_INTERCEPTOR);
198 if (it.state() == LookupIterator::DATA) {
199 bool is_readonly_non_configurable =
200 it.IsReadOnly() && !it.IsConfigurable();
201 if (is_readonly_non_configurable ||
202 (FLAG_track_constant_fields && access_info.IsDataConstantField())) {
203 Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
204 if (!is_readonly_non_configurable) {
205 // It's necessary to add dependency on the map that introduced
206 // the field.
207 DCHECK(access_info.IsDataConstantField());
208 DCHECK(!it.is_dictionary_holder());
209 MapRef map(js_heap_broker(),
210 handle(it.GetHolder<HeapObject>()->map(), isolate()));
211 dependencies()->DependOnFieldType(map, it.GetFieldDescriptorIndex());
212 }
213 return value;
214 }
215 }
216 }
217 return nullptr;
218 }
219
BuildLoadDataField(Handle<Name> name,PropertyAccessInfo const & access_info,Node * receiver,Node ** effect,Node ** control)220 Node* PropertyAccessBuilder::BuildLoadDataField(
221 Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver,
222 Node** effect, Node** control) {
223 DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
224 receiver = ResolveHolder(access_info, receiver);
225 if (Node* value =
226 TryBuildLoadConstantDataField(name, access_info, receiver)) {
227 return value;
228 }
229
230 FieldIndex const field_index = access_info.field_index();
231 Type const field_type = access_info.field_type();
232 MachineRepresentation const field_representation =
233 access_info.field_representation();
234 Node* storage = receiver;
235 if (!field_index.is_inobject()) {
236 storage = *effect = graph()->NewNode(
237 simplified()->LoadField(AccessBuilder::ForJSObjectPropertiesOrHash()),
238 storage, *effect, *control);
239 }
240 FieldAccess field_access = {
241 kTaggedBase,
242 field_index.offset(),
243 name,
244 MaybeHandle<Map>(),
245 field_type,
246 MachineType::TypeForRepresentation(field_representation),
247 kFullWriteBarrier};
248 if (field_representation == MachineRepresentation::kFloat64) {
249 if (!field_index.is_inobject() || field_index.is_hidden_field() ||
250 !FLAG_unbox_double_fields) {
251 FieldAccess const storage_access = {kTaggedBase,
252 field_index.offset(),
253 name,
254 MaybeHandle<Map>(),
255 Type::OtherInternal(),
256 MachineType::TaggedPointer(),
257 kPointerWriteBarrier};
258 storage = *effect = graph()->NewNode(
259 simplified()->LoadField(storage_access), storage, *effect, *control);
260 field_access.offset = HeapNumber::kValueOffset;
261 field_access.name = MaybeHandle<Name>();
262 }
263 } else if (field_representation == MachineRepresentation::kTaggedPointer) {
264 // Remember the map of the field value, if its map is stable. This is
265 // used by the LoadElimination to eliminate map checks on the result.
266 Handle<Map> field_map;
267 if (access_info.field_map().ToHandle(&field_map)) {
268 if (field_map->is_stable()) {
269 dependencies()->DependOnStableMap(MapRef(js_heap_broker(), field_map));
270 field_access.map = field_map;
271 }
272 }
273 }
274 Node* value = *effect = graph()->NewNode(
275 simplified()->LoadField(field_access), storage, *effect, *control);
276 return value;
277 }
278
279 } // namespace compiler
280 } // namespace internal
281 } // namespace v8
282