Home | History | Annotate | Download | only in compiler
      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:
     30     bool value_changed() const { return value_changed_; }
     31     void set_value_changed() { value_changed_ = true; }
     32     bool effect_changed() const { return 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 
     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.
     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 
     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:
     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_; }
     82   static Variable Invalid() { return Variable(kInvalid); }
     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;
     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:
    103   explicit Dependable(Zone* zone) : dependants_(zone) {}
    104   void AddDependency(Node* node) { dependants_.push_back(node); }
    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);
    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   }
    141   Id id() const { return id_; }
    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.
    145   void SetEscaped() { escaped_ = true; }
    146   bool HasEscaped() const { return escaped_; }
    147   const_iterator begin() const { return fields_.begin(); }
    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:
    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
    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