• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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