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, ¤t, 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