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(tebbi): 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   }
id()142   Id id() const { return id_; }
size()143   int size() const { return static_cast<int>(kTaggedSize * fields_.size()); }
144   // Escaped might mean that the object escaped to untracked memory or that it
145   // is used in an operation that requires materialization.
SetEscaped()146   void SetEscaped() { escaped_ = true; }
HasEscaped()147   bool HasEscaped() const { return escaped_; }
begin()148   const_iterator begin() const { return fields_.begin(); }
end()149   const_iterator end() const { return fields_.end(); }
150 
151  private:
152   bool escaped_ = false;
153   Id id_;
154   ZoneVector<Variable> fields_;
155 };
156 
157 class EscapeAnalysisResult {
158  public:
EscapeAnalysisResult(EscapeAnalysisTracker * tracker)159   explicit EscapeAnalysisResult(EscapeAnalysisTracker* tracker)
160       : tracker_(tracker) {}
161 
162   const VirtualObject* GetVirtualObject(Node* node);
163   Node* GetVirtualObjectField(const VirtualObject* vobject, int field,
164                               Node* effect);
165   Node* GetReplacementOf(Node* node);
166 
167  private:
168   EscapeAnalysisTracker* tracker_;
169 };
170 
171 class V8_EXPORT_PRIVATE EscapeAnalysis final
NON_EXPORTED_BASE(EffectGraphReducer)172     : public NON_EXPORTED_BASE(EffectGraphReducer) {
173  public:
174   EscapeAnalysis(JSGraph* jsgraph, TickCounter* tick_counter, Zone* zone);
175 
176   EscapeAnalysisResult analysis_result() {
177     DCHECK(Complete());
178     return EscapeAnalysisResult(tracker_);
179   }
180 
181  private:
182   void Reduce(Node* node, Reduction* reduction);
183   JSGraph* jsgraph() { return jsgraph_; }
184   Isolate* isolate() const { return jsgraph_->isolate(); }
185   EscapeAnalysisTracker* tracker_;
186   JSGraph* jsgraph_;
187 };
188 
189 }  // namespace compiler
190 }  // namespace internal
191 }  // namespace v8
192 
193 #endif  // V8_COMPILER_ESCAPE_ANALYSIS_H_
194