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/base/optional.h" 8#include "src/compiler/access-builder.h" 9#include "src/compiler/access-info.h" 10#include "src/compiler/compilation-dependencies.h" 11#include "src/compiler/js-graph.h" 12#include "src/compiler/node-matchers.h" 13#include "src/compiler/simplified-operator.h" 14#include "src/execution/isolate-inl.h" 15#include "src/objects/field-index-inl.h" 16#include "src/objects/heap-number.h" 17#include "src/objects/internal-index.h" 18#include "src/objects/lookup.h" 19#include "src/objects/property-details.h" 20 21namespace v8 { 22namespace internal { 23namespace compiler { 24 25Graph* PropertyAccessBuilder::graph() const { return jsgraph()->graph(); } 26 27Isolate* PropertyAccessBuilder::isolate() const { return jsgraph()->isolate(); } 28 29CommonOperatorBuilder* PropertyAccessBuilder::common() const { 30 return jsgraph()->common(); 31} 32 33SimplifiedOperatorBuilder* PropertyAccessBuilder::simplified() const { 34 return jsgraph()->simplified(); 35} 36 37bool HasOnlyStringMaps(JSHeapBroker* broker, ZoneVector<MapRef> const& maps) { 38 for (MapRef map : maps) { 39 if (!map.IsStringMap()) return false; 40 } 41 return true; 42} 43 44namespace { 45 46bool HasOnlyNumberMaps(JSHeapBroker* broker, ZoneVector<MapRef> const& maps) { 47 for (MapRef map : maps) { 48 if (map.instance_type() != HEAP_NUMBER_TYPE) return false; 49 } 50 return true; 51} 52 53} // namespace 54 55bool PropertyAccessBuilder::TryBuildStringCheck(JSHeapBroker* broker, 56 ZoneVector<MapRef> const& maps, 57 Node** receiver, Effect* effect, 58 Control control) { 59 if (HasOnlyStringMaps(broker, maps)) { 60 // Monormorphic string access (ignoring the fact that there are multiple 61 // String maps). 62 *receiver = *effect = 63 graph()->NewNode(simplified()->CheckString(FeedbackSource()), *receiver, 64 *effect, control); 65 return true; 66 } 67 return false; 68} 69 70bool PropertyAccessBuilder::TryBuildNumberCheck(JSHeapBroker* broker, 71 ZoneVector<MapRef> const& maps, 72 Node** receiver, Effect* effect, 73 Control control) { 74 if (HasOnlyNumberMaps(broker, maps)) { 75 // Monomorphic number access (we also deal with Smis here). 76 *receiver = *effect = 77 graph()->NewNode(simplified()->CheckNumber(FeedbackSource()), *receiver, 78 *effect, control); 79 return true; 80 } 81 return false; 82} 83 84void PropertyAccessBuilder::BuildCheckMaps(Node* object, Effect* effect, 85 Control control, 86 ZoneVector<MapRef> const& maps) { 87 HeapObjectMatcher m(object); 88 if (m.HasResolvedValue()) { 89 MapRef object_map = m.Ref(broker()).map(); 90 if (object_map.is_stable()) { 91 for (MapRef map : maps) { 92 if (map.equals(object_map)) { 93 dependencies()->DependOnStableMap(object_map); 94 return; 95 } 96 } 97 } 98 } 99 ZoneHandleSet<Map> map_set; 100 CheckMapsFlags flags = CheckMapsFlag::kNone; 101 for (MapRef map : maps) { 102 map_set.insert(map.object(), graph()->zone()); 103 if (map.is_migration_target()) { 104 flags |= CheckMapsFlag::kTryMigrateInstance; 105 } 106 } 107 *effect = graph()->NewNode(simplified()->CheckMaps(flags, map_set), object, 108 *effect, control); 109} 110 111Node* PropertyAccessBuilder::BuildCheckValue(Node* receiver, Effect* effect, 112 Control control, 113 Handle<HeapObject> value) { 114 HeapObjectMatcher m(receiver); 115 if (m.Is(value)) return receiver; 116 Node* expected = jsgraph()->HeapConstant(value); 117 Node* check = 118 graph()->NewNode(simplified()->ReferenceEqual(), receiver, expected); 119 *effect = 120 graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongValue), 121 check, *effect, control); 122 return expected; 123} 124 125Node* PropertyAccessBuilder::ResolveHolder( 126 PropertyAccessInfo const& access_info, Node* lookup_start_object) { 127 base::Optional<JSObjectRef> holder = access_info.holder(); 128 if (holder.has_value()) { 129 return jsgraph()->Constant(holder.value()); 130 } 131 return lookup_start_object; 132} 133 134MachineRepresentation PropertyAccessBuilder::ConvertRepresentation( 135 Representation representation) { 136 switch (representation.kind()) { 137 case Representation::kSmi: 138 return MachineRepresentation::kTaggedSigned; 139 case Representation::kDouble: 140 return MachineRepresentation::kFloat64; 141 case Representation::kHeapObject: 142 return MachineRepresentation::kTaggedPointer; 143 case Representation::kTagged: 144 return MachineRepresentation::kTagged; 145 default: 146 UNREACHABLE(); 147 } 148} 149 150base::Optional<Node*> PropertyAccessBuilder::FoldLoadDictPrototypeConstant( 151 PropertyAccessInfo const& access_info) { 152 DCHECK(V8_DICT_PROPERTY_CONST_TRACKING_BOOL); 153 DCHECK(access_info.IsDictionaryProtoDataConstant()); 154 155 InternalIndex index = access_info.dictionary_index(); 156 base::Optional<ObjectRef> value = 157 access_info.holder()->GetOwnDictionaryProperty(index, dependencies()); 158 if (!value) return {}; 159 160 for (MapRef map : access_info.lookup_start_object_maps()) { 161 Handle<Map> map_handle = map.object(); 162 // Non-JSReceivers that passed AccessInfoFactory::ComputePropertyAccessInfo 163 // must have different lookup start map. 164 if (!map_handle->IsJSReceiverMap()) { 165 // Perform the implicit ToObject for primitives here. 166 // Implemented according to ES6 section 7.3.2 GetV (V, P). 167 JSFunction constructor = 168 Map::GetConstructorFunction( 169 *map_handle, *broker()->target_native_context().object()) 170 .value(); 171 // {constructor.initial_map()} is loaded/stored with acquire-release 172 // semantics for constructors. 173 map = MakeRefAssumeMemoryFence(broker(), constructor.initial_map()); 174 DCHECK(map.object()->IsJSObjectMap()); 175 } 176 dependencies()->DependOnConstantInDictionaryPrototypeChain( 177 map, access_info.name(), value.value(), PropertyKind::kData); 178 } 179 180 return jsgraph()->Constant(value.value()); 181} 182 183Node* PropertyAccessBuilder::TryFoldLoadConstantDataField( 184 NameRef const& name, PropertyAccessInfo const& access_info, 185 Node* lookup_start_object) { 186 if (!access_info.IsFastDataConstant()) return nullptr; 187 188 // First, determine if we have a constant holder to load from. 189 base::Optional<JSObjectRef> holder = access_info.holder(); 190 191 // If {access_info} has a holder, just use it. 192 if (!holder.has_value()) { 193 // Otherwise, try to match the {lookup_start_object} as a constant. 194 HeapObjectMatcher m(lookup_start_object); 195 if (!m.HasResolvedValue() || !m.Ref(broker()).IsJSObject()) return nullptr; 196 197 // Let us make sure the actual map of the constant lookup_start_object is 198 // among the maps in {access_info}. 199 MapRef lookup_start_object_map = m.Ref(broker()).map(); 200 if (std::find_if(access_info.lookup_start_object_maps().begin(), 201 access_info.lookup_start_object_maps().end(), 202 [&](MapRef map) { 203 return map.equals(lookup_start_object_map); 204 }) == access_info.lookup_start_object_maps().end()) { 205 // The map of the lookup_start_object is not in the feedback, let us bail 206 // out. 207 return nullptr; 208 } 209 holder = m.Ref(broker()).AsJSObject(); 210 } 211 212 base::Optional<ObjectRef> value = 213 holder->GetOwnFastDataProperty(access_info.field_representation(), 214 access_info.field_index(), dependencies()); 215 return value.has_value() ? jsgraph()->Constant(*value) : nullptr; 216} 217 218Node* PropertyAccessBuilder::BuildLoadDataField(NameRef const& name, 219 Node* holder, 220 FieldAccess& field_access, 221 bool is_inobject, Node** effect, 222 Node** control) { 223 Node* storage = holder; 224 if (!is_inobject) { 225 storage = *effect = graph()->NewNode( 226 simplified()->LoadField( 227 AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer()), 228 storage, *effect, *control); 229 } 230 if (field_access.machine_type.representation() == 231 MachineRepresentation::kFloat64) { 232 if (dependencies() == nullptr) { 233 FieldAccess const storage_access = {kTaggedBase, 234 field_access.offset, 235 name.object(), 236 MaybeHandle<Map>(), 237 Type::Any(), 238 MachineType::AnyTagged(), 239 kPointerWriteBarrier, 240 field_access.const_field_info}; 241 storage = *effect = graph()->NewNode( 242 simplified()->LoadField(storage_access), storage, *effect, *control); 243 // We expect the loaded value to be a heap number here. With 244 // in-place field representation changes it is possible this is a 245 // no longer a heap number without map transitions. If we haven't taken 246 // a dependency on field representation, we should verify the loaded 247 // value is a heap number. 248 storage = *effect = graph()->NewNode(simplified()->CheckHeapObject(), 249 storage, *effect, *control); 250 Node* map = *effect = 251 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), 252 storage, *effect, *control); 253 Node* is_heap_number = 254 graph()->NewNode(simplified()->ReferenceEqual(), map, 255 jsgraph()->HeapNumberMapConstant()); 256 *effect = graph()->NewNode( 257 simplified()->CheckIf(DeoptimizeReason::kNotAHeapNumber), 258 is_heap_number, *effect, *control); 259 } else { 260 FieldAccess const storage_access = {kTaggedBase, 261 field_access.offset, 262 name.object(), 263 MaybeHandle<Map>(), 264 Type::OtherInternal(), 265 MachineType::TaggedPointer(), 266 kPointerWriteBarrier, 267 field_access.const_field_info}; 268 storage = *effect = graph()->NewNode( 269 simplified()->LoadField(storage_access), storage, *effect, *control); 270 } 271 field_access.offset = HeapNumber::kValueOffset; 272 field_access.name = MaybeHandle<Name>(); 273 } 274 Node* value = *effect = graph()->NewNode( 275 simplified()->LoadField(field_access), storage, *effect, *control); 276 return value; 277} 278 279Node* PropertyAccessBuilder::BuildLoadDataField( 280 NameRef const& name, PropertyAccessInfo const& access_info, 281 Node* lookup_start_object, Node** effect, Node** control) { 282 DCHECK(access_info.IsDataField() || access_info.IsFastDataConstant()); 283 284 if (Node* value = TryFoldLoadConstantDataField(name, access_info, 285 lookup_start_object)) { 286 return value; 287 } 288 289 MachineRepresentation const field_representation = 290 ConvertRepresentation(access_info.field_representation()); 291 Node* storage = ResolveHolder(access_info, lookup_start_object); 292 293 FieldAccess field_access = { 294 kTaggedBase, 295 access_info.field_index().offset(), 296 name.object(), 297 MaybeHandle<Map>(), 298 access_info.field_type(), 299 MachineType::TypeForRepresentation(field_representation), 300 kFullWriteBarrier, 301 access_info.GetConstFieldInfo()}; 302 if (field_representation == MachineRepresentation::kTaggedPointer || 303 field_representation == MachineRepresentation::kCompressedPointer) { 304 // Remember the map of the field value, if its map is stable. This is 305 // used by the LoadElimination to eliminate map checks on the result. 306 base::Optional<MapRef> field_map = access_info.field_map(); 307 if (field_map.has_value()) { 308 if (field_map->is_stable()) { 309 dependencies()->DependOnStableMap(field_map.value()); 310 field_access.map = field_map->object(); 311 } 312 } 313 } 314 return BuildLoadDataField(name, storage, field_access, 315 access_info.field_index().is_inobject(), effect, 316 control); 317} 318 319} // namespace compiler 320} // namespace internal 321} // namespace v8 322