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