11cb0ef41Sopenharmony_ci// Copyright 2017 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include "src/compiler/escape-analysis.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include "src/codegen/tick-counter.h"
81cb0ef41Sopenharmony_ci#include "src/compiler/frame-states.h"
91cb0ef41Sopenharmony_ci#include "src/compiler/linkage.h"
101cb0ef41Sopenharmony_ci#include "src/compiler/node-matchers.h"
111cb0ef41Sopenharmony_ci#include "src/compiler/operator-properties.h"
121cb0ef41Sopenharmony_ci#include "src/compiler/simplified-operator.h"
131cb0ef41Sopenharmony_ci#include "src/compiler/state-values-utils.h"
141cb0ef41Sopenharmony_ci#include "src/handles/handles-inl.h"
151cb0ef41Sopenharmony_ci#include "src/init/bootstrapper.h"
161cb0ef41Sopenharmony_ci#include "src/objects/map-inl.h"
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ci#ifdef DEBUG
191cb0ef41Sopenharmony_ci#define TRACE(...)                                    \
201cb0ef41Sopenharmony_ci  do {                                                \
211cb0ef41Sopenharmony_ci    if (FLAG_trace_turbo_escape) PrintF(__VA_ARGS__); \
221cb0ef41Sopenharmony_ci  } while (false)
231cb0ef41Sopenharmony_ci#else
241cb0ef41Sopenharmony_ci#define TRACE(...)
251cb0ef41Sopenharmony_ci#endif
261cb0ef41Sopenharmony_ci
271cb0ef41Sopenharmony_cinamespace v8 {
281cb0ef41Sopenharmony_cinamespace internal {
291cb0ef41Sopenharmony_cinamespace compiler {
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_citemplate <class T>
321cb0ef41Sopenharmony_ciclass Sidetable {
331cb0ef41Sopenharmony_ci public:
341cb0ef41Sopenharmony_ci  explicit Sidetable(Zone* zone) : map_(zone) {}
351cb0ef41Sopenharmony_ci  T& operator[](const Node* node) {
361cb0ef41Sopenharmony_ci    NodeId id = node->id();
371cb0ef41Sopenharmony_ci    if (id >= map_.size()) {
381cb0ef41Sopenharmony_ci      map_.resize(id + 1);
391cb0ef41Sopenharmony_ci    }
401cb0ef41Sopenharmony_ci    return map_[id];
411cb0ef41Sopenharmony_ci  }
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_ci private:
441cb0ef41Sopenharmony_ci  ZoneVector<T> map_;
451cb0ef41Sopenharmony_ci};
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_citemplate <class T>
481cb0ef41Sopenharmony_ciclass SparseSidetable {
491cb0ef41Sopenharmony_ci public:
501cb0ef41Sopenharmony_ci  explicit SparseSidetable(Zone* zone, T def_value = T())
511cb0ef41Sopenharmony_ci      : def_value_(std::move(def_value)), map_(zone) {}
521cb0ef41Sopenharmony_ci  void Set(const Node* node, T value) {
531cb0ef41Sopenharmony_ci    auto iter = map_.find(node->id());
541cb0ef41Sopenharmony_ci    if (iter != map_.end()) {
551cb0ef41Sopenharmony_ci      iter->second = std::move(value);
561cb0ef41Sopenharmony_ci    } else if (value != def_value_) {
571cb0ef41Sopenharmony_ci      map_.insert(iter, std::make_pair(node->id(), std::move(value)));
581cb0ef41Sopenharmony_ci    }
591cb0ef41Sopenharmony_ci  }
601cb0ef41Sopenharmony_ci  const T& Get(const Node* node) const {
611cb0ef41Sopenharmony_ci    auto iter = map_.find(node->id());
621cb0ef41Sopenharmony_ci    return iter != map_.end() ? iter->second : def_value_;
631cb0ef41Sopenharmony_ci  }
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_ci private:
661cb0ef41Sopenharmony_ci  T def_value_;
671cb0ef41Sopenharmony_ci  ZoneUnorderedMap<NodeId, T> map_;
681cb0ef41Sopenharmony_ci};
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci// Keeps track of the changes to the current node during reduction.
711cb0ef41Sopenharmony_ci// Encapsulates the current state of the IR graph and the reducer state like
721cb0ef41Sopenharmony_ci// side-tables. All access to the IR and the reducer state should happen through
731cb0ef41Sopenharmony_ci// a ReduceScope to ensure that changes and dependencies are tracked and all
741cb0ef41Sopenharmony_ci// necessary node revisitations happen.
751cb0ef41Sopenharmony_ciclass ReduceScope {
761cb0ef41Sopenharmony_ci public:
771cb0ef41Sopenharmony_ci  using Reduction = EffectGraphReducer::Reduction;
781cb0ef41Sopenharmony_ci  explicit ReduceScope(Node* node, Reduction* reduction)
791cb0ef41Sopenharmony_ci      : current_node_(node), reduction_(reduction) {}
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_ci  void SetValueChanged() { reduction()->set_value_changed(); }
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci protected:
841cb0ef41Sopenharmony_ci  Node* current_node() const { return current_node_; }
851cb0ef41Sopenharmony_ci  Reduction* reduction() { return reduction_; }
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci private:
881cb0ef41Sopenharmony_ci  Node* current_node_;
891cb0ef41Sopenharmony_ci  Reduction* reduction_;
901cb0ef41Sopenharmony_ci};
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci// A VariableTracker object keeps track of the values of variables at all points
931cb0ef41Sopenharmony_ci// of the effect chain and introduces new phi nodes when necessary.
941cb0ef41Sopenharmony_ci// Initially and by default, variables are mapped to nullptr, which means that
951cb0ef41Sopenharmony_ci// the variable allocation point does not dominate the current point on the
961cb0ef41Sopenharmony_ci// effect chain. We map variables that represent uninitialized memory to the
971cb0ef41Sopenharmony_ci// Dead node to ensure it is not read.
981cb0ef41Sopenharmony_ci// Unmapped values are impossible by construction, it is indistinguishable if a
991cb0ef41Sopenharmony_ci// PersistentMap does not contain an element or maps it to the default element.
1001cb0ef41Sopenharmony_ciclass VariableTracker {
1011cb0ef41Sopenharmony_ci private:
1021cb0ef41Sopenharmony_ci  // The state of all variables at one point in the effect chain.
1031cb0ef41Sopenharmony_ci  class State {
1041cb0ef41Sopenharmony_ci   public:
1051cb0ef41Sopenharmony_ci    using Map = PersistentMap<Variable, Node*>;
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci    explicit State(Zone* zone) : map_(zone) {}
1081cb0ef41Sopenharmony_ci    Node* Get(Variable var) const {
1091cb0ef41Sopenharmony_ci      CHECK(var != Variable::Invalid());
1101cb0ef41Sopenharmony_ci      return map_.Get(var);
1111cb0ef41Sopenharmony_ci    }
1121cb0ef41Sopenharmony_ci    void Set(Variable var, Node* node) {
1131cb0ef41Sopenharmony_ci      CHECK(var != Variable::Invalid());
1141cb0ef41Sopenharmony_ci      return map_.Set(var, node);
1151cb0ef41Sopenharmony_ci    }
1161cb0ef41Sopenharmony_ci    Map::iterator begin() const { return map_.begin(); }
1171cb0ef41Sopenharmony_ci    Map::iterator end() const { return map_.end(); }
1181cb0ef41Sopenharmony_ci    bool operator!=(const State& other) const { return map_ != other.map_; }
1191cb0ef41Sopenharmony_ci
1201cb0ef41Sopenharmony_ci   private:
1211cb0ef41Sopenharmony_ci    Map map_;
1221cb0ef41Sopenharmony_ci  };
1231cb0ef41Sopenharmony_ci
1241cb0ef41Sopenharmony_ci public:
1251cb0ef41Sopenharmony_ci  VariableTracker(JSGraph* graph, EffectGraphReducer* reducer, Zone* zone);
1261cb0ef41Sopenharmony_ci  VariableTracker(const VariableTracker&) = delete;
1271cb0ef41Sopenharmony_ci  VariableTracker& operator=(const VariableTracker&) = delete;
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci  Variable NewVariable() { return Variable(next_variable_++); }
1301cb0ef41Sopenharmony_ci  Node* Get(Variable var, Node* effect) { return table_.Get(effect).Get(var); }
1311cb0ef41Sopenharmony_ci  Zone* zone() { return zone_; }
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_ci  class V8_NODISCARD Scope : public ReduceScope {
1341cb0ef41Sopenharmony_ci   public:
1351cb0ef41Sopenharmony_ci    Scope(VariableTracker* tracker, Node* node, Reduction* reduction);
1361cb0ef41Sopenharmony_ci    ~Scope();
1371cb0ef41Sopenharmony_ci    Maybe<Node*> Get(Variable var) {
1381cb0ef41Sopenharmony_ci      Node* node = current_state_.Get(var);
1391cb0ef41Sopenharmony_ci      if (node && node->opcode() == IrOpcode::kDead) {
1401cb0ef41Sopenharmony_ci        // TODO(turbofan): We use {Dead} as a sentinel for uninitialized memory.
1411cb0ef41Sopenharmony_ci        // Reading uninitialized memory can only happen in unreachable code. In
1421cb0ef41Sopenharmony_ci        // this case, we have to mark the object as escaping to avoid dead nodes
1431cb0ef41Sopenharmony_ci        // in the graph. This is a workaround that should be removed once we can
1441cb0ef41Sopenharmony_ci        // handle dead nodes everywhere.
1451cb0ef41Sopenharmony_ci        return Nothing<Node*>();
1461cb0ef41Sopenharmony_ci      }
1471cb0ef41Sopenharmony_ci      return Just(node);
1481cb0ef41Sopenharmony_ci    }
1491cb0ef41Sopenharmony_ci    void Set(Variable var, Node* node) { current_state_.Set(var, node); }
1501cb0ef41Sopenharmony_ci
1511cb0ef41Sopenharmony_ci   private:
1521cb0ef41Sopenharmony_ci    VariableTracker* states_;
1531cb0ef41Sopenharmony_ci    State current_state_;
1541cb0ef41Sopenharmony_ci  };
1551cb0ef41Sopenharmony_ci
1561cb0ef41Sopenharmony_ci private:
1571cb0ef41Sopenharmony_ci  State MergeInputs(Node* effect_phi);
1581cb0ef41Sopenharmony_ci  Zone* zone_;
1591cb0ef41Sopenharmony_ci  JSGraph* graph_;
1601cb0ef41Sopenharmony_ci  SparseSidetable<State> table_;
1611cb0ef41Sopenharmony_ci  ZoneVector<Node*> buffer_;
1621cb0ef41Sopenharmony_ci  EffectGraphReducer* reducer_;
1631cb0ef41Sopenharmony_ci  int next_variable_ = 0;
1641cb0ef41Sopenharmony_ci  TickCounter* const tick_counter_;
1651cb0ef41Sopenharmony_ci};
1661cb0ef41Sopenharmony_ci
1671cb0ef41Sopenharmony_ci// Encapsulates the current state of the escape analysis reducer to preserve
1681cb0ef41Sopenharmony_ci// invariants regarding changes and re-visitation.
1691cb0ef41Sopenharmony_ciclass EscapeAnalysisTracker : public ZoneObject {
1701cb0ef41Sopenharmony_ci public:
1711cb0ef41Sopenharmony_ci  EscapeAnalysisTracker(JSGraph* jsgraph, EffectGraphReducer* reducer,
1721cb0ef41Sopenharmony_ci                        Zone* zone)
1731cb0ef41Sopenharmony_ci      : virtual_objects_(zone),
1741cb0ef41Sopenharmony_ci        replacements_(zone),
1751cb0ef41Sopenharmony_ci        variable_states_(jsgraph, reducer, zone),
1761cb0ef41Sopenharmony_ci        jsgraph_(jsgraph),
1771cb0ef41Sopenharmony_ci        zone_(zone) {}
1781cb0ef41Sopenharmony_ci  EscapeAnalysisTracker(const EscapeAnalysisTracker&) = delete;
1791cb0ef41Sopenharmony_ci  EscapeAnalysisTracker& operator=(const EscapeAnalysisTracker&) = delete;
1801cb0ef41Sopenharmony_ci
1811cb0ef41Sopenharmony_ci  class V8_NODISCARD Scope : public VariableTracker::Scope {
1821cb0ef41Sopenharmony_ci   public:
1831cb0ef41Sopenharmony_ci    Scope(EffectGraphReducer* reducer, EscapeAnalysisTracker* tracker,
1841cb0ef41Sopenharmony_ci          Node* node, Reduction* reduction)
1851cb0ef41Sopenharmony_ci        : VariableTracker::Scope(&tracker->variable_states_, node, reduction),
1861cb0ef41Sopenharmony_ci          tracker_(tracker),
1871cb0ef41Sopenharmony_ci          reducer_(reducer) {}
1881cb0ef41Sopenharmony_ci    const VirtualObject* GetVirtualObject(Node* node) {
1891cb0ef41Sopenharmony_ci      VirtualObject* vobject = tracker_->virtual_objects_.Get(node);
1901cb0ef41Sopenharmony_ci      if (vobject) vobject->AddDependency(current_node());
1911cb0ef41Sopenharmony_ci      return vobject;
1921cb0ef41Sopenharmony_ci    }
1931cb0ef41Sopenharmony_ci    // Create or retrieve a virtual object for the current node.
1941cb0ef41Sopenharmony_ci    const VirtualObject* InitVirtualObject(int size) {
1951cb0ef41Sopenharmony_ci      DCHECK_EQ(IrOpcode::kAllocate, current_node()->opcode());
1961cb0ef41Sopenharmony_ci      VirtualObject* vobject = tracker_->virtual_objects_.Get(current_node());
1971cb0ef41Sopenharmony_ci      if (vobject) {
1981cb0ef41Sopenharmony_ci        CHECK(vobject->size() == size);
1991cb0ef41Sopenharmony_ci      } else {
2001cb0ef41Sopenharmony_ci        vobject = tracker_->NewVirtualObject(size);
2011cb0ef41Sopenharmony_ci      }
2021cb0ef41Sopenharmony_ci      if (vobject) vobject->AddDependency(current_node());
2031cb0ef41Sopenharmony_ci      vobject_ = vobject;
2041cb0ef41Sopenharmony_ci      return vobject;
2051cb0ef41Sopenharmony_ci    }
2061cb0ef41Sopenharmony_ci
2071cb0ef41Sopenharmony_ci    void SetVirtualObject(Node* object) {
2081cb0ef41Sopenharmony_ci      vobject_ = tracker_->virtual_objects_.Get(object);
2091cb0ef41Sopenharmony_ci    }
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci    void SetEscaped(Node* node) {
2121cb0ef41Sopenharmony_ci      if (VirtualObject* object = tracker_->virtual_objects_.Get(node)) {
2131cb0ef41Sopenharmony_ci        if (object->HasEscaped()) return;
2141cb0ef41Sopenharmony_ci        TRACE("Setting %s#%d to escaped because of use by %s#%d\n",
2151cb0ef41Sopenharmony_ci              node->op()->mnemonic(), node->id(),
2161cb0ef41Sopenharmony_ci              current_node()->op()->mnemonic(), current_node()->id());
2171cb0ef41Sopenharmony_ci        object->SetEscaped();
2181cb0ef41Sopenharmony_ci        object->RevisitDependants(reducer_);
2191cb0ef41Sopenharmony_ci      }
2201cb0ef41Sopenharmony_ci    }
2211cb0ef41Sopenharmony_ci    // The inputs of the current node have to be accessed through the scope to
2221cb0ef41Sopenharmony_ci    // ensure that they respect the node replacements.
2231cb0ef41Sopenharmony_ci    Node* ValueInput(int i) {
2241cb0ef41Sopenharmony_ci      return tracker_->ResolveReplacement(
2251cb0ef41Sopenharmony_ci          NodeProperties::GetValueInput(current_node(), i));
2261cb0ef41Sopenharmony_ci    }
2271cb0ef41Sopenharmony_ci    Node* ContextInput() {
2281cb0ef41Sopenharmony_ci      return tracker_->ResolveReplacement(
2291cb0ef41Sopenharmony_ci          NodeProperties::GetContextInput(current_node()));
2301cb0ef41Sopenharmony_ci    }
2311cb0ef41Sopenharmony_ci    // Accessing the current node is fine for `FrameState nodes.
2321cb0ef41Sopenharmony_ci    Node* CurrentNode() {
2331cb0ef41Sopenharmony_ci      DCHECK_EQ(current_node()->opcode(), IrOpcode::kFrameState);
2341cb0ef41Sopenharmony_ci      return current_node();
2351cb0ef41Sopenharmony_ci    }
2361cb0ef41Sopenharmony_ci
2371cb0ef41Sopenharmony_ci    void SetReplacement(Node* replacement) {
2381cb0ef41Sopenharmony_ci      replacement_ = replacement;
2391cb0ef41Sopenharmony_ci      vobject_ =
2401cb0ef41Sopenharmony_ci          replacement ? tracker_->virtual_objects_.Get(replacement) : nullptr;
2411cb0ef41Sopenharmony_ci      if (replacement) {
2421cb0ef41Sopenharmony_ci        TRACE("Set %s#%d as replacement.\n", replacement->op()->mnemonic(),
2431cb0ef41Sopenharmony_ci              replacement->id());
2441cb0ef41Sopenharmony_ci      } else {
2451cb0ef41Sopenharmony_ci        TRACE("Set nullptr as replacement.\n");
2461cb0ef41Sopenharmony_ci      }
2471cb0ef41Sopenharmony_ci    }
2481cb0ef41Sopenharmony_ci
2491cb0ef41Sopenharmony_ci    void MarkForDeletion() { SetReplacement(tracker_->jsgraph_->Dead()); }
2501cb0ef41Sopenharmony_ci
2511cb0ef41Sopenharmony_ci    ~Scope() {
2521cb0ef41Sopenharmony_ci      if (replacement_ != tracker_->replacements_[current_node()] ||
2531cb0ef41Sopenharmony_ci          vobject_ != tracker_->virtual_objects_.Get(current_node())) {
2541cb0ef41Sopenharmony_ci        reduction()->set_value_changed();
2551cb0ef41Sopenharmony_ci      }
2561cb0ef41Sopenharmony_ci      tracker_->replacements_[current_node()] = replacement_;
2571cb0ef41Sopenharmony_ci      tracker_->virtual_objects_.Set(current_node(), vobject_);
2581cb0ef41Sopenharmony_ci    }
2591cb0ef41Sopenharmony_ci
2601cb0ef41Sopenharmony_ci   private:
2611cb0ef41Sopenharmony_ci    EscapeAnalysisTracker* tracker_;
2621cb0ef41Sopenharmony_ci    EffectGraphReducer* reducer_;
2631cb0ef41Sopenharmony_ci    VirtualObject* vobject_ = nullptr;
2641cb0ef41Sopenharmony_ci    Node* replacement_ = nullptr;
2651cb0ef41Sopenharmony_ci  };
2661cb0ef41Sopenharmony_ci
2671cb0ef41Sopenharmony_ci  Node* GetReplacementOf(Node* node) { return replacements_[node]; }
2681cb0ef41Sopenharmony_ci  Node* ResolveReplacement(Node* node) {
2691cb0ef41Sopenharmony_ci    if (Node* replacement = GetReplacementOf(node)) {
2701cb0ef41Sopenharmony_ci      return replacement;
2711cb0ef41Sopenharmony_ci    }
2721cb0ef41Sopenharmony_ci    return node;
2731cb0ef41Sopenharmony_ci  }
2741cb0ef41Sopenharmony_ci
2751cb0ef41Sopenharmony_ci private:
2761cb0ef41Sopenharmony_ci  friend class EscapeAnalysisResult;
2771cb0ef41Sopenharmony_ci  static const size_t kMaxTrackedObjects = 100;
2781cb0ef41Sopenharmony_ci
2791cb0ef41Sopenharmony_ci  VirtualObject* NewVirtualObject(int size) {
2801cb0ef41Sopenharmony_ci    if (next_object_id_ >= kMaxTrackedObjects) return nullptr;
2811cb0ef41Sopenharmony_ci    return zone_->New<VirtualObject>(&variable_states_, next_object_id_++,
2821cb0ef41Sopenharmony_ci                                     size);
2831cb0ef41Sopenharmony_ci  }
2841cb0ef41Sopenharmony_ci
2851cb0ef41Sopenharmony_ci  SparseSidetable<VirtualObject*> virtual_objects_;
2861cb0ef41Sopenharmony_ci  Sidetable<Node*> replacements_;
2871cb0ef41Sopenharmony_ci  VariableTracker variable_states_;
2881cb0ef41Sopenharmony_ci  VirtualObject::Id next_object_id_ = 0;
2891cb0ef41Sopenharmony_ci  JSGraph* const jsgraph_;
2901cb0ef41Sopenharmony_ci  Zone* const zone_;
2911cb0ef41Sopenharmony_ci};
2921cb0ef41Sopenharmony_ci
2931cb0ef41Sopenharmony_ciEffectGraphReducer::EffectGraphReducer(
2941cb0ef41Sopenharmony_ci    Graph* graph, std::function<void(Node*, Reduction*)> reduce,
2951cb0ef41Sopenharmony_ci    TickCounter* tick_counter, Zone* zone)
2961cb0ef41Sopenharmony_ci    : graph_(graph),
2971cb0ef41Sopenharmony_ci      state_(graph, kNumStates),
2981cb0ef41Sopenharmony_ci      revisit_(zone),
2991cb0ef41Sopenharmony_ci      stack_(zone),
3001cb0ef41Sopenharmony_ci      reduce_(std::move(reduce)),
3011cb0ef41Sopenharmony_ci      tick_counter_(tick_counter) {}
3021cb0ef41Sopenharmony_ci
3031cb0ef41Sopenharmony_civoid EffectGraphReducer::ReduceFrom(Node* node) {
3041cb0ef41Sopenharmony_ci  // Perform DFS and eagerly trigger revisitation as soon as possible.
3051cb0ef41Sopenharmony_ci  // A stack element {node, i} indicates that input i of node should be visited
3061cb0ef41Sopenharmony_ci  // next.
3071cb0ef41Sopenharmony_ci  DCHECK(stack_.empty());
3081cb0ef41Sopenharmony_ci  stack_.push({node, 0});
3091cb0ef41Sopenharmony_ci  while (!stack_.empty()) {
3101cb0ef41Sopenharmony_ci    tick_counter_->TickAndMaybeEnterSafepoint();
3111cb0ef41Sopenharmony_ci    Node* current = stack_.top().node;
3121cb0ef41Sopenharmony_ci    int& input_index = stack_.top().input_index;
3131cb0ef41Sopenharmony_ci    if (input_index < current->InputCount()) {
3141cb0ef41Sopenharmony_ci      Node* input = current->InputAt(input_index);
3151cb0ef41Sopenharmony_ci      input_index++;
3161cb0ef41Sopenharmony_ci      switch (state_.Get(input)) {
3171cb0ef41Sopenharmony_ci        case State::kVisited:
3181cb0ef41Sopenharmony_ci          // The input is already reduced.
3191cb0ef41Sopenharmony_ci          break;
3201cb0ef41Sopenharmony_ci        case State::kOnStack:
3211cb0ef41Sopenharmony_ci          // The input is on the DFS stack right now, so it will be revisited
3221cb0ef41Sopenharmony_ci          // later anyway.
3231cb0ef41Sopenharmony_ci          break;
3241cb0ef41Sopenharmony_ci        case State::kUnvisited:
3251cb0ef41Sopenharmony_ci        case State::kRevisit: {
3261cb0ef41Sopenharmony_ci          state_.Set(input, State::kOnStack);
3271cb0ef41Sopenharmony_ci          stack_.push({input, 0});
3281cb0ef41Sopenharmony_ci          break;
3291cb0ef41Sopenharmony_ci        }
3301cb0ef41Sopenharmony_ci      }
3311cb0ef41Sopenharmony_ci    } else {
3321cb0ef41Sopenharmony_ci      stack_.pop();
3331cb0ef41Sopenharmony_ci      Reduction reduction;
3341cb0ef41Sopenharmony_ci      reduce_(current, &reduction);
3351cb0ef41Sopenharmony_ci      for (Edge edge : current->use_edges()) {
3361cb0ef41Sopenharmony_ci        // Mark uses for revisitation.
3371cb0ef41Sopenharmony_ci        Node* use = edge.from();
3381cb0ef41Sopenharmony_ci        if (NodeProperties::IsEffectEdge(edge)) {
3391cb0ef41Sopenharmony_ci          if (reduction.effect_changed()) Revisit(use);
3401cb0ef41Sopenharmony_ci        } else {
3411cb0ef41Sopenharmony_ci          if (reduction.value_changed()) Revisit(use);
3421cb0ef41Sopenharmony_ci        }
3431cb0ef41Sopenharmony_ci      }
3441cb0ef41Sopenharmony_ci      state_.Set(current, State::kVisited);
3451cb0ef41Sopenharmony_ci      // Process the revisitation buffer immediately. This improves performance
3461cb0ef41Sopenharmony_ci      // of escape analysis. Using a stack for {revisit_} reverses the order in
3471cb0ef41Sopenharmony_ci      // which the revisitation happens. This also seems to improve performance.
3481cb0ef41Sopenharmony_ci      while (!revisit_.empty()) {
3491cb0ef41Sopenharmony_ci        Node* revisit = revisit_.top();
3501cb0ef41Sopenharmony_ci        if (state_.Get(revisit) == State::kRevisit) {
3511cb0ef41Sopenharmony_ci          state_.Set(revisit, State::kOnStack);
3521cb0ef41Sopenharmony_ci          stack_.push({revisit, 0});
3531cb0ef41Sopenharmony_ci        }
3541cb0ef41Sopenharmony_ci        revisit_.pop();
3551cb0ef41Sopenharmony_ci      }
3561cb0ef41Sopenharmony_ci    }
3571cb0ef41Sopenharmony_ci  }
3581cb0ef41Sopenharmony_ci}
3591cb0ef41Sopenharmony_ci
3601cb0ef41Sopenharmony_civoid EffectGraphReducer::Revisit(Node* node) {
3611cb0ef41Sopenharmony_ci  if (state_.Get(node) == State::kVisited) {
3621cb0ef41Sopenharmony_ci    TRACE("  Queueing for revisit: %s#%d\n", node->op()->mnemonic(),
3631cb0ef41Sopenharmony_ci          node->id());
3641cb0ef41Sopenharmony_ci    state_.Set(node, State::kRevisit);
3651cb0ef41Sopenharmony_ci    revisit_.push(node);
3661cb0ef41Sopenharmony_ci  }
3671cb0ef41Sopenharmony_ci}
3681cb0ef41Sopenharmony_ci
3691cb0ef41Sopenharmony_ciVariableTracker::VariableTracker(JSGraph* graph, EffectGraphReducer* reducer,
3701cb0ef41Sopenharmony_ci                                 Zone* zone)
3711cb0ef41Sopenharmony_ci    : zone_(zone),
3721cb0ef41Sopenharmony_ci      graph_(graph),
3731cb0ef41Sopenharmony_ci      table_(zone, State(zone)),
3741cb0ef41Sopenharmony_ci      buffer_(zone),
3751cb0ef41Sopenharmony_ci      reducer_(reducer),
3761cb0ef41Sopenharmony_ci      tick_counter_(reducer->tick_counter()) {}
3771cb0ef41Sopenharmony_ci
3781cb0ef41Sopenharmony_ciVariableTracker::Scope::Scope(VariableTracker* states, Node* node,
3791cb0ef41Sopenharmony_ci                              Reduction* reduction)
3801cb0ef41Sopenharmony_ci    : ReduceScope(node, reduction),
3811cb0ef41Sopenharmony_ci      states_(states),
3821cb0ef41Sopenharmony_ci      current_state_(states->zone_) {
3831cb0ef41Sopenharmony_ci  switch (node->opcode()) {
3841cb0ef41Sopenharmony_ci    case IrOpcode::kEffectPhi:
3851cb0ef41Sopenharmony_ci      current_state_ = states_->MergeInputs(node);
3861cb0ef41Sopenharmony_ci      break;
3871cb0ef41Sopenharmony_ci    default:
3881cb0ef41Sopenharmony_ci      int effect_inputs = node->op()->EffectInputCount();
3891cb0ef41Sopenharmony_ci      if (effect_inputs == 1) {
3901cb0ef41Sopenharmony_ci        current_state_ =
3911cb0ef41Sopenharmony_ci            states_->table_.Get(NodeProperties::GetEffectInput(node, 0));
3921cb0ef41Sopenharmony_ci      } else {
3931cb0ef41Sopenharmony_ci        DCHECK_EQ(0, effect_inputs);
3941cb0ef41Sopenharmony_ci      }
3951cb0ef41Sopenharmony_ci  }
3961cb0ef41Sopenharmony_ci}
3971cb0ef41Sopenharmony_ci
3981cb0ef41Sopenharmony_ciVariableTracker::Scope::~Scope() {
3991cb0ef41Sopenharmony_ci  if (!reduction()->effect_changed() &&
4001cb0ef41Sopenharmony_ci      states_->table_.Get(current_node()) != current_state_) {
4011cb0ef41Sopenharmony_ci    reduction()->set_effect_changed();
4021cb0ef41Sopenharmony_ci  }
4031cb0ef41Sopenharmony_ci  states_->table_.Set(current_node(), current_state_);
4041cb0ef41Sopenharmony_ci}
4051cb0ef41Sopenharmony_ci
4061cb0ef41Sopenharmony_ciVariableTracker::State VariableTracker::MergeInputs(Node* effect_phi) {
4071cb0ef41Sopenharmony_ci  // A variable that is mapped to [nullptr] was not assigned a value on every
4081cb0ef41Sopenharmony_ci  // execution path to the current effect phi. Relying on the invariant that
4091cb0ef41Sopenharmony_ci  // every variable is initialized (at least with a sentinel like the Dead
4101cb0ef41Sopenharmony_ci  // node), this means that the variable initialization does not dominate the
4111cb0ef41Sopenharmony_ci  // current point. So for loop effect phis, we can keep nullptr for a variable
4121cb0ef41Sopenharmony_ci  // as long as the first input of the loop has nullptr for this variable. For
4131cb0ef41Sopenharmony_ci  // non-loop effect phis, we can even keep it nullptr as long as any input has
4141cb0ef41Sopenharmony_ci  // nullptr.
4151cb0ef41Sopenharmony_ci  DCHECK_EQ(IrOpcode::kEffectPhi, effect_phi->opcode());
4161cb0ef41Sopenharmony_ci  int arity = effect_phi->op()->EffectInputCount();
4171cb0ef41Sopenharmony_ci  Node* control = NodeProperties::GetControlInput(effect_phi, 0);
4181cb0ef41Sopenharmony_ci  TRACE("control: %s#%d\n", control->op()->mnemonic(), control->id());
4191cb0ef41Sopenharmony_ci  bool is_loop = control->opcode() == IrOpcode::kLoop;
4201cb0ef41Sopenharmony_ci  buffer_.reserve(arity + 1);
4211cb0ef41Sopenharmony_ci
4221cb0ef41Sopenharmony_ci  State first_input = table_.Get(NodeProperties::GetEffectInput(effect_phi, 0));
4231cb0ef41Sopenharmony_ci  State result = first_input;
4241cb0ef41Sopenharmony_ci  for (std::pair<Variable, Node*> var_value : first_input) {
4251cb0ef41Sopenharmony_ci    tick_counter_->TickAndMaybeEnterSafepoint();
4261cb0ef41Sopenharmony_ci    if (Node* value = var_value.second) {
4271cb0ef41Sopenharmony_ci      Variable var = var_value.first;
4281cb0ef41Sopenharmony_ci      TRACE("var %i:\n", var.id_);
4291cb0ef41Sopenharmony_ci      buffer_.clear();
4301cb0ef41Sopenharmony_ci      buffer_.push_back(value);
4311cb0ef41Sopenharmony_ci      bool identical_inputs = true;
4321cb0ef41Sopenharmony_ci      int num_defined_inputs = 1;
4331cb0ef41Sopenharmony_ci      TRACE("  input 0: %s#%d\n", value->op()->mnemonic(), value->id());
4341cb0ef41Sopenharmony_ci      for (int i = 1; i < arity; ++i) {
4351cb0ef41Sopenharmony_ci        Node* next_value =
4361cb0ef41Sopenharmony_ci            table_.Get(NodeProperties::GetEffectInput(effect_phi, i)).Get(var);
4371cb0ef41Sopenharmony_ci        if (next_value != value) identical_inputs = false;
4381cb0ef41Sopenharmony_ci        if (next_value != nullptr) {
4391cb0ef41Sopenharmony_ci          num_defined_inputs++;
4401cb0ef41Sopenharmony_ci          TRACE("  input %i: %s#%d\n", i, next_value->op()->mnemonic(),
4411cb0ef41Sopenharmony_ci                next_value->id());
4421cb0ef41Sopenharmony_ci        } else {
4431cb0ef41Sopenharmony_ci          TRACE("  input %i: nullptr\n", i);
4441cb0ef41Sopenharmony_ci        }
4451cb0ef41Sopenharmony_ci        buffer_.push_back(next_value);
4461cb0ef41Sopenharmony_ci      }
4471cb0ef41Sopenharmony_ci
4481cb0ef41Sopenharmony_ci      Node* old_value = table_.Get(effect_phi).Get(var);
4491cb0ef41Sopenharmony_ci      if (old_value) {
4501cb0ef41Sopenharmony_ci        TRACE("  old: %s#%d\n", old_value->op()->mnemonic(), old_value->id());
4511cb0ef41Sopenharmony_ci      } else {
4521cb0ef41Sopenharmony_ci        TRACE("  old: nullptr\n");
4531cb0ef41Sopenharmony_ci      }
4541cb0ef41Sopenharmony_ci      // Reuse a previously created phi node if possible.
4551cb0ef41Sopenharmony_ci      if (old_value && old_value->opcode() == IrOpcode::kPhi &&
4561cb0ef41Sopenharmony_ci          NodeProperties::GetControlInput(old_value, 0) == control) {
4571cb0ef41Sopenharmony_ci        // Since a phi node can never dominate its control node,
4581cb0ef41Sopenharmony_ci        // [old_value] cannot originate from the inputs. Thus [old_value]
4591cb0ef41Sopenharmony_ci        // must have been created by a previous reduction of this [effect_phi].
4601cb0ef41Sopenharmony_ci        for (int i = 0; i < arity; ++i) {
4611cb0ef41Sopenharmony_ci          Node* old_input = NodeProperties::GetValueInput(old_value, i);
4621cb0ef41Sopenharmony_ci          Node* new_input = buffer_[i] ? buffer_[i] : graph_->Dead();
4631cb0ef41Sopenharmony_ci          if (old_input != new_input) {
4641cb0ef41Sopenharmony_ci            NodeProperties::ReplaceValueInput(old_value, new_input, i);
4651cb0ef41Sopenharmony_ci            reducer_->Revisit(old_value);
4661cb0ef41Sopenharmony_ci          }
4671cb0ef41Sopenharmony_ci        }
4681cb0ef41Sopenharmony_ci        result.Set(var, old_value);
4691cb0ef41Sopenharmony_ci      } else {
4701cb0ef41Sopenharmony_ci        if (num_defined_inputs == 1 && is_loop) {
4711cb0ef41Sopenharmony_ci          // For loop effect phis, the variable initialization dominates iff it
4721cb0ef41Sopenharmony_ci          // dominates the first input.
4731cb0ef41Sopenharmony_ci          DCHECK_EQ(2, arity);
4741cb0ef41Sopenharmony_ci          DCHECK_EQ(value, buffer_[0]);
4751cb0ef41Sopenharmony_ci          result.Set(var, value);
4761cb0ef41Sopenharmony_ci        } else if (num_defined_inputs < arity) {
4771cb0ef41Sopenharmony_ci          // If the variable is undefined on some input of this non-loop effect
4781cb0ef41Sopenharmony_ci          // phi, then its initialization does not dominate this point.
4791cb0ef41Sopenharmony_ci          result.Set(var, nullptr);
4801cb0ef41Sopenharmony_ci        } else {
4811cb0ef41Sopenharmony_ci          DCHECK_EQ(num_defined_inputs, arity);
4821cb0ef41Sopenharmony_ci          // We only create a phi if the values are different.
4831cb0ef41Sopenharmony_ci          if (identical_inputs) {
4841cb0ef41Sopenharmony_ci            result.Set(var, value);
4851cb0ef41Sopenharmony_ci          } else {
4861cb0ef41Sopenharmony_ci            TRACE("Creating new phi\n");
4871cb0ef41Sopenharmony_ci            buffer_.push_back(control);
4881cb0ef41Sopenharmony_ci            Node* phi = graph_->graph()->NewNode(
4891cb0ef41Sopenharmony_ci                graph_->common()->Phi(MachineRepresentation::kTagged, arity),
4901cb0ef41Sopenharmony_ci                arity + 1, &buffer_.front());
4911cb0ef41Sopenharmony_ci            // TODO(turbofan): Computing precise types here is tricky, because
4921cb0ef41Sopenharmony_ci            // of the necessary revisitations. If we really need this, we should
4931cb0ef41Sopenharmony_ci            // probably do it afterwards.
4941cb0ef41Sopenharmony_ci            NodeProperties::SetType(phi, Type::Any());
4951cb0ef41Sopenharmony_ci            reducer_->AddRoot(phi);
4961cb0ef41Sopenharmony_ci            result.Set(var, phi);
4971cb0ef41Sopenharmony_ci          }
4981cb0ef41Sopenharmony_ci        }
4991cb0ef41Sopenharmony_ci      }
5001cb0ef41Sopenharmony_ci#ifdef DEBUG
5011cb0ef41Sopenharmony_ci      if (Node* result_node = result.Get(var)) {
5021cb0ef41Sopenharmony_ci        TRACE("  result: %s#%d\n", result_node->op()->mnemonic(),
5031cb0ef41Sopenharmony_ci              result_node->id());
5041cb0ef41Sopenharmony_ci      } else {
5051cb0ef41Sopenharmony_ci        TRACE("  result: nullptr\n");
5061cb0ef41Sopenharmony_ci      }
5071cb0ef41Sopenharmony_ci#endif
5081cb0ef41Sopenharmony_ci    }
5091cb0ef41Sopenharmony_ci  }
5101cb0ef41Sopenharmony_ci  return result;
5111cb0ef41Sopenharmony_ci}
5121cb0ef41Sopenharmony_ci
5131cb0ef41Sopenharmony_cinamespace {
5141cb0ef41Sopenharmony_ci
5151cb0ef41Sopenharmony_ciint OffsetOfFieldAccess(const Operator* op) {
5161cb0ef41Sopenharmony_ci  DCHECK(op->opcode() == IrOpcode::kLoadField ||
5171cb0ef41Sopenharmony_ci         op->opcode() == IrOpcode::kStoreField);
5181cb0ef41Sopenharmony_ci  FieldAccess access = FieldAccessOf(op);
5191cb0ef41Sopenharmony_ci  return access.offset;
5201cb0ef41Sopenharmony_ci}
5211cb0ef41Sopenharmony_ci
5221cb0ef41Sopenharmony_ciMaybe<int> OffsetOfElementAt(ElementAccess const& access, int index) {
5231cb0ef41Sopenharmony_ci  MachineRepresentation representation = access.machine_type.representation();
5241cb0ef41Sopenharmony_ci  // Double elements accesses are not yet supported. See chromium:1237821.
5251cb0ef41Sopenharmony_ci  if (representation == MachineRepresentation::kFloat64) return Nothing<int>();
5261cb0ef41Sopenharmony_ci
5271cb0ef41Sopenharmony_ci  DCHECK_GE(index, 0);
5281cb0ef41Sopenharmony_ci  DCHECK_GE(ElementSizeLog2Of(representation), kTaggedSizeLog2);
5291cb0ef41Sopenharmony_ci  return Just(access.header_size +
5301cb0ef41Sopenharmony_ci              (index << ElementSizeLog2Of(representation)));
5311cb0ef41Sopenharmony_ci}
5321cb0ef41Sopenharmony_ci
5331cb0ef41Sopenharmony_ciMaybe<int> OffsetOfElementsAccess(const Operator* op, Node* index_node) {
5341cb0ef41Sopenharmony_ci  DCHECK(op->opcode() == IrOpcode::kLoadElement ||
5351cb0ef41Sopenharmony_ci         op->opcode() == IrOpcode::kStoreElement);
5361cb0ef41Sopenharmony_ci  Type index_type = NodeProperties::GetType(index_node);
5371cb0ef41Sopenharmony_ci  if (!index_type.Is(Type::OrderedNumber())) return Nothing<int>();
5381cb0ef41Sopenharmony_ci  double max = index_type.Max();
5391cb0ef41Sopenharmony_ci  double min = index_type.Min();
5401cb0ef41Sopenharmony_ci  int index = static_cast<int>(min);
5411cb0ef41Sopenharmony_ci  if (index < 0 || index != min || index != max) return Nothing<int>();
5421cb0ef41Sopenharmony_ci  return OffsetOfElementAt(ElementAccessOf(op), index);
5431cb0ef41Sopenharmony_ci}
5441cb0ef41Sopenharmony_ci
5451cb0ef41Sopenharmony_ciNode* LowerCompareMapsWithoutLoad(Node* checked_map,
5461cb0ef41Sopenharmony_ci                                  ZoneHandleSet<Map> const& checked_against,
5471cb0ef41Sopenharmony_ci                                  JSGraph* jsgraph) {
5481cb0ef41Sopenharmony_ci  Node* true_node = jsgraph->TrueConstant();
5491cb0ef41Sopenharmony_ci  Node* false_node = jsgraph->FalseConstant();
5501cb0ef41Sopenharmony_ci  Node* replacement = false_node;
5511cb0ef41Sopenharmony_ci  for (Handle<Map> map : checked_against) {
5521cb0ef41Sopenharmony_ci    Node* map_node = jsgraph->HeapConstant(map);
5531cb0ef41Sopenharmony_ci    // We cannot create a HeapConstant type here as we are off-thread.
5541cb0ef41Sopenharmony_ci    NodeProperties::SetType(map_node, Type::Internal());
5551cb0ef41Sopenharmony_ci    Node* comparison = jsgraph->graph()->NewNode(
5561cb0ef41Sopenharmony_ci        jsgraph->simplified()->ReferenceEqual(), checked_map, map_node);
5571cb0ef41Sopenharmony_ci    NodeProperties::SetType(comparison, Type::Boolean());
5581cb0ef41Sopenharmony_ci    if (replacement == false_node) {
5591cb0ef41Sopenharmony_ci      replacement = comparison;
5601cb0ef41Sopenharmony_ci    } else {
5611cb0ef41Sopenharmony_ci      replacement = jsgraph->graph()->NewNode(
5621cb0ef41Sopenharmony_ci          jsgraph->common()->Select(MachineRepresentation::kTaggedPointer),
5631cb0ef41Sopenharmony_ci          comparison, true_node, replacement);
5641cb0ef41Sopenharmony_ci      NodeProperties::SetType(replacement, Type::Boolean());
5651cb0ef41Sopenharmony_ci    }
5661cb0ef41Sopenharmony_ci  }
5671cb0ef41Sopenharmony_ci  return replacement;
5681cb0ef41Sopenharmony_ci}
5691cb0ef41Sopenharmony_ci
5701cb0ef41Sopenharmony_civoid ReduceNode(const Operator* op, EscapeAnalysisTracker::Scope* current,
5711cb0ef41Sopenharmony_ci                JSGraph* jsgraph) {
5721cb0ef41Sopenharmony_ci  switch (op->opcode()) {
5731cb0ef41Sopenharmony_ci    case IrOpcode::kAllocate: {
5741cb0ef41Sopenharmony_ci      NumberMatcher size(current->ValueInput(0));
5751cb0ef41Sopenharmony_ci      if (!size.HasResolvedValue()) break;
5761cb0ef41Sopenharmony_ci      int size_int = static_cast<int>(size.ResolvedValue());
5771cb0ef41Sopenharmony_ci      if (size_int != size.ResolvedValue()) break;
5781cb0ef41Sopenharmony_ci      if (const VirtualObject* vobject = current->InitVirtualObject(size_int)) {
5791cb0ef41Sopenharmony_ci        // Initialize with dead nodes as a sentinel for uninitialized memory.
5801cb0ef41Sopenharmony_ci        for (Variable field : *vobject) {
5811cb0ef41Sopenharmony_ci          current->Set(field, jsgraph->Dead());
5821cb0ef41Sopenharmony_ci        }
5831cb0ef41Sopenharmony_ci      }
5841cb0ef41Sopenharmony_ci      break;
5851cb0ef41Sopenharmony_ci    }
5861cb0ef41Sopenharmony_ci    case IrOpcode::kFinishRegion:
5871cb0ef41Sopenharmony_ci      current->SetVirtualObject(current->ValueInput(0));
5881cb0ef41Sopenharmony_ci      break;
5891cb0ef41Sopenharmony_ci    case IrOpcode::kStoreField: {
5901cb0ef41Sopenharmony_ci      Node* object = current->ValueInput(0);
5911cb0ef41Sopenharmony_ci      Node* value = current->ValueInput(1);
5921cb0ef41Sopenharmony_ci      const VirtualObject* vobject = current->GetVirtualObject(object);
5931cb0ef41Sopenharmony_ci      Variable var;
5941cb0ef41Sopenharmony_ci      if (vobject && !vobject->HasEscaped() &&
5951cb0ef41Sopenharmony_ci          vobject->FieldAt(OffsetOfFieldAccess(op)).To(&var)) {
5961cb0ef41Sopenharmony_ci        current->Set(var, value);
5971cb0ef41Sopenharmony_ci        current->MarkForDeletion();
5981cb0ef41Sopenharmony_ci      } else {
5991cb0ef41Sopenharmony_ci        current->SetEscaped(object);
6001cb0ef41Sopenharmony_ci        current->SetEscaped(value);
6011cb0ef41Sopenharmony_ci      }
6021cb0ef41Sopenharmony_ci      break;
6031cb0ef41Sopenharmony_ci    }
6041cb0ef41Sopenharmony_ci    case IrOpcode::kStoreElement: {
6051cb0ef41Sopenharmony_ci      Node* object = current->ValueInput(0);
6061cb0ef41Sopenharmony_ci      Node* index = current->ValueInput(1);
6071cb0ef41Sopenharmony_ci      Node* value = current->ValueInput(2);
6081cb0ef41Sopenharmony_ci      const VirtualObject* vobject = current->GetVirtualObject(object);
6091cb0ef41Sopenharmony_ci      int offset;
6101cb0ef41Sopenharmony_ci      Variable var;
6111cb0ef41Sopenharmony_ci      if (vobject && !vobject->HasEscaped() &&
6121cb0ef41Sopenharmony_ci          OffsetOfElementsAccess(op, index).To(&offset) &&
6131cb0ef41Sopenharmony_ci          vobject->FieldAt(offset).To(&var)) {
6141cb0ef41Sopenharmony_ci        current->Set(var, value);
6151cb0ef41Sopenharmony_ci        current->MarkForDeletion();
6161cb0ef41Sopenharmony_ci      } else {
6171cb0ef41Sopenharmony_ci        current->SetEscaped(value);
6181cb0ef41Sopenharmony_ci        current->SetEscaped(object);
6191cb0ef41Sopenharmony_ci      }
6201cb0ef41Sopenharmony_ci      break;
6211cb0ef41Sopenharmony_ci    }
6221cb0ef41Sopenharmony_ci    case IrOpcode::kLoadField: {
6231cb0ef41Sopenharmony_ci      Node* object = current->ValueInput(0);
6241cb0ef41Sopenharmony_ci      const VirtualObject* vobject = current->GetVirtualObject(object);
6251cb0ef41Sopenharmony_ci      Variable var;
6261cb0ef41Sopenharmony_ci      Node* value;
6271cb0ef41Sopenharmony_ci      if (vobject && !vobject->HasEscaped() &&
6281cb0ef41Sopenharmony_ci          vobject->FieldAt(OffsetOfFieldAccess(op)).To(&var) &&
6291cb0ef41Sopenharmony_ci          current->Get(var).To(&value)) {
6301cb0ef41Sopenharmony_ci        current->SetReplacement(value);
6311cb0ef41Sopenharmony_ci      } else {
6321cb0ef41Sopenharmony_ci        current->SetEscaped(object);
6331cb0ef41Sopenharmony_ci      }
6341cb0ef41Sopenharmony_ci      break;
6351cb0ef41Sopenharmony_ci    }
6361cb0ef41Sopenharmony_ci    case IrOpcode::kLoadElement: {
6371cb0ef41Sopenharmony_ci      Node* object = current->ValueInput(0);
6381cb0ef41Sopenharmony_ci      Node* index = current->ValueInput(1);
6391cb0ef41Sopenharmony_ci      const VirtualObject* vobject = current->GetVirtualObject(object);
6401cb0ef41Sopenharmony_ci      int offset;
6411cb0ef41Sopenharmony_ci      Variable var;
6421cb0ef41Sopenharmony_ci      Node* value;
6431cb0ef41Sopenharmony_ci      if (vobject && !vobject->HasEscaped() &&
6441cb0ef41Sopenharmony_ci          OffsetOfElementsAccess(op, index).To(&offset) &&
6451cb0ef41Sopenharmony_ci          vobject->FieldAt(offset).To(&var) && current->Get(var).To(&value)) {
6461cb0ef41Sopenharmony_ci        current->SetReplacement(value);
6471cb0ef41Sopenharmony_ci        break;
6481cb0ef41Sopenharmony_ci      } else if (vobject && !vobject->HasEscaped()) {
6491cb0ef41Sopenharmony_ci        // Compute the known length (aka the number of elements) of {object}
6501cb0ef41Sopenharmony_ci        // based on the virtual object information.
6511cb0ef41Sopenharmony_ci        ElementAccess const& access = ElementAccessOf(op);
6521cb0ef41Sopenharmony_ci        int const length =
6531cb0ef41Sopenharmony_ci            (vobject->size() - access.header_size) >>
6541cb0ef41Sopenharmony_ci            ElementSizeLog2Of(access.machine_type.representation());
6551cb0ef41Sopenharmony_ci        Variable var0, var1;
6561cb0ef41Sopenharmony_ci        Node* value0;
6571cb0ef41Sopenharmony_ci        Node* value1;
6581cb0ef41Sopenharmony_ci        if (length == 1 &&
6591cb0ef41Sopenharmony_ci            vobject->FieldAt(OffsetOfElementAt(access, 0)).To(&var) &&
6601cb0ef41Sopenharmony_ci            current->Get(var).To(&value) &&
6611cb0ef41Sopenharmony_ci            (value == nullptr ||
6621cb0ef41Sopenharmony_ci             NodeProperties::GetType(value).Is(access.type))) {
6631cb0ef41Sopenharmony_ci          // The {object} has no elements, and we know that the LoadElement
6641cb0ef41Sopenharmony_ci          // {index} must be within bounds, thus it must always yield this
6651cb0ef41Sopenharmony_ci          // one element of {object}.
6661cb0ef41Sopenharmony_ci          current->SetReplacement(value);
6671cb0ef41Sopenharmony_ci          break;
6681cb0ef41Sopenharmony_ci        } else if (length == 2 &&
6691cb0ef41Sopenharmony_ci                   vobject->FieldAt(OffsetOfElementAt(access, 0)).To(&var0) &&
6701cb0ef41Sopenharmony_ci                   current->Get(var0).To(&value0) &&
6711cb0ef41Sopenharmony_ci                   (value0 == nullptr ||
6721cb0ef41Sopenharmony_ci                    NodeProperties::GetType(value0).Is(access.type)) &&
6731cb0ef41Sopenharmony_ci                   vobject->FieldAt(OffsetOfElementAt(access, 1)).To(&var1) &&
6741cb0ef41Sopenharmony_ci                   current->Get(var1).To(&value1) &&
6751cb0ef41Sopenharmony_ci                   (value1 == nullptr ||
6761cb0ef41Sopenharmony_ci                    NodeProperties::GetType(value1).Is(access.type))) {
6771cb0ef41Sopenharmony_ci          if (value0 && value1) {
6781cb0ef41Sopenharmony_ci            // The {object} has exactly two elements, so the LoadElement
6791cb0ef41Sopenharmony_ci            // must return one of them (i.e. either the element at index
6801cb0ef41Sopenharmony_ci            // 0 or the one at index 1). So we can turn the LoadElement
6811cb0ef41Sopenharmony_ci            // into a Select operation instead (still allowing the {object}
6821cb0ef41Sopenharmony_ci            // to be scalar replaced). We must however mark the elements
6831cb0ef41Sopenharmony_ci            // of the {object} itself as escaping.
6841cb0ef41Sopenharmony_ci            Node* check =
6851cb0ef41Sopenharmony_ci                jsgraph->graph()->NewNode(jsgraph->simplified()->NumberEqual(),
6861cb0ef41Sopenharmony_ci                                          index, jsgraph->ZeroConstant());
6871cb0ef41Sopenharmony_ci            NodeProperties::SetType(check, Type::Boolean());
6881cb0ef41Sopenharmony_ci            Node* select = jsgraph->graph()->NewNode(
6891cb0ef41Sopenharmony_ci                jsgraph->common()->Select(access.machine_type.representation()),
6901cb0ef41Sopenharmony_ci                check, value0, value1);
6911cb0ef41Sopenharmony_ci            NodeProperties::SetType(select, access.type);
6921cb0ef41Sopenharmony_ci            current->SetReplacement(select);
6931cb0ef41Sopenharmony_ci            current->SetEscaped(value0);
6941cb0ef41Sopenharmony_ci            current->SetEscaped(value1);
6951cb0ef41Sopenharmony_ci            break;
6961cb0ef41Sopenharmony_ci          } else {
6971cb0ef41Sopenharmony_ci            // If the variables have no values, we have
6981cb0ef41Sopenharmony_ci            // not reached the fixed-point yet.
6991cb0ef41Sopenharmony_ci            break;
7001cb0ef41Sopenharmony_ci          }
7011cb0ef41Sopenharmony_ci        }
7021cb0ef41Sopenharmony_ci      }
7031cb0ef41Sopenharmony_ci      current->SetEscaped(object);
7041cb0ef41Sopenharmony_ci      break;
7051cb0ef41Sopenharmony_ci    }
7061cb0ef41Sopenharmony_ci    case IrOpcode::kTypeGuard: {
7071cb0ef41Sopenharmony_ci      current->SetVirtualObject(current->ValueInput(0));
7081cb0ef41Sopenharmony_ci      break;
7091cb0ef41Sopenharmony_ci    }
7101cb0ef41Sopenharmony_ci    case IrOpcode::kReferenceEqual: {
7111cb0ef41Sopenharmony_ci      Node* left = current->ValueInput(0);
7121cb0ef41Sopenharmony_ci      Node* right = current->ValueInput(1);
7131cb0ef41Sopenharmony_ci      const VirtualObject* left_object = current->GetVirtualObject(left);
7141cb0ef41Sopenharmony_ci      const VirtualObject* right_object = current->GetVirtualObject(right);
7151cb0ef41Sopenharmony_ci      Node* replacement = nullptr;
7161cb0ef41Sopenharmony_ci      if (left_object && !left_object->HasEscaped()) {
7171cb0ef41Sopenharmony_ci        if (right_object && !right_object->HasEscaped() &&
7181cb0ef41Sopenharmony_ci            left_object->id() == right_object->id()) {
7191cb0ef41Sopenharmony_ci          replacement = jsgraph->TrueConstant();
7201cb0ef41Sopenharmony_ci        } else {
7211cb0ef41Sopenharmony_ci          replacement = jsgraph->FalseConstant();
7221cb0ef41Sopenharmony_ci        }
7231cb0ef41Sopenharmony_ci      } else if (right_object && !right_object->HasEscaped()) {
7241cb0ef41Sopenharmony_ci        replacement = jsgraph->FalseConstant();
7251cb0ef41Sopenharmony_ci      }
7261cb0ef41Sopenharmony_ci      // TODO(turbofan) This is a workaround for uninhabited types. If we
7271cb0ef41Sopenharmony_ci      // replaced a value of uninhabited type with a constant, we would
7281cb0ef41Sopenharmony_ci      // widen the type of the node. This could produce inconsistent
7291cb0ef41Sopenharmony_ci      // types (which might confuse representation selection). We get
7301cb0ef41Sopenharmony_ci      // around this by refusing to constant-fold and escape-analyze
7311cb0ef41Sopenharmony_ci      // if the type is not inhabited.
7321cb0ef41Sopenharmony_ci      if (replacement && !NodeProperties::GetType(left).IsNone() &&
7331cb0ef41Sopenharmony_ci          !NodeProperties::GetType(right).IsNone()) {
7341cb0ef41Sopenharmony_ci        current->SetReplacement(replacement);
7351cb0ef41Sopenharmony_ci        break;
7361cb0ef41Sopenharmony_ci      }
7371cb0ef41Sopenharmony_ci      current->SetEscaped(left);
7381cb0ef41Sopenharmony_ci      current->SetEscaped(right);
7391cb0ef41Sopenharmony_ci      break;
7401cb0ef41Sopenharmony_ci    }
7411cb0ef41Sopenharmony_ci    case IrOpcode::kCheckMaps: {
7421cb0ef41Sopenharmony_ci      CheckMapsParameters params = CheckMapsParametersOf(op);
7431cb0ef41Sopenharmony_ci      Node* checked = current->ValueInput(0);
7441cb0ef41Sopenharmony_ci      const VirtualObject* vobject = current->GetVirtualObject(checked);
7451cb0ef41Sopenharmony_ci      Variable map_field;
7461cb0ef41Sopenharmony_ci      Node* map;
7471cb0ef41Sopenharmony_ci      if (vobject && !vobject->HasEscaped() &&
7481cb0ef41Sopenharmony_ci          vobject->FieldAt(HeapObject::kMapOffset).To(&map_field) &&
7491cb0ef41Sopenharmony_ci          current->Get(map_field).To(&map)) {
7501cb0ef41Sopenharmony_ci        if (map) {
7511cb0ef41Sopenharmony_ci          Type const map_type = NodeProperties::GetType(map);
7521cb0ef41Sopenharmony_ci          if (map_type.IsHeapConstant() &&
7531cb0ef41Sopenharmony_ci              params.maps().contains(
7541cb0ef41Sopenharmony_ci                  map_type.AsHeapConstant()->Ref().AsMap().object())) {
7551cb0ef41Sopenharmony_ci            current->MarkForDeletion();
7561cb0ef41Sopenharmony_ci            break;
7571cb0ef41Sopenharmony_ci          }
7581cb0ef41Sopenharmony_ci        } else {
7591cb0ef41Sopenharmony_ci          // If the variable has no value, we have not reached the fixed-point
7601cb0ef41Sopenharmony_ci          // yet.
7611cb0ef41Sopenharmony_ci          break;
7621cb0ef41Sopenharmony_ci        }
7631cb0ef41Sopenharmony_ci      }
7641cb0ef41Sopenharmony_ci      current->SetEscaped(checked);
7651cb0ef41Sopenharmony_ci      break;
7661cb0ef41Sopenharmony_ci    }
7671cb0ef41Sopenharmony_ci    case IrOpcode::kCompareMaps: {
7681cb0ef41Sopenharmony_ci      Node* object = current->ValueInput(0);
7691cb0ef41Sopenharmony_ci      const VirtualObject* vobject = current->GetVirtualObject(object);
7701cb0ef41Sopenharmony_ci      Variable map_field;
7711cb0ef41Sopenharmony_ci      Node* object_map;
7721cb0ef41Sopenharmony_ci      if (vobject && !vobject->HasEscaped() &&
7731cb0ef41Sopenharmony_ci          vobject->FieldAt(HeapObject::kMapOffset).To(&map_field) &&
7741cb0ef41Sopenharmony_ci          current->Get(map_field).To(&object_map)) {
7751cb0ef41Sopenharmony_ci        if (object_map) {
7761cb0ef41Sopenharmony_ci          current->SetReplacement(LowerCompareMapsWithoutLoad(
7771cb0ef41Sopenharmony_ci              object_map, CompareMapsParametersOf(op), jsgraph));
7781cb0ef41Sopenharmony_ci          break;
7791cb0ef41Sopenharmony_ci        } else {
7801cb0ef41Sopenharmony_ci          // If the variable has no value, we have not reached the fixed-point
7811cb0ef41Sopenharmony_ci          // yet.
7821cb0ef41Sopenharmony_ci          break;
7831cb0ef41Sopenharmony_ci        }
7841cb0ef41Sopenharmony_ci      }
7851cb0ef41Sopenharmony_ci      current->SetEscaped(object);
7861cb0ef41Sopenharmony_ci      break;
7871cb0ef41Sopenharmony_ci    }
7881cb0ef41Sopenharmony_ci    case IrOpcode::kCheckHeapObject: {
7891cb0ef41Sopenharmony_ci      Node* checked = current->ValueInput(0);
7901cb0ef41Sopenharmony_ci      switch (checked->opcode()) {
7911cb0ef41Sopenharmony_ci        case IrOpcode::kAllocate:
7921cb0ef41Sopenharmony_ci        case IrOpcode::kFinishRegion:
7931cb0ef41Sopenharmony_ci        case IrOpcode::kHeapConstant:
7941cb0ef41Sopenharmony_ci          current->SetReplacement(checked);
7951cb0ef41Sopenharmony_ci          break;
7961cb0ef41Sopenharmony_ci        default:
7971cb0ef41Sopenharmony_ci          current->SetEscaped(checked);
7981cb0ef41Sopenharmony_ci          break;
7991cb0ef41Sopenharmony_ci      }
8001cb0ef41Sopenharmony_ci      break;
8011cb0ef41Sopenharmony_ci    }
8021cb0ef41Sopenharmony_ci    case IrOpcode::kMapGuard: {
8031cb0ef41Sopenharmony_ci      Node* object = current->ValueInput(0);
8041cb0ef41Sopenharmony_ci      const VirtualObject* vobject = current->GetVirtualObject(object);
8051cb0ef41Sopenharmony_ci      if (vobject && !vobject->HasEscaped()) {
8061cb0ef41Sopenharmony_ci        current->MarkForDeletion();
8071cb0ef41Sopenharmony_ci      }
8081cb0ef41Sopenharmony_ci      break;
8091cb0ef41Sopenharmony_ci    }
8101cb0ef41Sopenharmony_ci    case IrOpcode::kStateValues:
8111cb0ef41Sopenharmony_ci      // We visit StateValue nodes through their correpsonding FrameState node,
8121cb0ef41Sopenharmony_ci      // so we need to make sure we revisit the FrameState.
8131cb0ef41Sopenharmony_ci      current->SetValueChanged();
8141cb0ef41Sopenharmony_ci      break;
8151cb0ef41Sopenharmony_ci    case IrOpcode::kFrameState: {
8161cb0ef41Sopenharmony_ci      // We mark the receiver as escaping due to the non-standard `.getThis`
8171cb0ef41Sopenharmony_ci      // API.
8181cb0ef41Sopenharmony_ci      FrameState frame_state{current->CurrentNode()};
8191cb0ef41Sopenharmony_ci      if (frame_state.frame_state_info().type() !=
8201cb0ef41Sopenharmony_ci          FrameStateType::kUnoptimizedFunction)
8211cb0ef41Sopenharmony_ci        break;
8221cb0ef41Sopenharmony_ci      StateValuesAccess::iterator it =
8231cb0ef41Sopenharmony_ci          StateValuesAccess(frame_state.parameters()).begin();
8241cb0ef41Sopenharmony_ci      if (!it.done()) {
8251cb0ef41Sopenharmony_ci        if (Node* receiver = it.node()) {
8261cb0ef41Sopenharmony_ci          current->SetEscaped(receiver);
8271cb0ef41Sopenharmony_ci        }
8281cb0ef41Sopenharmony_ci        current->SetEscaped(frame_state.function());
8291cb0ef41Sopenharmony_ci      }
8301cb0ef41Sopenharmony_ci      break;
8311cb0ef41Sopenharmony_ci    }
8321cb0ef41Sopenharmony_ci    default: {
8331cb0ef41Sopenharmony_ci      // For unknown nodes, treat all value inputs as escaping.
8341cb0ef41Sopenharmony_ci      int value_input_count = op->ValueInputCount();
8351cb0ef41Sopenharmony_ci      for (int i = 0; i < value_input_count; ++i) {
8361cb0ef41Sopenharmony_ci        Node* input = current->ValueInput(i);
8371cb0ef41Sopenharmony_ci        current->SetEscaped(input);
8381cb0ef41Sopenharmony_ci      }
8391cb0ef41Sopenharmony_ci      if (OperatorProperties::HasContextInput(op)) {
8401cb0ef41Sopenharmony_ci        current->SetEscaped(current->ContextInput());
8411cb0ef41Sopenharmony_ci      }
8421cb0ef41Sopenharmony_ci      break;
8431cb0ef41Sopenharmony_ci    }
8441cb0ef41Sopenharmony_ci  }
8451cb0ef41Sopenharmony_ci}
8461cb0ef41Sopenharmony_ci
8471cb0ef41Sopenharmony_ci}  // namespace
8481cb0ef41Sopenharmony_ci
8491cb0ef41Sopenharmony_civoid EscapeAnalysis::Reduce(Node* node, Reduction* reduction) {
8501cb0ef41Sopenharmony_ci  const Operator* op = node->op();
8511cb0ef41Sopenharmony_ci  TRACE("Reducing %s#%d\n", op->mnemonic(), node->id());
8521cb0ef41Sopenharmony_ci
8531cb0ef41Sopenharmony_ci  EscapeAnalysisTracker::Scope current(this, tracker_, node, reduction);
8541cb0ef41Sopenharmony_ci  ReduceNode(op, &current, jsgraph());
8551cb0ef41Sopenharmony_ci}
8561cb0ef41Sopenharmony_ci
8571cb0ef41Sopenharmony_ciEscapeAnalysis::EscapeAnalysis(JSGraph* jsgraph, TickCounter* tick_counter,
8581cb0ef41Sopenharmony_ci                               Zone* zone)
8591cb0ef41Sopenharmony_ci    : EffectGraphReducer(
8601cb0ef41Sopenharmony_ci          jsgraph->graph(),
8611cb0ef41Sopenharmony_ci          [this](Node* node, Reduction* reduction) { Reduce(node, reduction); },
8621cb0ef41Sopenharmony_ci          tick_counter, zone),
8631cb0ef41Sopenharmony_ci      tracker_(zone->New<EscapeAnalysisTracker>(jsgraph, this, zone)),
8641cb0ef41Sopenharmony_ci      jsgraph_(jsgraph) {}
8651cb0ef41Sopenharmony_ci
8661cb0ef41Sopenharmony_ciNode* EscapeAnalysisResult::GetReplacementOf(Node* node) {
8671cb0ef41Sopenharmony_ci  Node* replacement = tracker_->GetReplacementOf(node);
8681cb0ef41Sopenharmony_ci  // Replacements cannot have replacements. This is important to ensure
8691cb0ef41Sopenharmony_ci  // re-visitation: If a replacement is replaced, then all nodes accessing
8701cb0ef41Sopenharmony_ci  // the replacement have to be updated.
8711cb0ef41Sopenharmony_ci  if (replacement) DCHECK_NULL(tracker_->GetReplacementOf(replacement));
8721cb0ef41Sopenharmony_ci  return replacement;
8731cb0ef41Sopenharmony_ci}
8741cb0ef41Sopenharmony_ci
8751cb0ef41Sopenharmony_ciNode* EscapeAnalysisResult::GetVirtualObjectField(const VirtualObject* vobject,
8761cb0ef41Sopenharmony_ci                                                  int field, Node* effect) {
8771cb0ef41Sopenharmony_ci  return tracker_->variable_states_.Get(vobject->FieldAt(field).FromJust(),
8781cb0ef41Sopenharmony_ci                                        effect);
8791cb0ef41Sopenharmony_ci}
8801cb0ef41Sopenharmony_ci
8811cb0ef41Sopenharmony_ciconst VirtualObject* EscapeAnalysisResult::GetVirtualObject(Node* node) {
8821cb0ef41Sopenharmony_ci  return tracker_->virtual_objects_.Get(node);
8831cb0ef41Sopenharmony_ci}
8841cb0ef41Sopenharmony_ci
8851cb0ef41Sopenharmony_ciVirtualObject::VirtualObject(VariableTracker* var_states, VirtualObject::Id id,
8861cb0ef41Sopenharmony_ci                             int size)
8871cb0ef41Sopenharmony_ci    : Dependable(var_states->zone()), id_(id), fields_(var_states->zone()) {
8881cb0ef41Sopenharmony_ci  DCHECK(IsAligned(size, kTaggedSize));
8891cb0ef41Sopenharmony_ci  TRACE("Creating VirtualObject id:%d size:%d\n", id, size);
8901cb0ef41Sopenharmony_ci  int num_fields = size / kTaggedSize;
8911cb0ef41Sopenharmony_ci  fields_.reserve(num_fields);
8921cb0ef41Sopenharmony_ci  for (int i = 0; i < num_fields; ++i) {
8931cb0ef41Sopenharmony_ci    fields_.push_back(var_states->NewVariable());
8941cb0ef41Sopenharmony_ci  }
8951cb0ef41Sopenharmony_ci}
8961cb0ef41Sopenharmony_ci
8971cb0ef41Sopenharmony_ci#undef TRACE
8981cb0ef41Sopenharmony_ci
8991cb0ef41Sopenharmony_ci}  // namespace compiler
9001cb0ef41Sopenharmony_ci}  // namespace internal
9011cb0ef41Sopenharmony_ci}  // namespace v8
902