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