LCOV - code coverage report
Current view: top level - src/compiler - escape-analysis.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 315 318 99.1 %
Date: 2019-04-17 Functions: 34 34 100.0 %

          Line data    Source code
       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             : #include "src/compiler/escape-analysis.h"
       6             : 
       7             : #include "src/bootstrapper.h"
       8             : #include "src/compiler/linkage.h"
       9             : #include "src/compiler/node-matchers.h"
      10             : #include "src/compiler/operator-properties.h"
      11             : #include "src/compiler/simplified-operator.h"
      12             : #include "src/handles-inl.h"
      13             : #include "src/objects/map-inl.h"
      14             : 
      15             : #ifdef DEBUG
      16             : #define TRACE(...)                                    \
      17             :   do {                                                \
      18             :     if (FLAG_trace_turbo_escape) PrintF(__VA_ARGS__); \
      19             :   } while (false)
      20             : #else
      21             : #define TRACE(...)
      22             : #endif
      23             : 
      24             : namespace v8 {
      25             : namespace internal {
      26             : namespace compiler {
      27             : 
      28             : template <class T>
      29             : class Sidetable {
      30             :  public:
      31             :   explicit Sidetable(Zone* zone) : map_(zone) {}
      32   122643363 :   T& operator[](const Node* node) {
      33             :     NodeId id = node->id();
      34   245286726 :     if (id >= map_.size()) {
      35     2430760 :       map_.resize(id + 1);
      36             :     }
      37   122643376 :     return map_[id];
      38             :   }
      39             : 
      40             :  private:
      41             :   ZoneVector<T> map_;
      42             : };
      43             : 
      44             : template <class T>
      45             : class SparseSidetable {
      46             :  public:
      47             :   explicit SparseSidetable(Zone* zone, T def_value = T())
      48      928326 :       : def_value_(std::move(def_value)), map_(zone) {}
      49    66423226 :   void Set(const Node* node, T value) {
      50   132846970 :     auto iter = map_.find(node->id());
      51    66423744 :     if (iter != map_.end()) {
      52     2249858 :       iter->second = std::move(value);
      53    64173834 :     } else if (value != def_value_) {
      54     6234976 :       map_.insert(iter, std::make_pair(node->id(), std::move(value)));
      55             :     }
      56    66423697 :   }
      57             :   const T& Get(const Node* node) const {
      58   350243573 :     auto iter = map_.find(node->id());
      59   175126188 :     return iter != map_.end() ? iter->second : def_value_;
      60             :   }
      61             : 
      62             :  private:
      63             :   T def_value_;
      64             :   ZoneUnorderedMap<NodeId, T> map_;
      65             : };
      66             : 
      67             : // Keeps track of the changes to the current node during reduction.
      68             : // Encapsulates the current state of the IR graph and the reducer state like
      69             : // side-tables. All access to the IR and the reducer state should happen through
      70             : // a ReduceScope to ensure that changes and dependencies are tracked and all
      71             : // necessary node revisitations happen.
      72             : class ReduceScope {
      73             :  public:
      74             :   using Reduction = EffectGraphReducer::Reduction;
      75             :   explicit ReduceScope(Node* node, Reduction* reduction)
      76    33212390 :       : current_node_(node), reduction_(reduction) {}
      77             : 
      78             :  protected:
      79             :   Node* current_node() const { return current_node_; }
      80             :   Reduction* reduction() { return reduction_; }
      81             : 
      82             :  private:
      83             :   Node* current_node_;
      84             :   Reduction* reduction_;
      85             : };
      86             : 
      87             : // A VariableTracker object keeps track of the values of variables at all points
      88             : // of the effect chain and introduces new phi nodes when necessary.
      89             : // Initially and by default, variables are mapped to nullptr, which means that
      90             : // the variable allocation point does not dominate the current point on the
      91             : // effect chain. We map variables that represent uninitialized memory to the
      92             : // Dead node to ensure it is not read.
      93             : // Unmapped values are impossible by construction, it is indistinguishable if a
      94             : // PersistentMap does not contain an element or maps it to the default element.
      95             : class VariableTracker {
      96             :  private:
      97             :   // The state of all variables at one point in the effect chain.
      98             :   class State {
      99             :    public:
     100             :     using Map = PersistentMap<Variable, Node*>;
     101             : 
     102             :     explicit State(Zone* zone) : map_(zone) {}
     103    13095177 :     Node* Get(Variable var) const {
     104    13095177 :       CHECK(var != Variable::Invalid());
     105    13095177 :       return map_.Get(var);
     106             :     }
     107     6189339 :     void Set(Variable var, Node* node) {
     108     6189339 :       CHECK(var != Variable::Invalid());
     109     6189339 :       return map_.Set(var, node);
     110             :     }
     111             :     Map::iterator begin() const { return map_.begin(); }
     112             :     Map::iterator end() const { return map_.end(); }
     113    64424895 :     bool operator!=(const State& other) const { return map_ != other.map_; }
     114             : 
     115             :    private:
     116             :     Map map_;
     117             :   };
     118             : 
     119             :  public:
     120             :   VariableTracker(JSGraph* graph, EffectGraphReducer* reducer, Zone* zone);
     121      766738 :   Variable NewVariable() { return Variable(next_variable_++); }
     122     1353062 :   Node* Get(Variable var, Node* effect) { return table_.Get(effect).Get(var); }
     123             :   Zone* zone() { return zone_; }
     124             : 
     125             :   class Scope : public ReduceScope {
     126             :    public:
     127             :     Scope(VariableTracker* tracker, Node* node, Reduction* reduction);
     128             :     ~Scope();
     129       25977 :     Maybe<Node*> Get(Variable var) {
     130       25977 :       Node* node = current_state_.Get(var);
     131       44921 :       if (node && node->opcode() == IrOpcode::kDead) {
     132             :         // TODO(tebbi): We use {Dead} as a sentinel for uninitialized memory.
     133             :         // Reading uninitialized memory can only happen in unreachable code. In
     134             :         // this case, we have to mark the object as escaping to avoid dead nodes
     135             :         // in the graph. This is a workaround that should be removed once we can
     136             :         // handle dead nodes everywhere.
     137             :         return Nothing<Node*>();
     138             :       }
     139             :       return Just(node);
     140             :     }
     141     2631967 :     void Set(Variable var, Node* node) { current_state_.Set(var, node); }
     142             : 
     143             :    private:
     144             :     VariableTracker* states_;
     145             :     State current_state_;
     146             :   };
     147             : 
     148             :  private:
     149             :   State MergeInputs(Node* effect_phi);
     150             :   Zone* zone_;
     151             :   JSGraph* graph_;
     152             :   SparseSidetable<State> table_;
     153             :   ZoneVector<Node*> buffer_;
     154             :   EffectGraphReducer* reducer_;
     155             :   int next_variable_ = 0;
     156             : 
     157             :   DISALLOW_COPY_AND_ASSIGN(VariableTracker);
     158             : };
     159             : 
     160             : // Encapsulates the current state of the escape analysis reducer to preserve
     161             : // invariants regarding changes and re-visitation.
     162             : class EscapeAnalysisTracker : public ZoneObject {
     163             :  public:
     164      464165 :   EscapeAnalysisTracker(JSGraph* jsgraph, EffectGraphReducer* reducer,
     165             :                         Zone* zone)
     166             :       : virtual_objects_(zone),
     167             :         replacements_(zone),
     168             :         variable_states_(jsgraph, reducer, zone),
     169             :         jsgraph_(jsgraph),
     170      464164 :         zone_(zone) {}
     171             : 
     172             :   class Scope : public VariableTracker::Scope {
     173             :    public:
     174             :     Scope(EffectGraphReducer* reducer, EscapeAnalysisTracker* tracker,
     175             :           Node* node, Reduction* reduction)
     176             :         : VariableTracker::Scope(&tracker->variable_states_, node, reduction),
     177             :           tracker_(tracker),
     178    33211880 :           reducer_(reducer) {}
     179     5329469 :     const VirtualObject* GetVirtualObject(Node* node) {
     180    10658973 :       VirtualObject* vobject = tracker_->virtual_objects_.Get(node);
     181     5329504 :       if (vobject) vobject->AddDependency(current_node());
     182     5329504 :       return vobject;
     183             :     }
     184             :     // Create or retrieve a virtual object for the current node.
     185      298867 :     const VirtualObject* InitVirtualObject(int size) {
     186             :       DCHECK_EQ(IrOpcode::kAllocate, current_node()->opcode());
     187      597734 :       VirtualObject* vobject = tracker_->virtual_objects_.Get(current_node());
     188      298867 :       if (vobject) {
     189      130488 :         CHECK(vobject->size() == size);
     190             :       } else {
     191      168379 :         vobject = tracker_->NewVirtualObject(size);
     192             :       }
     193      298867 :       if (vobject) vobject->AddDependency(current_node());
     194      298866 :       vobject_ = vobject;
     195      298866 :       return vobject;
     196             :     }
     197             : 
     198             :     void SetVirtualObject(Node* object) {
     199      343465 :       vobject_ = tracker_->virtual_objects_.Get(object);
     200             :     }
     201             : 
     202    22103205 :     void SetEscaped(Node* node) {
     203    44206304 :       if (VirtualObject* object = tracker_->virtual_objects_.Get(node)) {
     204     1375560 :         if (object->HasEscaped()) return;
     205             :         TRACE("Setting %s#%d to escaped because of use by %s#%d\n",
     206             :               node->op()->mnemonic(), node->id(),
     207             :               current_node()->op()->mnemonic(), current_node()->id());
     208             :         object->SetEscaped();
     209       84186 :         object->RevisitDependants(reducer_);
     210             :       }
     211             :     }
     212             :     // The inputs of the current node have to be accessed through the scope to
     213             :     // ensure that they respect the node replacements.
     214    21675567 :     Node* ValueInput(int i) {
     215    21675567 :       return tracker_->ResolveReplacement(
     216    21675568 :           NodeProperties::GetValueInput(current_node(), i));
     217             :     }
     218     3628478 :     Node* ContextInput() {
     219     3628478 :       return tracker_->ResolveReplacement(
     220     3628477 :           NodeProperties::GetContextInput(current_node()));
     221             :     }
     222             : 
     223     1022842 :     void SetReplacement(Node* replacement) {
     224     1022842 :       replacement_ = replacement;
     225             :       vobject_ =
     226     2040369 :           replacement ? tracker_->virtual_objects_.Get(replacement) : nullptr;
     227             :       if (replacement) {
     228             :         TRACE("Set %s#%d as replacement.\n", replacement->op()->mnemonic(),
     229             :               replacement->id());
     230             :       } else {
     231             :         TRACE("Set nullptr as replacement.\n");
     232             :       }
     233     1022838 :     }
     234             : 
     235     1002640 :     void MarkForDeletion() { SetReplacement(tracker_->jsgraph_->Dead()); }
     236             : 
     237    66423187 :     ~Scope() {
     238    64886389 :       if (replacement_ != tracker_->replacements_[current_node()] ||
     239    63349610 :           vobject_ != tracker_->virtual_objects_.Get(current_node())) {
     240             :         reduction()->set_value_changed();
     241             :       }
     242    33211478 :       tracker_->replacements_[current_node()] = replacement_;
     243    33211379 :       tracker_->virtual_objects_.Set(current_node(), vobject_);
     244    33211738 :     }
     245             : 
     246             :    private:
     247             :     EscapeAnalysisTracker* tracker_;
     248             :     EffectGraphReducer* reducer_;
     249             :     VirtualObject* vobject_ = nullptr;
     250             :     Node* replacement_ = nullptr;
     251             :   };
     252             : 
     253    56235431 :   Node* GetReplacementOf(Node* node) { return replacements_[node]; }
     254             :   Node* ResolveReplacement(Node* node) {
     255    25304045 :     if (Node* replacement = GetReplacementOf(node)) {
     256             :       return replacement;
     257             :     }
     258             :     return node;
     259             :   }
     260             : 
     261             :  private:
     262             :   friend class EscapeAnalysisResult;
     263             :   static const size_t kMaxTrackedObjects = 100;
     264             : 
     265      168379 :   VirtualObject* NewVirtualObject(int size) {
     266      168379 :     if (next_object_id_ >= kMaxTrackedObjects) return nullptr;
     267      112350 :     return new (zone_)
     268      224700 :         VirtualObject(&variable_states_, next_object_id_++, size);
     269             :   }
     270             : 
     271             :   SparseSidetable<VirtualObject*> virtual_objects_;
     272             :   Sidetable<Node*> replacements_;
     273             :   VariableTracker variable_states_;
     274             :   VirtualObject::Id next_object_id_ = 0;
     275             :   JSGraph* const jsgraph_;
     276             :   Zone* const zone_;
     277             : 
     278             :   DISALLOW_COPY_AND_ASSIGN(EscapeAnalysisTracker);
     279             : };
     280             : 
     281      464161 : EffectGraphReducer::EffectGraphReducer(
     282             :     Graph* graph, std::function<void(Node*, Reduction*)> reduce, Zone* zone)
     283             :     : graph_(graph),
     284             :       state_(graph, kNumStates),
     285             :       revisit_(zone),
     286             :       stack_(zone),
     287     1392491 :       reduce_(std::move(reduce)) {}
     288             : 
     289      464161 : void EffectGraphReducer::ReduceFrom(Node* node) {
     290             :   // Perform DFS and eagerly trigger revisitation as soon as possible.
     291             :   // A stack element {node, i} indicates that input i of node should be visited
     292             :   // next.
     293             :   DCHECK(stack_.empty());
     294      928056 :   stack_.push({node, 0});
     295   140291918 :   while (!stack_.empty()) {
     296   139827750 :     Node* current = stack_.top().node;
     297             :     int& input_index = stack_.top().input_index;
     298   279655500 :     if (input_index < current->InputCount()) {
     299             :       Node* input = current->InputAt(input_index);
     300   106615940 :       input_index++;
     301   106615940 :       switch (state_.Get(input)) {
     302             :         case State::kVisited:
     303             :           // The input is already reduced.
     304             :           break;
     305             :         case State::kOnStack:
     306             :           // The input is on the DFS stack right now, so it will be revisited
     307             :           // later anyway.
     308             :           break;
     309             :         case State::kUnvisited:
     310             :         case State::kRevisit: {
     311             :           state_.Set(input, State::kOnStack);
     312    60803067 :           stack_.push({input, 0});
     313    30401655 :           break;
     314             :         }
     315             :       }
     316             :     } else {
     317             :       stack_.pop();
     318    33211775 :       Reduction reduction;
     319             :       reduce_(current, &reduction);
     320   241471101 :       for (Edge edge : current->use_edges()) {
     321             :         // Mark uses for revisitation.
     322             :         Node* use = edge.from();
     323   104129863 :         if (NodeProperties::IsEffectEdge(edge)) {
     324    15206787 :           if (reduction.effect_changed()) Revisit(use);
     325             :         } else {
     326    88925174 :           if (reduction.value_changed()) Revisit(use);
     327             :         }
     328             :       }
     329             :       state_.Set(current, State::kVisited);
     330             :       // Process the revisitation buffer immediately. This improves performance
     331             :       // of escape analysis. Using a stack for {revisit_} reverses the order in
     332             :       // which the revisitation happens. This also seems to improve performance.
     333    35558857 :       while (!revisit_.empty()) {
     334     2347017 :         Node* revisit = revisit_.top();
     335     2347017 :         if (state_.Get(revisit) == State::kRevisit) {
     336             :           state_.Set(revisit, State::kOnStack);
     337     4694279 :           stack_.push({revisit, 0});
     338             :         }
     339             :         revisit_.pop();
     340             :       }
     341             :     }
     342             :   }
     343      464168 : }
     344             : 
     345    10849347 : void EffectGraphReducer::Revisit(Node* node) {
     346    21698694 :   if (state_.Get(node) == State::kVisited) {
     347             :     TRACE("  Queueing for revisit: %s#%d\n", node->op()->mnemonic(),
     348             :           node->id());
     349             :     state_.Set(node, State::kRevisit);
     350             :     revisit_.push(node);
     351             :   }
     352    10849319 : }
     353             : 
     354      464161 : VariableTracker::VariableTracker(JSGraph* graph, EffectGraphReducer* reducer,
     355             :                                  Zone* zone)
     356             :     : zone_(zone),
     357             :       graph_(graph),
     358             :       table_(zone, State(zone)),
     359             :       buffer_(zone),
     360      928322 :       reducer_(reducer) {}
     361             : 
     362    33212390 : VariableTracker::Scope::Scope(VariableTracker* states, Node* node,
     363             :                               Reduction* reduction)
     364             :     : ReduceScope(node, reduction),
     365             :       states_(states),
     366    33212390 :       current_state_(states->zone_) {
     367    33212390 :   switch (node->opcode()) {
     368             :     case IrOpcode::kEffectPhi:
     369      384422 :       current_state_ = states_->MergeInputs(node);
     370      384418 :       break;
     371             :     default:
     372             :       int effect_inputs = node->op()->EffectInputCount();
     373    32827968 :       if (effect_inputs == 1) {
     374             :         current_state_ =
     375    28347960 :             states_->table_.Get(NodeProperties::GetEffectInput(node, 0));
     376             :       } else {
     377             :         DCHECK_EQ(0, effect_inputs);
     378             :       }
     379             :   }
     380    33212094 : }
     381             : 
     382    66423368 : VariableTracker::Scope::~Scope() {
     383    66423581 :   if (!reduction()->effect_changed() &&
     384    33211710 :       states_->table_.Get(current_node()) != current_state_) {
     385             :     reduction()->set_effect_changed();
     386             :   }
     387    33211871 :   states_->table_.Set(current_node(), current_state_);
     388    33211694 : }
     389             : 
     390      384427 : VariableTracker::State VariableTracker::MergeInputs(Node* effect_phi) {
     391             :   // A variable that is mapped to [nullptr] was not assigned a value on every
     392             :   // execution path to the current effect phi. Relying on the invariant that
     393             :   // every variable is initialized (at least with a sentinel like the Dead
     394             :   // node), this means that the variable initialization does not dominate the
     395             :   // current point. So for loop effect phis, we can keep nullptr for a variable
     396             :   // as long as the first input of the loop has nullptr for this variable. For
     397             :   // non-loop effect phis, we can even keep it nullptr as long as any input has
     398             :   // nullptr.
     399             :   DCHECK_EQ(IrOpcode::kEffectPhi, effect_phi->opcode());
     400             :   int arity = effect_phi->op()->EffectInputCount();
     401      384427 :   Node* control = NodeProperties::GetControlInput(effect_phi, 0);
     402             :   TRACE("control: %s#%d\n", control->op()->mnemonic(), control->id());
     403      384408 :   bool is_loop = control->opcode() == IrOpcode::kLoop;
     404      384408 :   buffer_.reserve(arity + 1);
     405             : 
     406      768802 :   State first_input = table_.Get(NodeProperties::GetEffectInput(effect_phi, 0));
     407      384399 :   State result = first_input;
     408     7538324 :   for (std::pair<Variable, Node*> var_value : first_input) {
     409     3579823 :     if (Node* value = var_value.second) {
     410     3580857 :       Variable var = var_value.first;
     411             :       TRACE("var %i:\n", var.id_);
     412             :       buffer_.clear();
     413     3580857 :       buffer_.push_back(value);
     414             :       bool identical_inputs = true;
     415             :       int num_defined_inputs = 1;
     416             :       TRACE("  input 0: %s#%d\n", value->op()->mnemonic(), value->id());
     417    21542581 :       for (int i = 1; i < arity; ++i) {
     418             :         Node* next_value =
     419    17997250 :             table_.Get(NodeProperties::GetEffectInput(effect_phi, i)).Get(var);
     420     8977628 :         if (next_value != value) identical_inputs = false;
     421     8977628 :         if (next_value != nullptr) {
     422     7874523 :           num_defined_inputs++;
     423             :           TRACE("  input %i: %s#%d\n", i, next_value->op()->mnemonic(),
     424             :                 next_value->id());
     425             :         } else {
     426             :           TRACE("  input %i: nullptr\n", i);
     427             :         }
     428     8977628 :         buffer_.push_back(next_value);
     429             :       }
     430             : 
     431     3570971 :       Node* old_value = table_.Get(effect_phi).Get(var);
     432             :       if (old_value) {
     433             :         TRACE("  old: %s#%d\n", old_value->op()->mnemonic(), old_value->id());
     434             :       } else {
     435             :         TRACE("  old: nullptr\n");
     436             :       }
     437             :       // Reuse a previously created phi node if possible.
     438     5300470 :       if (old_value && old_value->opcode() == IrOpcode::kPhi &&
     439      325610 :           NodeProperties::GetControlInput(old_value, 0) == control) {
     440             :         // Since a phi node can never dominate its control node,
     441             :         // [old_value] cannot originate from the inputs. Thus [old_value]
     442             :         // must have been created by a previous reduction of this [effect_phi].
     443     2164842 :         for (int i = 0; i < arity; ++i) {
     444     1012821 :           NodeProperties::ReplaceValueInput(
     445     2025622 :               old_value, buffer_[i] ? buffer_[i] : graph_->Dead(), i);
     446             :           // This change cannot affect the rest of the reducer, so there is no
     447             :           // need to trigger additional revisitations.
     448             :         }
     449      139240 :         result.Set(var, old_value);
     450             :       } else {
     451     3424504 :         if (num_defined_inputs == 1 && is_loop) {
     452             :           // For loop effect phis, the variable initialization dominates iff it
     453             :           // dominates the first input.
     454             :           DCHECK_EQ(2, arity);
     455             :           DCHECK_EQ(value, buffer_[0]);
     456      263120 :           result.Set(var, value);
     457     3161384 :         } else if (num_defined_inputs < arity) {
     458             :           // If the variable is undefined on some input of this non-loop effect
     459             :           // phi, then its initialization does not dominate this point.
     460      207535 :           result.Set(var, nullptr);
     461             :         } else {
     462             :           DCHECK_EQ(num_defined_inputs, arity);
     463             :           // We only create a phi if the values are different.
     464     2953849 :           if (identical_inputs) {
     465     2796753 :             result.Set(var, value);
     466             :           } else {
     467             :             TRACE("Creating new phi\n");
     468      157096 :             buffer_.push_back(control);
     469      157096 :             Node* phi = graph_->graph()->NewNode(
     470      157096 :                 graph_->common()->Phi(MachineRepresentation::kTagged, arity),
     471      157096 :                 arity + 1, &buffer_.front());
     472             :             // TODO(tebbi): Computing precise types here is tricky, because of
     473             :             // the necessary revisitations. If we really need this, we should
     474             :             // probably do it afterwards.
     475             :             NodeProperties::SetType(phi, Type::Any());
     476      157096 :             reducer_->AddRoot(phi);
     477      157096 :             result.Set(var, phi);
     478             :           }
     479             :         }
     480             :       }
     481             : #ifdef DEBUG
     482             :       if (Node* result_node = result.Get(var)) {
     483             :         TRACE("  result: %s#%d\n", result_node->op()->mnemonic(),
     484             :               result_node->id());
     485             :       } else {
     486             :         TRACE("  result: nullptr\n");
     487             :       }
     488             : #endif
     489             :     }
     490             :   }
     491      384417 :   return result;
     492             : }
     493             : 
     494             : namespace {
     495             : 
     496             : int OffsetOfFieldAccess(const Operator* op) {
     497             :   DCHECK(op->opcode() == IrOpcode::kLoadField ||
     498             :          op->opcode() == IrOpcode::kStoreField);
     499      970970 :   FieldAccess access = FieldAccessOf(op);
     500             :   return access.offset;
     501             : }
     502             : 
     503             : int OffsetOfElementAt(ElementAccess const& access, int index) {
     504             :   DCHECK_GE(index, 0);
     505             :   DCHECK_GE(ElementSizeLog2Of(access.machine_type.representation()),
     506             :             kTaggedSizeLog2);
     507       47398 :   return access.header_size +
     508       47542 :          (index << ElementSizeLog2Of(access.machine_type.representation()));
     509             : }
     510             : 
     511       46904 : Maybe<int> OffsetOfElementsAccess(const Operator* op, Node* index_node) {
     512             :   DCHECK(op->opcode() == IrOpcode::kLoadElement ||
     513             :          op->opcode() == IrOpcode::kStoreElement);
     514       46904 :   Type index_type = NodeProperties::GetType(index_node);
     515       46904 :   if (!index_type.Is(Type::OrderedNumber())) return Nothing<int>();
     516       46904 :   double max = index_type.Max();
     517       46904 :   double min = index_type.Min();
     518       46904 :   int index = static_cast<int>(min);
     519       46904 :   if (index < 0 || index != min || index != max) return Nothing<int>();
     520       45875 :   return Just(OffsetOfElementAt(ElementAccessOf(op), index));
     521             : }
     522             : 
     523         123 : Node* LowerCompareMapsWithoutLoad(Node* checked_map,
     524             :                                   ZoneHandleSet<Map> const& checked_against,
     525             :                                   JSGraph* jsgraph) {
     526         123 :   Node* true_node = jsgraph->TrueConstant();
     527         123 :   Node* false_node = jsgraph->FalseConstant();
     528             :   Node* replacement = false_node;
     529         246 :   for (Handle<Map> map : checked_against) {
     530         123 :     Node* map_node = jsgraph->HeapConstant(map);
     531             :     // We cannot create a HeapConstant type here as we are off-thread.
     532             :     NodeProperties::SetType(map_node, Type::Internal());
     533         123 :     Node* comparison = jsgraph->graph()->NewNode(
     534             :         jsgraph->simplified()->ReferenceEqual(), checked_map, map_node);
     535             :     NodeProperties::SetType(comparison, Type::Boolean());
     536         123 :     if (replacement == false_node) {
     537             :       replacement = comparison;
     538             :     } else {
     539           0 :       replacement = jsgraph->graph()->NewNode(
     540             :           jsgraph->common()->Select(MachineRepresentation::kTaggedPointer),
     541             :           comparison, true_node, replacement);
     542             :       NodeProperties::SetType(replacement, Type::Boolean());
     543             :     }
     544             :   }
     545         123 :   return replacement;
     546             : }
     547             : 
     548    33212009 : void ReduceNode(const Operator* op, EscapeAnalysisTracker::Scope* current,
     549             :                 JSGraph* jsgraph) {
     550    33212009 :   switch (op->opcode()) {
     551             :     case IrOpcode::kAllocate: {
     552      298867 :       NumberMatcher size(current->ValueInput(0));
     553      298868 :       if (!size.HasValue()) break;
     554      298867 :       int size_int = static_cast<int>(size.Value());
     555      298867 :       if (size_int != size.Value()) break;
     556      298867 :       if (const VirtualObject* vobject = current->InitVirtualObject(size_int)) {
     557             :         // Initialize with dead nodes as a sentinel for uninitialized memory.
     558     1876631 :         for (Variable field : *vobject) {
     559     1633794 :           current->Set(field, jsgraph->Dead());
     560             :         }
     561             :       }
     562             :       break;
     563             :     }
     564             :     case IrOpcode::kFinishRegion:
     565      312058 :       current->SetVirtualObject(current->ValueInput(0));
     566             :       break;
     567             :     case IrOpcode::kStoreField: {
     568     3379831 :       Node* object = current->ValueInput(0);
     569     3379829 :       Node* value = current->ValueInput(1);
     570     3379825 :       const VirtualObject* vobject = current->GetVirtualObject(object);
     571             :       Variable var;
     572     4332436 :       if (vobject && !vobject->HasEscaped() &&
     573      952607 :           vobject->FieldAt(OffsetOfFieldAccess(op)).To(&var)) {
     574             :         current->Set(var, value);
     575      952610 :         current->MarkForDeletion();
     576             :       } else {
     577     2427224 :         current->SetEscaped(object);
     578     2427224 :         current->SetEscaped(value);
     579             :       }
     580             :       break;
     581             :     }
     582             :     case IrOpcode::kStoreElement: {
     583      103800 :       Node* object = current->ValueInput(0);
     584      103800 :       Node* index = current->ValueInput(1);
     585      103800 :       Node* value = current->ValueInput(2);
     586      103800 :       const VirtualObject* vobject = current->GetVirtualObject(object);
     587             :       int offset;
     588             :       Variable var;
     589      242631 :       if (vobject && !vobject->HasEscaped() &&
     590      194951 :           OffsetOfElementsAccess(op, index).To(&offset) &&
     591       45568 :           vobject->FieldAt(offset).To(&var)) {
     592             :         current->Set(var, value);
     593       45568 :         current->MarkForDeletion();
     594             :       } else {
     595       58232 :         current->SetEscaped(value);
     596       58232 :         current->SetEscaped(object);
     597             :       }
     598             :       break;
     599             :     }
     600             :     case IrOpcode::kLoadField: {
     601     1334004 :       Node* object = current->ValueInput(0);
     602     1334003 :       const VirtualObject* vobject = current->GetVirtualObject(object);
     603             :       Variable var;
     604             :       Node* value;
     605     1451106 :       if (vobject && !vobject->HasEscaped() &&
     606     1370709 :           vobject->FieldAt(OffsetOfFieldAccess(op)).To(&var) &&
     607       18342 :           current->Get(var).To(&value)) {
     608       18342 :         current->SetReplacement(value);
     609             :       } else {
     610     1315662 :         current->SetEscaped(object);
     611             :       }
     612             :       break;
     613             :     }
     614             :     case IrOpcode::kLoadElement: {
     615       26932 :       Node* object = current->ValueInput(0);
     616       26932 :       Node* index = current->ValueInput(1);
     617       26932 :       const VirtualObject* vobject = current->GetVirtualObject(object);
     618             :       int offset;
     619             :       Variable var;
     620             :       Node* value;
     621       30474 :       if (vobject && !vobject->HasEscaped() &&
     622        1628 :           OffsetOfElementsAccess(op, index).To(&offset) &&
     623       27837 :           vobject->FieldAt(offset).To(&var) && current->Get(var).To(&value)) {
     624         299 :         current->SetReplacement(value);
     625       26633 :       } else if (vobject && !vobject->HasEscaped()) {
     626             :         // Compute the known length (aka the number of elements) of {object}
     627             :         // based on the virtual object information.
     628        1022 :         ElementAccess const& access = ElementAccessOf(op);
     629             :         int const length =
     630        1022 :             (vobject->size() - access.header_size) >>
     631        1022 :             ElementSizeLog2Of(access.machine_type.representation());
     632             :         Variable var0, var1;
     633             :         Node* value0;
     634             :         Node* value1;
     635        1166 :         if (length == 1 &&
     636         288 :             vobject->FieldAt(OffsetOfElementAt(access, 0)).To(&var) &&
     637        1310 :             current->Get(var).To(&value) &&
     638          98 :             (value == nullptr ||
     639        1120 :              NodeProperties::GetType(value).Is(access.type))) {
     640             :           // The {object} has no elements, and we know that the LoadElement
     641             :           // {index} must be within bounds, thus it must always yield this
     642             :           // one element of {object}.
     643         144 :           current->SetReplacement(value);
     644         144 :           break;
     645        1643 :         } else if (length == 2 &&
     646        1530 :                    vobject->FieldAt(OffsetOfElementAt(access, 0)).To(&var0) &&
     647        1530 :                    current->Get(var0).To(&value0) &&
     648         458 :                    (value0 == nullptr ||
     649        2094 :                     NodeProperties::GetType(value0).Is(access.type)) &&
     650        1516 :                    vobject->FieldAt(OffsetOfElementAt(access, 1)).To(&var1) &&
     651        2394 :                    current->Get(var1).To(&value1) &&
     652         451 :                    (value1 == nullptr ||
     653        1329 :                     NodeProperties::GetType(value1).Is(access.type))) {
     654         758 :           if (value0 && value1) {
     655             :             // The {object} has exactly two elements, so the LoadElement
     656             :             // must return one of them (i.e. either the element at index
     657             :             // 0 or the one at index 1). So we can turn the LoadElement
     658             :             // into a Select operation instead (still allowing the {object}
     659             :             // to be scalar replaced). We must however mark the elements
     660             :             // of the {object} itself as escaping.
     661             :             Node* check =
     662         451 :                 jsgraph->graph()->NewNode(jsgraph->simplified()->NumberEqual(),
     663             :                                           index, jsgraph->ZeroConstant());
     664             :             NodeProperties::SetType(check, Type::Boolean());
     665         451 :             Node* select = jsgraph->graph()->NewNode(
     666             :                 jsgraph->common()->Select(access.machine_type.representation()),
     667             :                 check, value0, value1);
     668             :             NodeProperties::SetType(select, access.type);
     669         451 :             current->SetReplacement(select);
     670         451 :             current->SetEscaped(value0);
     671         451 :             current->SetEscaped(value1);
     672         451 :             break;
     673             :           } else {
     674             :             // If the variables have no values, we have
     675             :             // not reached the fixed-point yet.
     676             :             break;
     677             :           }
     678             :         }
     679             :       }
     680       26030 :       current->SetEscaped(object);
     681       26030 :       break;
     682             :     }
     683             :     case IrOpcode::kTypeGuard: {
     684       31408 :       current->SetVirtualObject(current->ValueInput(0));
     685             :       break;
     686             :     }
     687             :     case IrOpcode::kReferenceEqual: {
     688      199662 :       Node* left = current->ValueInput(0);
     689      199662 :       Node* right = current->ValueInput(1);
     690      199662 :       const VirtualObject* left_object = current->GetVirtualObject(left);
     691      199662 :       const VirtualObject* right_object = current->GetVirtualObject(right);
     692             :       Node* replacement = nullptr;
     693      199662 :       if (left_object && !left_object->HasEscaped()) {
     694         256 :         if (right_object && !right_object->HasEscaped() &&
     695             :             left_object->id() == right_object->id()) {
     696           4 :           replacement = jsgraph->TrueConstant();
     697             :         } else {
     698         252 :           replacement = jsgraph->FalseConstant();
     699             :         }
     700      199406 :       } else if (right_object && !right_object->HasEscaped()) {
     701         470 :         replacement = jsgraph->FalseConstant();
     702             :       }
     703      199662 :       if (replacement) {
     704             :         // TODO(tebbi) This is a workaround for uninhabited types. If we
     705             :         // replaced a value of uninhabited type with a constant, we would
     706             :         // widen the type of the node. This could produce inconsistent
     707             :         // types (which might confuse representation selection). We get
     708             :         // around this by refusing to constant-fold and escape-analyze
     709             :         // if the type is not inhabited.
     710         726 :         if (!NodeProperties::GetType(left).IsNone() &&
     711             :             !NodeProperties::GetType(right).IsNone()) {
     712         726 :           current->SetReplacement(replacement);
     713             :         } else {
     714           0 :           current->SetEscaped(left);
     715           0 :           current->SetEscaped(right);
     716             :         }
     717             :       }
     718             :       break;
     719             :     }
     720             :     case IrOpcode::kCheckMaps: {
     721       68012 :       CheckMapsParameters params = CheckMapsParametersOf(op);
     722       68012 :       Node* checked = current->ValueInput(0);
     723       68012 :       const VirtualObject* vobject = current->GetVirtualObject(checked);
     724             :       Variable map_field;
     725             :       Node* map;
     726       85016 :       if (vobject && !vobject->HasEscaped() &&
     727       79090 :           vobject->FieldAt(HeapObject::kMapOffset).To(&map_field) &&
     728        5539 :           current->Get(map_field).To(&map)) {
     729        5539 :         if (map) {
     730        4439 :           Type const map_type = NodeProperties::GetType(map);
     731             :           AllowHandleDereference handle_dereference;
     732        8865 :           if (map_type.IsHeapConstant() &&
     733        4426 :               params.maps().contains(
     734             :                   Handle<Map>::cast(map_type.AsHeapConstant()->Value()))) {
     735        4334 :             current->MarkForDeletion();
     736        4334 :             break;
     737             :           }
     738             :         } else {
     739             :           // If the variable has no value, we have not reached the fixed-point
     740             :           // yet.
     741             :           break;
     742             :         }
     743             :       }
     744       62578 :       current->SetEscaped(checked);
     745       62578 :       break;
     746             :     }
     747             :     case IrOpcode::kCompareMaps: {
     748        9387 :       Node* object = current->ValueInput(0);
     749        9387 :       const VirtualObject* vobject = current->GetVirtualObject(object);
     750             :       Variable map_field;
     751             :       Node* object_map;
     752        9777 :       if (vobject && !vobject->HasEscaped() &&
     753        9647 :           vobject->FieldAt(HeapObject::kMapOffset).To(&map_field) &&
     754         130 :           current->Get(map_field).To(&object_map)) {
     755         130 :         if (object_map) {
     756         123 :           current->SetReplacement(LowerCompareMapsWithoutLoad(
     757         246 :               object_map, CompareMapsParametersOf(op), jsgraph));
     758         123 :           break;
     759             :         } else {
     760             :           // If the variable has no value, we have not reached the fixed-point
     761             :           // yet.
     762             :           break;
     763             :         }
     764             :       }
     765        9257 :       current->SetEscaped(object);
     766        9257 :       break;
     767             :     }
     768             :     case IrOpcode::kCheckHeapObject: {
     769       31935 :       Node* checked = current->ValueInput(0);
     770       31935 :       switch (checked->opcode()) {
     771             :         case IrOpcode::kAllocate:
     772             :         case IrOpcode::kFinishRegion:
     773             :         case IrOpcode::kHeapConstant:
     774         116 :           current->SetReplacement(checked);
     775         116 :           break;
     776             :         default:
     777       31819 :           current->SetEscaped(checked);
     778       31819 :           break;
     779             :       }
     780             :       break;
     781             :     }
     782             :     case IrOpcode::kMapGuard: {
     783        8224 :       Node* object = current->ValueInput(0);
     784        8224 :       const VirtualObject* vobject = current->GetVirtualObject(object);
     785        8224 :       if (vobject && !vobject->HasEscaped()) {
     786         130 :         current->MarkForDeletion();
     787             :       }
     788             :       break;
     789             :     }
     790             :     case IrOpcode::kStateValues:
     791             :     case IrOpcode::kFrameState:
     792             :       // These uses are always safe.
     793             :       break;
     794             :     default: {
     795             :       // For unknown nodes, treat all value inputs as escaping.
     796             :       int value_input_count = op->ValueInputCount();
     797    43141213 :       for (int i = 0; i < value_input_count; ++i) {
     798    12057775 :         Node* input = current->ValueInput(i);
     799    12057771 :         current->SetEscaped(input);
     800             :       }
     801    19025774 :       if (OperatorProperties::HasContextInput(op)) {
     802     3628477 :         current->SetEscaped(current->ContextInput());
     803             :       }
     804             :       break;
     805             :     }
     806             :   }
     807    33211845 : }
     808             : 
     809             : }  // namespace
     810             : 
     811    33211880 : void EscapeAnalysis::Reduce(Node* node, Reduction* reduction) {
     812             :   const Operator* op = node->op();
     813             :   TRACE("Reducing %s#%d\n", op->mnemonic(), node->id());
     814             : 
     815    66423582 :   EscapeAnalysisTracker::Scope current(this, tracker_, node, reduction);
     816    33211581 :   ReduceNode(op, &current, jsgraph());
     817    33211715 : }
     818             : 
     819      464161 : EscapeAnalysis::EscapeAnalysis(JSGraph* jsgraph, Zone* zone)
     820             :     : EffectGraphReducer(
     821             :           jsgraph->graph(),
     822    33211756 :           [this](Node* node, Reduction* reduction) { Reduce(node, reduction); },
     823             :           zone),
     824      464165 :       tracker_(new (zone) EscapeAnalysisTracker(jsgraph, this, zone)),
     825     1392486 :       jsgraph_(jsgraph) {}
     826             : 
     827    30931392 : Node* EscapeAnalysisResult::GetReplacementOf(Node* node) {
     828    30931392 :   Node* replacement = tracker_->GetReplacementOf(node);
     829             :   // Replacements cannot have replacements. This is important to ensure
     830             :   // re-visitation: If a replacement is replaced, then all nodes accessing
     831             :   // the replacement have to be updated.
     832             :   if (replacement) DCHECK_NULL(tracker_->GetReplacementOf(replacement));
     833    30931492 :   return replacement;
     834             : }
     835             : 
     836      676531 : Node* EscapeAnalysisResult::GetVirtualObjectField(const VirtualObject* vobject,
     837             :                                                   int field, Node* effect) {
     838     2029593 :   return tracker_->variable_states_.Get(vobject->FieldAt(field).FromJust(),
     839     1353062 :                                         effect);
     840             : }
     841             : 
     842    53358268 : const VirtualObject* EscapeAnalysisResult::GetVirtualObject(Node* node) {
     843   106716787 :   return tracker_->virtual_objects_.Get(node);
     844             : }
     845             : 
     846      112350 : VirtualObject::VirtualObject(VariableTracker* var_states, VirtualObject::Id id,
     847             :                              int size)
     848      112350 :     : Dependable(var_states->zone()), id_(id), fields_(var_states->zone()) {
     849             :   DCHECK(IsAligned(size, kTaggedSize));
     850             :   TRACE("Creating VirtualObject id:%d size:%d\n", id, size);
     851      112350 :   int num_fields = size / kTaggedSize;
     852      112350 :   fields_.reserve(num_fields);
     853     1645826 :   for (int i = 0; i < num_fields; ++i) {
     854     1533476 :     fields_.push_back(var_states->NewVariable());
     855             :   }
     856      112350 : }
     857             : 
     858             : #undef TRACE
     859             : 
     860             : }  // namespace compiler
     861             : }  // namespace internal
     862      122004 : }  // namespace v8

Generated by: LCOV version 1.10