diff --git a/BUILD.gn b/BUILD.gn index f1f5c4bd22..3b2b25bfb2 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1811,6 +1811,8 @@ v8_compiler_sources = [ "src/compiler/machine-operator-reducer.h", "src/compiler/machine-operator.cc", "src/compiler/machine-operator.h", + "src/compiler/map-inference.cc", + "src/compiler/map-inference.h", "src/compiler/memory-optimizer.cc", "src/compiler/memory-optimizer.h", "src/compiler/node-aux-data.h", diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc index ec4a3d72c2..1f7aeb8d7e 100644 --- a/src/compiler/js-call-reducer.cc +++ b/src/compiler/js-call-reducer.cc @@ -16,6 +16,7 @@ #include "src/compiler/compilation-dependencies.h" #include "src/compiler/js-graph.h" #include "src/compiler/linkage.h" +#include "src/compiler/map-inference.h" #include "src/compiler/node-matchers.h" #include "src/compiler/property-access-builder.h" #include "src/compiler/simplified-operator.h" @@ -35,193 +36,6 @@ namespace v8 { namespace internal { namespace compiler { -// The MapInference class provides access to the "inferred" maps of an -// {object}. This information can be either "reliable", meaning that the object -// is guaranteed to have one of these maps at runtime, or "unreliable", meaning -// that the object is guaranteed to have HAD one of these maps. -// -// The MapInference class does not expose whether or not the information is -// reliable. A client is expected to eventually make the information reliable by -// calling one of several methods that will either insert map checks, or record -// stability dependencies (or do nothing if the information was already -// reliable). -class MapInference { - public: - MapInference(JSHeapBroker* broker, Node* object, Node* effect); - - // The destructor checks that the information has been made reliable (if - // necessary) and force-crashes if not. - ~MapInference(); - - // Is there any information at all? - V8_WARN_UNUSED_RESULT bool HaveMaps() const; - - // These queries don't require a guard. - // - V8_WARN_UNUSED_RESULT bool AllOfInstanceTypesAreJSReceiver() const; - // Here, {type} must not be a String type. - V8_WARN_UNUSED_RESULT bool AllOfInstanceTypesAre(InstanceType type) const; - - // These queries require a guard. (Even instance types are generally not - // reliable because of how the representation of a string can change.) - V8_WARN_UNUSED_RESULT MapHandles const& GetMaps(); - V8_WARN_UNUSED_RESULT bool AllOfInstanceTypes( - std::function f); - - // These methods provide a guard. - // - // Returns true iff maps were already reliable or stability dependencies were - // successfully recorded. - V8_WARN_UNUSED_RESULT bool RelyOnMapsViaStability( - CompilationDependencies* dependencies); - // Records stability dependencies if possible, otherwise it inserts map - // checks. Does nothing if maps were already reliable. Returns true iff - // dependencies were taken. - bool RelyOnMapsPreferStability(CompilationDependencies* dependencies, - JSGraph* jsgraph, Node** effect, Node* control, - const VectorSlotPair& feedback); - // Inserts map checks even if maps were already reliable. - void InsertMapChecks(JSGraph* jsgraph, Node** effect, Node* control, - const VectorSlotPair& feedback); - - // Internally marks the maps as reliable (thus bypassing the safety check) and - // returns the NoChange reduction. USE THIS ONLY WHEN RETURNING, e.g.: - // if (foo) return inference.NoChange(); - V8_WARN_UNUSED_RESULT Reduction NoChange(); - - private: - JSHeapBroker* const broker_; - Node* const object_; - - MapHandles maps_; - enum { - kReliableOrGuarded, - kUnreliableDontNeedGuard, - kUnreliableNeedGuard - } maps_state_; - - bool Safe() const; - void SetNeedGuardIfUnreliable(); - void SetGuarded(); - - V8_WARN_UNUSED_RESULT bool AllOfInstanceTypesUnsafe( - std::function f) const; - V8_WARN_UNUSED_RESULT bool RelyOnMapsHelper( - CompilationDependencies* dependencies, JSGraph* jsgraph, Node** effect, - Node* control, const VectorSlotPair& feedback); -}; - -MapInference::MapInference(JSHeapBroker* broker, Node* object, Node* effect) - : broker_(broker), object_(object) { - ZoneHandleSet maps; - auto result = - NodeProperties::InferReceiverMaps(broker_, object_, effect, &maps); - maps_.insert(maps_.end(), maps.begin(), maps.end()); - maps_state_ = (result == NodeProperties::kUnreliableReceiverMaps) - ? kUnreliableDontNeedGuard - : kReliableOrGuarded; - DCHECK_EQ(maps_.empty(), result == NodeProperties::kNoReceiverMaps); -} - -bool MapInference::Safe() const { return maps_state_ != kUnreliableNeedGuard; } - -void MapInference::SetNeedGuardIfUnreliable() { - CHECK(HaveMaps()); - if (maps_state_ == kUnreliableDontNeedGuard) { - maps_state_ = kUnreliableNeedGuard; - } -} - -void MapInference::SetGuarded() { maps_state_ = kReliableOrGuarded; } - -MapInference::~MapInference() { CHECK(Safe()); } - -bool MapInference::HaveMaps() const { return !maps_.empty(); } - -bool MapInference::AllOfInstanceTypesAreJSReceiver() const { - return AllOfInstanceTypesUnsafe(InstanceTypeChecker::IsJSReceiver); -} - -bool MapInference::AllOfInstanceTypesAre(InstanceType type) const { - CHECK(!InstanceTypeChecker::IsString(type)); - return AllOfInstanceTypesUnsafe( - [type](InstanceType other) { return type == other; }); -} - -bool MapInference::AllOfInstanceTypes(std::function f) { - SetNeedGuardIfUnreliable(); - return AllOfInstanceTypesUnsafe(f); -} - -bool MapInference::AllOfInstanceTypesUnsafe( - std::function f) const { - CHECK(HaveMaps()); - return std::all_of(maps_.begin(), maps_.end(), - [f](Handle map) { return f(map->instance_type()); }); -} - -MapHandles const& MapInference::GetMaps() { - SetNeedGuardIfUnreliable(); - return maps_; -} - -void MapInference::InsertMapChecks(JSGraph* jsgraph, Node** effect, - Node* control, - const VectorSlotPair& feedback) { - CHECK(HaveMaps()); - CHECK(feedback.IsValid()); - ZoneHandleSet maps; - for (Handle map : maps_) maps.insert(map, jsgraph->graph()->zone()); - *effect = jsgraph->graph()->NewNode( - jsgraph->simplified()->CheckMaps(CheckMapsFlag::kNone, maps, feedback), - object_, *effect, control); - SetGuarded(); -} - -bool MapInference::RelyOnMapsViaStability( - CompilationDependencies* dependencies) { - CHECK(HaveMaps()); - return RelyOnMapsHelper(dependencies, nullptr, nullptr, nullptr, {}); -} - -bool MapInference::RelyOnMapsPreferStability( - CompilationDependencies* dependencies, JSGraph* jsgraph, Node** effect, - Node* control, const VectorSlotPair& feedback) { - CHECK(HaveMaps()); - if (Safe()) return false; - if (RelyOnMapsViaStability(dependencies)) return true; - CHECK(RelyOnMapsHelper(nullptr, jsgraph, effect, control, feedback)); - return false; -} - -bool MapInference::RelyOnMapsHelper(CompilationDependencies* dependencies, - JSGraph* jsgraph, Node** effect, - Node* control, - const VectorSlotPair& feedback) { - if (Safe()) return true; - - auto is_stable = [](Handle map) { return map->is_stable(); }; - if (dependencies != nullptr && - std::all_of(maps_.cbegin(), maps_.cend(), is_stable)) { - for (Handle map : maps_) { - dependencies->DependOnStableMap(MapRef(broker_, map)); - } - SetGuarded(); - return true; - } else if (feedback.IsValid()) { - InsertMapChecks(jsgraph, effect, control, feedback); - return true; - } else { - return false; - } -} - -Reduction MapInference::NoChange() { - SetGuarded(); - maps_.clear(); // Just to make some CHECKs fail if {this} gets used after. - return Reducer::NoChange(); -} - Reduction JSCallReducer::ReduceMathUnary(Node* node, const Operator* op) { CallParameters const& p = CallParametersOf(node->op()); if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { diff --git a/src/compiler/map-inference.cc b/src/compiler/map-inference.cc new file mode 100644 index 0000000000..f07e4a0de8 --- /dev/null +++ b/src/compiler/map-inference.cc @@ -0,0 +1,131 @@ +// Copyright 2019 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/compiler/map-inference.h" + +#include "src/compiler/compilation-dependencies.h" +#include "src/compiler/js-graph.h" +#include "src/compiler/simplified-operator.h" +#include "src/objects/map-inl.h" +#include "src/vector-slot-pair.h" +#include "src/zone/zone-handle-set.h" + +namespace v8 { +namespace internal { +namespace compiler { + +MapInference::MapInference(JSHeapBroker* broker, Node* object, Node* effect) + : broker_(broker), object_(object) { + ZoneHandleSet maps; + auto result = + NodeProperties::InferReceiverMaps(broker_, object_, effect, &maps); + maps_.insert(maps_.end(), maps.begin(), maps.end()); + maps_state_ = (result == NodeProperties::kUnreliableReceiverMaps) + ? kUnreliableDontNeedGuard + : kReliableOrGuarded; + DCHECK_EQ(maps_.empty(), result == NodeProperties::kNoReceiverMaps); +} + +MapInference::~MapInference() { CHECK(Safe()); } + +bool MapInference::Safe() const { return maps_state_ != kUnreliableNeedGuard; } + +void MapInference::SetNeedGuardIfUnreliable() { + CHECK(HaveMaps()); + if (maps_state_ == kUnreliableDontNeedGuard) { + maps_state_ = kUnreliableNeedGuard; + } +} + +void MapInference::SetGuarded() { maps_state_ = kReliableOrGuarded; } + +bool MapInference::HaveMaps() const { return !maps_.empty(); } + +bool MapInference::AllOfInstanceTypesAreJSReceiver() const { + return AllOfInstanceTypesUnsafe(InstanceTypeChecker::IsJSReceiver); +} + +bool MapInference::AllOfInstanceTypesAre(InstanceType type) const { + CHECK(!InstanceTypeChecker::IsString(type)); + return AllOfInstanceTypesUnsafe( + [type](InstanceType other) { return type == other; }); +} + +bool MapInference::AllOfInstanceTypes(std::function f) { + SetNeedGuardIfUnreliable(); + return AllOfInstanceTypesUnsafe(f); +} + +bool MapInference::AllOfInstanceTypesUnsafe( + std::function f) const { + CHECK(HaveMaps()); + return std::all_of(maps_.begin(), maps_.end(), + [f](Handle map) { return f(map->instance_type()); }); +} + +MapHandles const& MapInference::GetMaps() { + SetNeedGuardIfUnreliable(); + return maps_; +} + +void MapInference::InsertMapChecks(JSGraph* jsgraph, Node** effect, + Node* control, + const VectorSlotPair& feedback) { + CHECK(HaveMaps()); + CHECK(feedback.IsValid()); + ZoneHandleSet maps; + for (Handle map : maps_) maps.insert(map, jsgraph->graph()->zone()); + *effect = jsgraph->graph()->NewNode( + jsgraph->simplified()->CheckMaps(CheckMapsFlag::kNone, maps, feedback), + object_, *effect, control); + SetGuarded(); +} + +bool MapInference::RelyOnMapsViaStability( + CompilationDependencies* dependencies) { + CHECK(HaveMaps()); + return RelyOnMapsHelper(dependencies, nullptr, nullptr, nullptr, {}); +} + +bool MapInference::RelyOnMapsPreferStability( + CompilationDependencies* dependencies, JSGraph* jsgraph, Node** effect, + Node* control, const VectorSlotPair& feedback) { + CHECK(HaveMaps()); + if (Safe()) return false; + if (RelyOnMapsViaStability(dependencies)) return true; + CHECK(RelyOnMapsHelper(nullptr, jsgraph, effect, control, feedback)); + return false; +} + +bool MapInference::RelyOnMapsHelper(CompilationDependencies* dependencies, + JSGraph* jsgraph, Node** effect, + Node* control, + const VectorSlotPair& feedback) { + if (Safe()) return true; + + auto is_stable = [](Handle map) { return map->is_stable(); }; + if (dependencies != nullptr && + std::all_of(maps_.cbegin(), maps_.cend(), is_stable)) { + for (Handle map : maps_) { + dependencies->DependOnStableMap(MapRef(broker_, map)); + } + SetGuarded(); + return true; + } else if (feedback.IsValid()) { + InsertMapChecks(jsgraph, effect, control, feedback); + return true; + } else { + return false; + } +} + +Reduction MapInference::NoChange() { + SetGuarded(); + maps_.clear(); // Just to make some CHECKs fail if {this} gets used after. + return Reducer::NoChange(); +} + +} // namespace compiler +} // namespace internal +} // namespace v8 diff --git a/src/compiler/map-inference.h b/src/compiler/map-inference.h new file mode 100644 index 0000000000..28dd37b1ee --- /dev/null +++ b/src/compiler/map-inference.h @@ -0,0 +1,105 @@ +// Copyright 2019 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_COMPILER_MAP_INFERENCE_H_ +#define V8_COMPILER_MAP_INFERENCE_H_ + +#include "include/v8config.h" +#include "src/compiler/graph-reducer.h" +#include "src/objects/instance-type.h" +#include "src/objects/map.h" + +namespace v8 { +namespace internal { + +class VectorSlotPair; + +namespace compiler { + +class CompilationDependencies; +class JSGraph; +class JSHeapBroker; +class Node; + +// The MapInference class provides access to the "inferred" maps of an +// {object}. This information can be either "reliable", meaning that the object +// is guaranteed to have one of these maps at runtime, or "unreliable", meaning +// that the object is guaranteed to have HAD one of these maps. +// +// The MapInference class does not expose whether or not the information is +// reliable. A client is expected to eventually make the information reliable by +// calling one of several methods that will either insert map checks, or record +// stability dependencies (or do nothing if the information was already +// reliable). +class MapInference { + public: + MapInference(JSHeapBroker* broker, Node* object, Node* effect); + + // The destructor checks that the information has been made reliable (if + // necessary) and force-crashes if not. + ~MapInference(); + + // Is there any information at all? + V8_WARN_UNUSED_RESULT bool HaveMaps() const; + + // These queries don't require a guard. + // + V8_WARN_UNUSED_RESULT bool AllOfInstanceTypesAreJSReceiver() const; + // Here, {type} must not be a String type. + V8_WARN_UNUSED_RESULT bool AllOfInstanceTypesAre(InstanceType type) const; + + // These queries require a guard. (Even instance types are generally not + // reliable because of how the representation of a string can change.) + V8_WARN_UNUSED_RESULT MapHandles const& GetMaps(); + V8_WARN_UNUSED_RESULT bool AllOfInstanceTypes( + std::function f); + + // These methods provide a guard. + // + // Returns true iff maps were already reliable or stability dependencies were + // successfully recorded. + V8_WARN_UNUSED_RESULT bool RelyOnMapsViaStability( + CompilationDependencies* dependencies); + // Records stability dependencies if possible, otherwise it inserts map + // checks. Does nothing if maps were already reliable. Returns true iff + // dependencies were taken. + bool RelyOnMapsPreferStability(CompilationDependencies* dependencies, + JSGraph* jsgraph, Node** effect, Node* control, + const VectorSlotPair& feedback); + // Inserts map checks even if maps were already reliable. + void InsertMapChecks(JSGraph* jsgraph, Node** effect, Node* control, + const VectorSlotPair& feedback); + + // Internally marks the maps as reliable (thus bypassing the safety check) and + // returns the NoChange reduction. USE THIS ONLY WHEN RETURNING, e.g.: + // if (foo) return inference.NoChange(); + V8_WARN_UNUSED_RESULT Reduction NoChange(); + + private: + JSHeapBroker* const broker_; + Node* const object_; + + MapHandles maps_; + enum { + kReliableOrGuarded, + kUnreliableDontNeedGuard, + kUnreliableNeedGuard + } maps_state_; + + bool Safe() const; + void SetNeedGuardIfUnreliable(); + void SetGuarded(); + + V8_WARN_UNUSED_RESULT bool AllOfInstanceTypesUnsafe( + std::function f) const; + V8_WARN_UNUSED_RESULT bool RelyOnMapsHelper( + CompilationDependencies* dependencies, JSGraph* jsgraph, Node** effect, + Node* control, const VectorSlotPair& feedback); +}; + +} // namespace compiler +} // namespace internal +} // namespace v8 + +#endif // V8_COMPILER_MAP_INFERENCE_H_