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 #ifndef V8_COMPILER_ESCAPE_ANALYSIS_H_ 6 #define V8_COMPILER_ESCAPE_ANALYSIS_H_ 7 8 #include "src/base/functional.h" 9 #include "src/common/globals.h" 10 #include "src/compiler/graph-reducer.h" 11 #include "src/compiler/js-graph.h" 12 #include "src/compiler/persistent-map.h" 13 #include "src/objects/name.h" 14 15 namespace v8 { 16 namespace internal { 17 18 class TickCounter; 19 20 namespace compiler { 21 22 class CommonOperatorBuilder; 23 class VariableTracker; 24 class EscapeAnalysisTracker; 25 26 // {EffectGraphReducer} reduces up to a fixed point. It distinguishes changes to 27 // the effect output of a node from changes to the value output to reduce the 28 // number of revisitations. 29 class EffectGraphReducer { 30 public: 31 class Reduction { 32 public: value_changed()33 bool value_changed() const { return value_changed_; } set_value_changed()34 void set_value_changed() { value_changed_ = true; } effect_changed()35 bool effect_changed() const { return effect_changed_; } set_effect_changed()36 void set_effect_changed() { effect_changed_ = true; } 37 38 private: 39 bool value_changed_ = false; 40 bool effect_changed_ = false; 41 }; 42 43 EffectGraphReducer(Graph* graph, 44 std::function<void(Node*, Reduction*)> reduce, 45 TickCounter* tick_counter, Zone* zone); 46 ReduceGraph()47 void ReduceGraph() { ReduceFrom(graph_->end()); } 48 49 // Mark node for revisitation. 50 void Revisit(Node* node); 51 52 // Add a new root node to start reduction from. This is useful if the reducer 53 // adds nodes that are not yet reachable, but should already be considered 54 // part of the graph. AddRoot(Node * node)55 void AddRoot(Node* node) { 56 DCHECK_EQ(State::kUnvisited, state_.Get(node)); 57 state_.Set(node, State::kRevisit); 58 revisit_.push(node); 59 } 60 Complete()61 bool Complete() { return stack_.empty() && revisit_.empty(); } 62 tick_counter()63 TickCounter* tick_counter() const { return tick_counter_; } 64 65 private: 66 struct NodeState { 67 Node* node; 68 int input_index; 69 }; 70 void ReduceFrom(Node* node); 71 enum class State : uint8_t { kUnvisited = 0, kRevisit, kOnStack, kVisited }; 72 const uint8_t kNumStates = static_cast<uint8_t>(State::kVisited) + 1; 73 Graph* graph_; 74 NodeMarker<State> state_; 75 ZoneStack<Node*> revisit_; 76 ZoneStack<NodeState> stack_; 77 std::function<void(Node*, Reduction*)> reduce_; 78 TickCounter* const tick_counter_; 79 }; 80 81 // A variable is an abstract storage location, which is lowered to SSA values 82 // and phi nodes by {VariableTracker}. 83 class Variable { 84 public: Variable()85 Variable() : id_(kInvalid) {} 86 bool operator==(Variable other) const { return id_ == other.id_; } 87 bool operator!=(Variable other) const { return id_ != other.id_; } 88 bool operator<(Variable other) const { return id_ < other.id_; } Invalid()89 static Variable Invalid() { return Variable(kInvalid); } hash_value(Variable v)90 friend V8_INLINE size_t hash_value(Variable v) { 91 return base::hash_value(v.id_); 92 } 93 friend std::ostream& operator<<(std::ostream& os, Variable var) { 94 return os << var.id_; 95 } 96 97 private: 98 using Id = int; Variable(Id id)99 explicit Variable(Id id) : id_(id) {} 100 Id id_; 101 static const Id kInvalid = -1; 102 103 friend class VariableTracker; 104 }; 105 106 // An object that can track the nodes in the graph whose current reduction 107 // depends on the value of the object. 108 class Dependable : public ZoneObject { 109 public: Dependable(Zone * zone)110 explicit Dependable(Zone* zone) : dependants_(zone) {} AddDependency(Node * node)111 void AddDependency(Node* node) { dependants_.push_back(node); } RevisitDependants(EffectGraphReducer * reducer)112 void RevisitDependants(EffectGraphReducer* reducer) { 113 for (Node* node : dependants_) { 114 reducer->Revisit(node); 115 } 116 dependants_.clear(); 117 } 118 119 private: 120 ZoneVector<Node*> dependants_; 121 }; 122 123 // A virtual object represents an allocation site and tracks the Variables 124 // associated with its fields as well as its global escape status. 125 class VirtualObject : public Dependable { 126 public: 127 using Id = uint32_t; 128 using const_iterator = ZoneVector<Variable>::const_iterator; 129 VirtualObject(VariableTracker* var_states, Id id, int size); FieldAt(int offset)130 Maybe<Variable> FieldAt(int offset) const { 131 CHECK(IsAligned(offset, kTaggedSize)); 132 CHECK(!HasEscaped()); 133 if (offset >= size()) { 134 // TODO(turbofan): Reading out-of-bounds can only happen in unreachable 135 // code. In this case, we have to mark the object as escaping to avoid 136 // dead nodes in the graph. This is a workaround that should be removed 137 // once we can handle dead nodes everywhere. 138 return Nothing<Variable>(); 139 } 140 return Just(fields_.at(offset / kTaggedSize)); 141 } FieldAt(Maybe<int> maybe_offset)142 Maybe<Variable> FieldAt(Maybe<int> maybe_offset) const { 143 int offset; 144 if (!maybe_offset.To(&offset)) return Nothing<Variable>(); 145 return FieldAt(offset); 146 } id()147 Id id() const { return id_; } size()148 int size() const { return static_cast<int>(kTaggedSize * fields_.size()); } 149 // Escaped might mean that the object escaped to untracked memory or that it 150 // is used in an operation that requires materialization. SetEscaped()151 void SetEscaped() { escaped_ = true; } HasEscaped()152 bool HasEscaped() const { return escaped_; } begin()153 const_iterator begin() const { return fields_.begin(); } end()154 const_iterator end() const { return fields_.end(); } 155 156 private: 157 bool escaped_ = false; 158 Id id_; 159 ZoneVector<Variable> fields_; 160 }; 161 162 class EscapeAnalysisResult { 163 public: EscapeAnalysisResult(EscapeAnalysisTracker * tracker)164 explicit EscapeAnalysisResult(EscapeAnalysisTracker* tracker) 165 : tracker_(tracker) {} 166 167 const VirtualObject* GetVirtualObject(Node* node); 168 Node* GetVirtualObjectField(const VirtualObject* vobject, int field, 169 Node* effect); 170 Node* GetReplacementOf(Node* node); 171 172 private: 173 EscapeAnalysisTracker* tracker_; 174 }; 175 176 class V8_EXPORT_PRIVATE EscapeAnalysis final NON_EXPORTED_BASE(EffectGraphReducer)177 : public NON_EXPORTED_BASE(EffectGraphReducer) { 178 public: 179 EscapeAnalysis(JSGraph* jsgraph, TickCounter* tick_counter, Zone* zone); 180 181 EscapeAnalysisResult analysis_result() { 182 DCHECK(Complete()); 183 return EscapeAnalysisResult(tracker_); 184 } 185 186 private: 187 void Reduce(Node* node, Reduction* reduction); 188 JSGraph* jsgraph() { return jsgraph_; } 189 Isolate* isolate() const { return jsgraph_->isolate(); } 190 EscapeAnalysisTracker* tracker_; 191 JSGraph* jsgraph_; 192 }; 193 194 } // namespace compiler 195 } // namespace internal 196 } // namespace v8 197 198 #endif // V8_COMPILER_ESCAPE_ANALYSIS_H_ 199