1 // Copyright 2015 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/compilation-dependencies.h" 6 7 #include "src/handles-inl.h" 8 #include "src/objects-inl.h" 9 10 namespace v8 { 11 namespace internal { 12 namespace compiler { 13 14 CompilationDependencies::CompilationDependencies(Isolate* isolate, Zone* zone) 15 : zone_(zone), dependencies_(zone) {} 16 17 class CompilationDependencies::Dependency : public ZoneObject { 18 public: 19 virtual bool IsValid() const = 0; 20 virtual void Install(MaybeObjectHandle code) = 0; 21 }; 22 23 class InitialMapDependency final : public CompilationDependencies::Dependency { 24 public: 25 // TODO(neis): Once the concurrent compiler frontend is always-on, we no 26 // longer need to explicitly store the initial map. 27 InitialMapDependency(const JSFunctionRef& function, const MapRef& initial_map) 28 : function_(function), initial_map_(initial_map) { 29 DCHECK(function_.has_initial_map()); 30 DCHECK(function_.initial_map().equals(initial_map_)); 31 } 32 33 bool IsValid() const override { 34 Handle<JSFunction> function = function_.object<JSFunction>(); 35 return function->has_initial_map() && 36 function->initial_map() == *initial_map_.object<Map>(); 37 } 38 39 void Install(MaybeObjectHandle code) override { 40 SLOW_DCHECK(IsValid()); 41 DependentCode::InstallDependency(function_.isolate(), code, 42 initial_map_.object<Map>(), 43 DependentCode::kInitialMapChangedGroup); 44 } 45 46 private: 47 JSFunctionRef function_; 48 MapRef initial_map_; 49 }; 50 51 class PrototypePropertyDependency final 52 : public CompilationDependencies::Dependency { 53 public: 54 // TODO(neis): Once the concurrent compiler frontend is always-on, we no 55 // longer need to explicitly store the prototype. 56 PrototypePropertyDependency(const JSFunctionRef& function, 57 const ObjectRef& prototype) 58 : function_(function), prototype_(prototype) { 59 DCHECK(function_.has_prototype()); 60 DCHECK(!function_.PrototypeRequiresRuntimeLookup()); 61 DCHECK(function_.prototype().equals(prototype_)); 62 } 63 64 bool IsValid() const override { 65 Handle<JSFunction> function = function_.object<JSFunction>(); 66 return function->has_prototype_slot() && function->has_prototype() && 67 !function->PrototypeRequiresRuntimeLookup() && 68 function->prototype() == *prototype_.object(); 69 } 70 71 void Install(MaybeObjectHandle code) override { 72 SLOW_DCHECK(IsValid()); 73 Handle<JSFunction> function = function_.object<JSFunction>(); 74 if (!function->has_initial_map()) JSFunction::EnsureHasInitialMap(function); 75 Handle<Map> initial_map(function->initial_map(), function_.isolate()); 76 DependentCode::InstallDependency(function_.isolate(), code, initial_map, 77 DependentCode::kInitialMapChangedGroup); 78 } 79 80 private: 81 JSFunctionRef function_; 82 ObjectRef prototype_; 83 }; 84 85 class StableMapDependency final : public CompilationDependencies::Dependency { 86 public: 87 explicit StableMapDependency(const MapRef& map) : map_(map) { 88 DCHECK(map_.is_stable()); 89 } 90 91 bool IsValid() const override { return map_.object<Map>()->is_stable(); } 92 93 void Install(MaybeObjectHandle code) override { 94 SLOW_DCHECK(IsValid()); 95 DependentCode::InstallDependency(map_.isolate(), code, map_.object<Map>(), 96 DependentCode::kPrototypeCheckGroup); 97 } 98 99 private: 100 MapRef map_; 101 }; 102 103 class TransitionDependency final : public CompilationDependencies::Dependency { 104 public: 105 explicit TransitionDependency(const MapRef& map) : map_(map) { 106 DCHECK(!map_.is_deprecated()); 107 } 108 109 bool IsValid() const override { return !map_.object<Map>()->is_deprecated(); } 110 111 void Install(MaybeObjectHandle code) override { 112 SLOW_DCHECK(IsValid()); 113 DependentCode::InstallDependency(map_.isolate(), code, map_.object<Map>(), 114 DependentCode::kTransitionGroup); 115 } 116 117 private: 118 MapRef map_; 119 }; 120 121 class PretenureModeDependency final 122 : public CompilationDependencies::Dependency { 123 public: 124 // TODO(neis): Once the concurrent compiler frontend is always-on, we no 125 // longer need to explicitly store the mode. 126 PretenureModeDependency(const AllocationSiteRef& site, PretenureFlag mode) 127 : site_(site), mode_(mode) { 128 DCHECK_EQ(mode_, site_.GetPretenureMode()); 129 } 130 131 bool IsValid() const override { 132 return mode_ == site_.object<AllocationSite>()->GetPretenureMode(); 133 } 134 135 void Install(MaybeObjectHandle code) override { 136 SLOW_DCHECK(IsValid()); 137 DependentCode::InstallDependency( 138 site_.isolate(), code, site_.object<AllocationSite>(), 139 DependentCode::kAllocationSiteTenuringChangedGroup); 140 } 141 142 private: 143 AllocationSiteRef site_; 144 PretenureFlag mode_; 145 }; 146 147 class FieldTypeDependency final : public CompilationDependencies::Dependency { 148 public: 149 // TODO(neis): Once the concurrent compiler frontend is always-on, we no 150 // longer need to explicitly store the type. 151 FieldTypeDependency(const MapRef& owner, int descriptor, 152 const ObjectRef& type) 153 : owner_(owner), descriptor_(descriptor), type_(type) { 154 DCHECK(owner_.equals(owner_.FindFieldOwner(descriptor_))); 155 DCHECK(type_.equals(owner_.GetFieldType(descriptor_))); 156 } 157 158 bool IsValid() const override { 159 DisallowHeapAllocation no_heap_allocation; 160 Handle<Map> owner = owner_.object<Map>(); 161 Handle<FieldType> type = type_.object<FieldType>(); 162 return *type == owner->instance_descriptors()->GetFieldType(descriptor_); 163 } 164 165 void Install(MaybeObjectHandle code) override { 166 SLOW_DCHECK(IsValid()); 167 DependentCode::InstallDependency(owner_.isolate(), code, 168 owner_.object<Map>(), 169 DependentCode::kFieldOwnerGroup); 170 } 171 172 private: 173 MapRef owner_; 174 int descriptor_; 175 ObjectRef type_; 176 }; 177 178 class GlobalPropertyDependency final 179 : public CompilationDependencies::Dependency { 180 public: 181 // TODO(neis): Once the concurrent compiler frontend is always-on, we no 182 // longer need to explicitly store the type and the read_only flag. 183 GlobalPropertyDependency(const PropertyCellRef& cell, PropertyCellType type, 184 bool read_only) 185 : cell_(cell), type_(type), read_only_(read_only) { 186 DCHECK_EQ(type_, cell_.property_details().cell_type()); 187 DCHECK_EQ(read_only_, cell_.property_details().IsReadOnly()); 188 } 189 190 bool IsValid() const override { 191 Handle<PropertyCell> cell = cell_.object<PropertyCell>(); 192 return type_ == cell->property_details().cell_type() && 193 read_only_ == cell->property_details().IsReadOnly(); 194 } 195 196 void Install(MaybeObjectHandle code) override { 197 SLOW_DCHECK(IsValid()); 198 DependentCode::InstallDependency(cell_.isolate(), code, 199 cell_.object<PropertyCell>(), 200 DependentCode::kPropertyCellChangedGroup); 201 } 202 203 private: 204 PropertyCellRef cell_; 205 PropertyCellType type_; 206 bool read_only_; 207 }; 208 209 class ProtectorDependency final : public CompilationDependencies::Dependency { 210 public: 211 explicit ProtectorDependency(const PropertyCellRef& cell) : cell_(cell) { 212 DCHECK_EQ(cell_.value().AsSmi(), Isolate::kProtectorValid); 213 } 214 215 bool IsValid() const override { 216 Handle<PropertyCell> cell = cell_.object<PropertyCell>(); 217 return cell->value() == Smi::FromInt(Isolate::kProtectorValid); 218 } 219 220 void Install(MaybeObjectHandle code) override { 221 SLOW_DCHECK(IsValid()); 222 DependentCode::InstallDependency(cell_.isolate(), code, 223 cell_.object<PropertyCell>(), 224 DependentCode::kPropertyCellChangedGroup); 225 } 226 227 private: 228 PropertyCellRef cell_; 229 }; 230 231 class ElementsKindDependency final 232 : public CompilationDependencies::Dependency { 233 public: 234 // TODO(neis): Once the concurrent compiler frontend is always-on, we no 235 // longer need to explicitly store the elements kind. 236 ElementsKindDependency(const AllocationSiteRef& site, ElementsKind kind) 237 : site_(site), kind_(kind) { 238 DCHECK(AllocationSite::ShouldTrack(kind_)); 239 DCHECK_EQ(kind_, site_.PointsToLiteral() 240 ? site_.boilerplate().value().GetElementsKind() 241 : site_.GetElementsKind()); 242 } 243 244 bool IsValid() const override { 245 Handle<AllocationSite> site = site_.object<AllocationSite>(); 246 ElementsKind kind = site->PointsToLiteral() 247 ? site->boilerplate()->GetElementsKind() 248 : site->GetElementsKind(); 249 return kind_ == kind; 250 } 251 252 void Install(MaybeObjectHandle code) override { 253 SLOW_DCHECK(IsValid()); 254 DependentCode::InstallDependency( 255 site_.isolate(), code, site_.object<AllocationSite>(), 256 DependentCode::kAllocationSiteTransitionChangedGroup); 257 } 258 259 private: 260 AllocationSiteRef site_; 261 ElementsKind kind_; 262 }; 263 264 class InitialMapInstanceSizePredictionDependency final 265 : public CompilationDependencies::Dependency { 266 public: 267 InitialMapInstanceSizePredictionDependency(const JSFunctionRef& function, 268 int instance_size) 269 : function_(function), instance_size_(instance_size) {} 270 271 bool IsValid() const override { 272 // The dependency is valid if the prediction is the same as the current 273 // slack tracking result. 274 int instance_size = 275 function_.object<JSFunction>()->ComputeInstanceSizeWithMinSlack( 276 function_.isolate()); 277 return instance_size == instance_size_; 278 } 279 280 void Install(MaybeObjectHandle code) override { 281 DCHECK(IsValid()); 282 // Finish the slack tracking. 283 function_.object<JSFunction>()->CompleteInobjectSlackTrackingIfActive(); 284 } 285 286 private: 287 JSFunctionRef function_; 288 int instance_size_; 289 }; 290 291 MapRef CompilationDependencies::DependOnInitialMap( 292 const JSFunctionRef& function) { 293 MapRef map = function.initial_map(); 294 dependencies_.push_front(new (zone_) InitialMapDependency(function, map)); 295 return map; 296 } 297 298 ObjectRef CompilationDependencies::DependOnPrototypeProperty( 299 const JSFunctionRef& function) { 300 ObjectRef prototype = function.prototype(); 301 dependencies_.push_front( 302 new (zone_) PrototypePropertyDependency(function, prototype)); 303 return prototype; 304 } 305 306 void CompilationDependencies::DependOnStableMap(const MapRef& map) { 307 if (map.CanTransition()) { 308 dependencies_.push_front(new (zone_) StableMapDependency(map)); 309 } else { 310 DCHECK(map.is_stable()); 311 } 312 } 313 314 void CompilationDependencies::DependOnTransition(const MapRef& target_map) { 315 if (target_map.CanBeDeprecated()) { 316 dependencies_.push_front(new (zone_) TransitionDependency(target_map)); 317 } else { 318 DCHECK(!target_map.is_deprecated()); 319 } 320 } 321 322 PretenureFlag CompilationDependencies::DependOnPretenureMode( 323 const AllocationSiteRef& site) { 324 PretenureFlag mode = site.GetPretenureMode(); 325 dependencies_.push_front(new (zone_) PretenureModeDependency(site, mode)); 326 return mode; 327 } 328 329 void CompilationDependencies::DependOnFieldType(const MapRef& map, 330 int descriptor) { 331 MapRef owner = map.FindFieldOwner(descriptor); 332 ObjectRef type = owner.GetFieldType(descriptor); 333 DCHECK(type.equals(map.GetFieldType(descriptor))); 334 dependencies_.push_front(new (zone_) 335 FieldTypeDependency(owner, descriptor, type)); 336 } 337 338 void CompilationDependencies::DependOnGlobalProperty( 339 const PropertyCellRef& cell) { 340 PropertyCellType type = cell.property_details().cell_type(); 341 bool read_only = cell.property_details().IsReadOnly(); 342 dependencies_.push_front(new (zone_) 343 GlobalPropertyDependency(cell, type, read_only)); 344 } 345 346 void CompilationDependencies::DependOnProtector(const PropertyCellRef& cell) { 347 dependencies_.push_front(new (zone_) ProtectorDependency(cell)); 348 } 349 350 void CompilationDependencies::DependOnElementsKind( 351 const AllocationSiteRef& site) { 352 // Do nothing if the object doesn't have any useful element transitions left. 353 ElementsKind kind = site.PointsToLiteral() 354 ? site.boilerplate().value().GetElementsKind() 355 : site.GetElementsKind(); 356 if (AllocationSite::ShouldTrack(kind)) { 357 dependencies_.push_front(new (zone_) ElementsKindDependency(site, kind)); 358 } 359 } 360 361 bool CompilationDependencies::AreValid() const { 362 for (auto dep : dependencies_) { 363 if (!dep->IsValid()) return false; 364 } 365 return true; 366 } 367 368 bool CompilationDependencies::Commit(Handle<Code> code) { 369 // Check validity of all dependencies first, such that we can avoid installing 370 // anything when there's already an invalid dependency. 371 if (!AreValid()) { 372 dependencies_.clear(); 373 return false; 374 } 375 376 for (auto dep : dependencies_) { 377 // Check each dependency's validity again right before installing it, 378 // because a GC can trigger invalidation for some dependency kinds. 379 if (!dep->IsValid()) { 380 dependencies_.clear(); 381 return false; 382 } 383 dep->Install(MaybeObjectHandle::Weak(code)); 384 } 385 dependencies_.clear(); 386 return true; 387 } 388 389 namespace { 390 void DependOnStablePrototypeChain(JSHeapBroker* broker, 391 CompilationDependencies* deps, 392 Handle<Map> map, 393 MaybeHandle<JSReceiver> last_prototype) { 394 for (PrototypeIterator i(broker->isolate(), map); !i.IsAtEnd(); i.Advance()) { 395 Handle<JSReceiver> const current = 396 PrototypeIterator::GetCurrent<JSReceiver>(i); 397 deps->DependOnStableMap( 398 MapRef(broker, handle(current->map(), broker->isolate()))); 399 Handle<JSReceiver> last; 400 if (last_prototype.ToHandle(&last) && last.is_identical_to(current)) { 401 break; 402 } 403 } 404 } 405 } // namespace 406 407 void CompilationDependencies::DependOnStablePrototypeChains( 408 JSHeapBroker* broker, Handle<Context> native_context, 409 std::vector<Handle<Map>> const& receiver_maps, Handle<JSObject> holder) { 410 Isolate* isolate = holder->GetIsolate(); 411 // Determine actual holder and perform prototype chain checks. 412 for (auto map : receiver_maps) { 413 // Perform the implicit ToObject for primitives here. 414 // Implemented according to ES6 section 7.3.2 GetV (V, P). 415 Handle<JSFunction> constructor; 416 if (Map::GetConstructorFunction(map, native_context) 417 .ToHandle(&constructor)) { 418 map = handle(constructor->initial_map(), isolate); 419 } 420 DependOnStablePrototypeChain(broker, this, map, holder); 421 } 422 } 423 424 void CompilationDependencies::DependOnElementsKinds( 425 const AllocationSiteRef& site) { 426 AllocationSiteRef current = site; 427 while (true) { 428 DependOnElementsKind(current); 429 if (!current.nested_site().IsAllocationSite()) break; 430 current = current.nested_site().AsAllocationSite(); 431 } 432 CHECK_EQ(current.nested_site().AsSmi(), 0); 433 } 434 435 SlackTrackingPrediction::SlackTrackingPrediction(MapRef initial_map, 436 int instance_size) 437 : instance_size_(instance_size), 438 inobject_property_count_( 439 (instance_size >> kPointerSizeLog2) - 440 initial_map.GetInObjectPropertiesStartInWords()) {} 441 442 SlackTrackingPrediction 443 CompilationDependencies::DependOnInitialMapInstanceSizePrediction( 444 const JSFunctionRef& function) { 445 MapRef initial_map = DependOnInitialMap(function); 446 int instance_size = function.InitialMapInstanceSizeWithMinSlack(); 447 // Currently, we always install the prediction dependency. If this turns out 448 // to be too expensive, we can only install the dependency if slack 449 // tracking is active. 450 dependencies_.push_front( 451 new (zone_) 452 InitialMapInstanceSizePredictionDependency(function, instance_size)); 453 DCHECK_LE(instance_size, function.initial_map().instance_size()); 454 return SlackTrackingPrediction(initial_map, instance_size); 455 } 456 457 } // namespace compiler 458 } // namespace internal 459 } // namespace v8 460