| // Copyright 2017 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_COMPILER_ESCAPE_ANALYSIS_H_ |
| #define V8_COMPILER_ESCAPE_ANALYSIS_H_ |
| |
| #include "src/base/functional.h" |
| #include "src/common/globals.h" |
| #include "src/compiler/graph-reducer.h" |
| #include "src/compiler/js-graph.h" |
| #include "src/compiler/persistent-map.h" |
| #include "src/objects/name.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| class TickCounter; |
| |
| namespace compiler { |
| |
| class CommonOperatorBuilder; |
| class VariableTracker; |
| class EscapeAnalysisTracker; |
| |
| // {EffectGraphReducer} reduces up to a fixed point. It distinguishes changes to |
| // the effect output of a node from changes to the value output to reduce the |
| // number of revisitations. |
| class EffectGraphReducer { |
| public: |
| class Reduction { |
| public: |
| bool value_changed() const { return value_changed_; } |
| void set_value_changed() { value_changed_ = true; } |
| bool effect_changed() const { return effect_changed_; } |
| void set_effect_changed() { effect_changed_ = true; } |
| |
| private: |
| bool value_changed_ = false; |
| bool effect_changed_ = false; |
| }; |
| |
| EffectGraphReducer(Graph* graph, |
| std::function<void(Node*, Reduction*)> reduce, |
| TickCounter* tick_counter, Zone* zone); |
| |
| void ReduceGraph() { ReduceFrom(graph_->end()); } |
| |
| // Mark node for revisitation. |
| void Revisit(Node* node); |
| |
| // Add a new root node to start reduction from. This is useful if the reducer |
| // adds nodes that are not yet reachable, but should already be considered |
| // part of the graph. |
| void AddRoot(Node* node) { |
| DCHECK_EQ(State::kUnvisited, state_.Get(node)); |
| state_.Set(node, State::kRevisit); |
| revisit_.push(node); |
| } |
| |
| bool Complete() { return stack_.empty() && revisit_.empty(); } |
| |
| TickCounter* tick_counter() const { return tick_counter_; } |
| |
| private: |
| struct NodeState { |
| Node* node; |
| int input_index; |
| }; |
| void ReduceFrom(Node* node); |
| enum class State : uint8_t { kUnvisited = 0, kRevisit, kOnStack, kVisited }; |
| const uint8_t kNumStates = static_cast<uint8_t>(State::kVisited) + 1; |
| Graph* graph_; |
| NodeMarker<State> state_; |
| ZoneStack<Node*> revisit_; |
| ZoneStack<NodeState> stack_; |
| std::function<void(Node*, Reduction*)> reduce_; |
| TickCounter* const tick_counter_; |
| }; |
| |
| // A variable is an abstract storage location, which is lowered to SSA values |
| // and phi nodes by {VariableTracker}. |
| class Variable { |
| public: |
| Variable() : id_(kInvalid) {} |
| bool operator==(Variable other) const { return id_ == other.id_; } |
| bool operator!=(Variable other) const { return id_ != other.id_; } |
| bool operator<(Variable other) const { return id_ < other.id_; } |
| static Variable Invalid() { return Variable(kInvalid); } |
| friend V8_INLINE size_t hash_value(Variable v) { |
| return base::hash_value(v.id_); |
| } |
| friend std::ostream& operator<<(std::ostream& os, Variable var) { |
| return os << var.id_; |
| } |
| |
| private: |
| using Id = int; |
| explicit Variable(Id id) : id_(id) {} |
| Id id_; |
| static const Id kInvalid = -1; |
| |
| friend class VariableTracker; |
| }; |
| |
| // An object that can track the nodes in the graph whose current reduction |
| // depends on the value of the object. |
| class Dependable : public ZoneObject { |
| public: |
| explicit Dependable(Zone* zone) : dependants_(zone) {} |
| void AddDependency(Node* node) { dependants_.push_back(node); } |
| void RevisitDependants(EffectGraphReducer* reducer) { |
| for (Node* node : dependants_) { |
| reducer->Revisit(node); |
| } |
| dependants_.clear(); |
| } |
| |
| private: |
| ZoneVector<Node*> dependants_; |
| }; |
| |
| // A virtual object represents an allocation site and tracks the Variables |
| // associated with its fields as well as its global escape status. |
| class VirtualObject : public Dependable { |
| public: |
| using Id = uint32_t; |
| using const_iterator = ZoneVector<Variable>::const_iterator; |
| VirtualObject(VariableTracker* var_states, Id id, int size); |
| Maybe<Variable> FieldAt(int offset) const { |
| CHECK(IsAligned(offset, kTaggedSize)); |
| CHECK(!HasEscaped()); |
| if (offset >= size()) { |
| // TODO(tebbi): Reading out-of-bounds can only happen in unreachable |
| // code. In this case, we have to mark the object as escaping to avoid |
| // dead nodes in the graph. This is a workaround that should be removed |
| // once we can handle dead nodes everywhere. |
| return Nothing<Variable>(); |
| } |
| return Just(fields_.at(offset / kTaggedSize)); |
| } |
| Id id() const { return id_; } |
| int size() const { return static_cast<int>(kTaggedSize * fields_.size()); } |
| // Escaped might mean that the object escaped to untracked memory or that it |
| // is used in an operation that requires materialization. |
| void SetEscaped() { escaped_ = true; } |
| bool HasEscaped() const { return escaped_; } |
| const_iterator begin() const { return fields_.begin(); } |
| const_iterator end() const { return fields_.end(); } |
| |
| private: |
| bool escaped_ = false; |
| Id id_; |
| ZoneVector<Variable> fields_; |
| }; |
| |
| class EscapeAnalysisResult { |
| public: |
| explicit EscapeAnalysisResult(EscapeAnalysisTracker* tracker) |
| : tracker_(tracker) {} |
| |
| const VirtualObject* GetVirtualObject(Node* node); |
| Node* GetVirtualObjectField(const VirtualObject* vobject, int field, |
| Node* effect); |
| Node* GetReplacementOf(Node* node); |
| |
| private: |
| EscapeAnalysisTracker* tracker_; |
| }; |
| |
| class V8_EXPORT_PRIVATE EscapeAnalysis final |
| : public NON_EXPORTED_BASE(EffectGraphReducer) { |
| public: |
| EscapeAnalysis(JSGraph* jsgraph, TickCounter* tick_counter, Zone* zone); |
| |
| EscapeAnalysisResult analysis_result() { |
| DCHECK(Complete()); |
| return EscapeAnalysisResult(tracker_); |
| } |
| |
| private: |
| void Reduce(Node* node, Reduction* reduction); |
| JSGraph* jsgraph() { return jsgraph_; } |
| Isolate* isolate() const { return jsgraph_->isolate(); } |
| EscapeAnalysisTracker* tracker_; |
| JSGraph* jsgraph_; |
| }; |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_COMPILER_ESCAPE_ANALYSIS_H_ |