V8 API Reference, 7.2.502.16 (for Deno 0.2.4)
property-access-builder.cc
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 
22 Graph* PropertyAccessBuilder::graph() const { return jsgraph()->graph(); }
23 
24 Isolate* PropertyAccessBuilder::isolate() const { return jsgraph()->isolate(); }
25 
26 CommonOperatorBuilder* PropertyAccessBuilder::common() const {
27  return jsgraph()->common();
28 }
29 
30 SimplifiedOperatorBuilder* PropertyAccessBuilder::simplified() const {
31  return jsgraph()->simplified();
32 }
33 
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 
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 
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 
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 
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::kJSCreateArrayFromIterable:
93  case IrOpcode::kJSCreateLiteralObject:
94  case IrOpcode::kJSCreateEmptyLiteralObject:
95  case IrOpcode::kJSCreateLiteralRegExp:
96  case IrOpcode::kJSCreateGeneratorObject:
97  case IrOpcode::kJSConstructForwardVarargs:
98  case IrOpcode::kJSConstruct:
99  case IrOpcode::kJSConstructWithArrayLike:
100  case IrOpcode::kJSConstructWithSpread:
101  case IrOpcode::kJSToName:
102  case IrOpcode::kJSToString:
103  case IrOpcode::kJSToObject:
104  case IrOpcode::kTypeOf:
105  case IrOpcode::kJSGetSuperConstructor:
106  return false;
107  case IrOpcode::kPhi: {
108  Node* control = NodeProperties::GetControlInput(receiver);
109  if (control->opcode() != IrOpcode::kMerge) return true;
110  for (int i = 0; i < receiver->InputCount() - 1; ++i) {
111  if (NeedsCheckHeapObject(receiver->InputAt(i))) return true;
112  }
113  return false;
114  }
115  default:
116  return true;
117  }
118 }
119 
120 } // namespace
121 
122 Node* PropertyAccessBuilder::BuildCheckHeapObject(Node* receiver, Node** effect,
123  Node* control) {
124  if (NeedsCheckHeapObject(receiver)) {
125  receiver = *effect = graph()->NewNode(simplified()->CheckHeapObject(),
126  receiver, *effect, control);
127  }
128  return receiver;
129 }
130 
131 void PropertyAccessBuilder::BuildCheckMaps(
132  Node* receiver, Node** effect, Node* control,
133  std::vector<Handle<Map>> const& receiver_maps) {
134  HeapObjectMatcher m(receiver);
135  if (m.HasValue()) {
136  Handle<Map> receiver_map(m.Value()->map(), isolate());
137  if (receiver_map->is_stable()) {
138  for (Handle<Map> map : receiver_maps) {
139  if (map.is_identical_to(receiver_map)) {
140  dependencies()->DependOnStableMap(MapRef(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 
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 
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 
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(broker(),
210  handle(it.GetHolder<HeapObject>()->map(), isolate()));
211  map.SerializeOwnDescriptors(); // TODO(neis): Remove later.
212  dependencies()->DependOnFieldType(map, it.GetFieldDescriptorIndex());
213  }
214  return value;
215  }
216  }
217  }
218  return nullptr;
219 }
220 
221 Node* PropertyAccessBuilder::BuildLoadDataField(
222  Handle<Name> name, PropertyAccessInfo const& access_info, Node* receiver,
223  Node** effect, Node** control) {
224  DCHECK(access_info.IsDataField() || access_info.IsDataConstantField());
225  receiver = ResolveHolder(access_info, receiver);
226  if (Node* value =
227  TryBuildLoadConstantDataField(name, access_info, receiver)) {
228  return value;
229  }
230 
231  FieldIndex const field_index = access_info.field_index();
232  Type const field_type = access_info.field_type();
233  MachineRepresentation const field_representation =
234  access_info.field_representation();
235  Node* storage = receiver;
236  if (!field_index.is_inobject()) {
237  storage = *effect = graph()->NewNode(
238  simplified()->LoadField(AccessBuilder::ForJSObjectPropertiesOrHash()),
239  storage, *effect, *control);
240  }
241  FieldAccess field_access = {
242  kTaggedBase,
243  field_index.offset(),
244  name,
245  MaybeHandle<Map>(),
246  field_type,
247  MachineType::TypeForRepresentation(field_representation),
248  kFullWriteBarrier,
249  LoadSensitivity::kCritical};
250  if (field_representation == MachineRepresentation::kFloat64) {
251  if (!field_index.is_inobject() || field_index.is_hidden_field() ||
252  !FLAG_unbox_double_fields) {
253  FieldAccess const storage_access = {kTaggedBase,
254  field_index.offset(),
255  name,
256  MaybeHandle<Map>(),
257  Type::OtherInternal(),
258  MachineType::TaggedPointer(),
259  kPointerWriteBarrier,
260  LoadSensitivity::kCritical};
261  storage = *effect = graph()->NewNode(
262  simplified()->LoadField(storage_access), storage, *effect, *control);
263  field_access.offset = HeapNumber::kValueOffset;
264  field_access.name = MaybeHandle<Name>();
265  }
266  } else if (field_representation == MachineRepresentation::kTaggedPointer) {
267  // Remember the map of the field value, if its map is stable. This is
268  // used by the LoadElimination to eliminate map checks on the result.
269  Handle<Map> field_map;
270  if (access_info.field_map().ToHandle(&field_map)) {
271  if (field_map->is_stable()) {
272  dependencies()->DependOnStableMap(MapRef(broker(), field_map));
273  field_access.map = field_map;
274  }
275  }
276  }
277  Node* value = *effect = graph()->NewNode(
278  simplified()->LoadField(field_access), storage, *effect, *control);
279  return value;
280 }
281 
282 } // namespace compiler
283 } // namespace internal
284 } // namespace v8
Definition: libplatform.h:13