11cb0ef41Sopenharmony_ci// Copyright 2019 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#ifndef V8_COMPILER_DECOMPRESSION_OPTIMIZER_H_ 61cb0ef41Sopenharmony_ci#define V8_COMPILER_DECOMPRESSION_OPTIMIZER_H_ 71cb0ef41Sopenharmony_ci 81cb0ef41Sopenharmony_ci#include "src/compiler/common-operator.h" 91cb0ef41Sopenharmony_ci#include "src/compiler/machine-operator.h" 101cb0ef41Sopenharmony_ci#include "src/compiler/node-marker.h" 111cb0ef41Sopenharmony_ci 121cb0ef41Sopenharmony_cinamespace v8 { 131cb0ef41Sopenharmony_cinamespace internal { 141cb0ef41Sopenharmony_cinamespace compiler { 151cb0ef41Sopenharmony_ci 161cb0ef41Sopenharmony_ci// Forward declare. 171cb0ef41Sopenharmony_ciclass Graph; 181cb0ef41Sopenharmony_ci 191cb0ef41Sopenharmony_ci// DecompressionOptimizer purpose is to hide the distinction between 32 bit and 201cb0ef41Sopenharmony_ci// 64 bit tagged values, while being able to use the compressed version of nodes 211cb0ef41Sopenharmony_ci// whenever possible. Its scope is narrowed down to loads of TaggedPointer and 221cb0ef41Sopenharmony_ci// AnyTagged (since TaggedSigned avoids full decompression always), and 231cb0ef41Sopenharmony_ci// HeapConstants. 241cb0ef41Sopenharmony_ci 251cb0ef41Sopenharmony_ci// DecompressionOptimizer will run only when pointer compression is enabled. 261cb0ef41Sopenharmony_ci 271cb0ef41Sopenharmony_ci// The phase needs to be run when Machine are present in the graph, i.e 281cb0ef41Sopenharmony_ci// at the very end of the pipeline. Also, since this phase may change 291cb0ef41Sopenharmony_ci// the load's MachineRepresentation from Tagged to Compressed, it's best 301cb0ef41Sopenharmony_ci// to run it as late as possible in order to keep the phases that know 311cb0ef41Sopenharmony_ci// about Compressed MachineRepresentation to a minimum. 321cb0ef41Sopenharmony_ci 331cb0ef41Sopenharmony_ci// As an example, if we Load a Tagged value only to Store it back again (i.e 341cb0ef41Sopenharmony_ci// Load -> Store nodes, with the Load's value being the Store's value) we don't 351cb0ef41Sopenharmony_ci// need to fully decompress it since the Store will ignore the top bits. 361cb0ef41Sopenharmony_ciclass V8_EXPORT_PRIVATE DecompressionOptimizer final { 371cb0ef41Sopenharmony_ci public: 381cb0ef41Sopenharmony_ci DecompressionOptimizer(Zone* zone, Graph* graph, 391cb0ef41Sopenharmony_ci CommonOperatorBuilder* common, 401cb0ef41Sopenharmony_ci MachineOperatorBuilder* machine); 411cb0ef41Sopenharmony_ci ~DecompressionOptimizer() = default; 421cb0ef41Sopenharmony_ci DecompressionOptimizer(const DecompressionOptimizer&) = delete; 431cb0ef41Sopenharmony_ci DecompressionOptimizer& operator=(const DecompressionOptimizer&) = delete; 441cb0ef41Sopenharmony_ci 451cb0ef41Sopenharmony_ci // Assign States to the nodes, and then change the node's Operator to use the 461cb0ef41Sopenharmony_ci // compressed version if possible. 471cb0ef41Sopenharmony_ci void Reduce(); 481cb0ef41Sopenharmony_ci 491cb0ef41Sopenharmony_ci private: 501cb0ef41Sopenharmony_ci // State refers to the node's state as follows: 511cb0ef41Sopenharmony_ci // * kUnvisited === This node has yet to be visited. 521cb0ef41Sopenharmony_ci // * kOnly32BitsObserved === This node either has been visited, or is on 531cb0ef41Sopenharmony_ci // to_visit_. We couldn't find a node that observes the upper bits. 541cb0ef41Sopenharmony_ci // * kEverythingObserved === This node either has been visited, or is on 551cb0ef41Sopenharmony_ci // to_visit_. We found at least one node that observes the upper bits. 561cb0ef41Sopenharmony_ci enum class State : uint8_t { 571cb0ef41Sopenharmony_ci kUnvisited = 0, 581cb0ef41Sopenharmony_ci kOnly32BitsObserved, 591cb0ef41Sopenharmony_ci kEverythingObserved, 601cb0ef41Sopenharmony_ci kNumberOfStates 611cb0ef41Sopenharmony_ci }; 621cb0ef41Sopenharmony_ci 631cb0ef41Sopenharmony_ci // Change node's op from HeapConstant to CompressedHeapConstant. 641cb0ef41Sopenharmony_ci void ChangeHeapConstant(Node* const node); 651cb0ef41Sopenharmony_ci 661cb0ef41Sopenharmony_ci // Change the phi's representation from Tagged to Compressed. 671cb0ef41Sopenharmony_ci void ChangePhi(Node* const node); 681cb0ef41Sopenharmony_ci 691cb0ef41Sopenharmony_ci // Change node's load into a compressed one. 701cb0ef41Sopenharmony_ci void ChangeLoad(Node* const node); 711cb0ef41Sopenharmony_ci 721cb0ef41Sopenharmony_ci // Go through the already marked nodes and changed the operation for the nodes 731cb0ef41Sopenharmony_ci // that can use compressed outputs. 741cb0ef41Sopenharmony_ci void ChangeNodes(); 751cb0ef41Sopenharmony_ci 761cb0ef41Sopenharmony_ci // Goes through the nodes to mark them all as appropriate. It will visit each 771cb0ef41Sopenharmony_ci // node at most twice: only when the node was unvisited, then marked as 781cb0ef41Sopenharmony_ci // kOnly32BitsObserved and visited, and finally marked as kEverythingObserved 791cb0ef41Sopenharmony_ci // and visited. 801cb0ef41Sopenharmony_ci void MarkNodes(); 811cb0ef41Sopenharmony_ci 821cb0ef41Sopenharmony_ci // Mark node's input as appropriate, according to node's opcode. Some input 831cb0ef41Sopenharmony_ci // State may be updated, and therefore has to be revisited. 841cb0ef41Sopenharmony_ci void MarkNodeInputs(Node* node); 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_ci // Mark node's State to be state. We only do this if we have new information, 871cb0ef41Sopenharmony_ci // i.e either if: 881cb0ef41Sopenharmony_ci // * We are marking an unvisited node, or 891cb0ef41Sopenharmony_ci // * We are marking a node as needing 64 bits when we previously had the 901cb0ef41Sopenharmony_ci // information that it could output 32 bits. Also, we store the HeapConstant 911cb0ef41Sopenharmony_ci // and TaggedPointer and AnyTagged loads that have their state set as 921cb0ef41Sopenharmony_ci // kOnly32BitsObserved. If the node's state changes, we queue it for revisit. 931cb0ef41Sopenharmony_ci void MaybeMarkAndQueueForRevisit(Node* const node, State state); 941cb0ef41Sopenharmony_ci 951cb0ef41Sopenharmony_ci bool IsEverythingObserved(Node* const node) { 961cb0ef41Sopenharmony_ci return states_.Get(node) == State::kEverythingObserved; 971cb0ef41Sopenharmony_ci } 981cb0ef41Sopenharmony_ci 991cb0ef41Sopenharmony_ci bool IsOnly32BitsObserved(Node* const node) { 1001cb0ef41Sopenharmony_ci return states_.Get(node) == State::kOnly32BitsObserved; 1011cb0ef41Sopenharmony_ci } 1021cb0ef41Sopenharmony_ci 1031cb0ef41Sopenharmony_ci Graph* graph() const { return graph_; } 1041cb0ef41Sopenharmony_ci CommonOperatorBuilder* common() const { return common_; } 1051cb0ef41Sopenharmony_ci MachineOperatorBuilder* machine() const { return machine_; } 1061cb0ef41Sopenharmony_ci 1071cb0ef41Sopenharmony_ci Graph* const graph_; 1081cb0ef41Sopenharmony_ci CommonOperatorBuilder* const common_; 1091cb0ef41Sopenharmony_ci MachineOperatorBuilder* const machine_; 1101cb0ef41Sopenharmony_ci NodeMarker<State> states_; 1111cb0ef41Sopenharmony_ci // to_visit_ is a Deque but it's used as if it were a Queue. The reason why we 1121cb0ef41Sopenharmony_ci // are using NodeDeque is because it attempts to reuse 'freed' zone memory 1131cb0ef41Sopenharmony_ci // instead of always allocating a new region. 1141cb0ef41Sopenharmony_ci NodeDeque to_visit_; 1151cb0ef41Sopenharmony_ci // Contains the nodes that can be changed into a compressed version of 1161cb0ef41Sopenharmony_ci // themselves. In a way, it functions as a NodeSet since each node will be 1171cb0ef41Sopenharmony_ci // contained at most once. It's a Vector since we care about insertion speed. 1181cb0ef41Sopenharmony_ci NodeVector compressed_candidate_nodes_; 1191cb0ef41Sopenharmony_ci}; 1201cb0ef41Sopenharmony_ci 1211cb0ef41Sopenharmony_ci} // namespace compiler 1221cb0ef41Sopenharmony_ci} // namespace internal 1231cb0ef41Sopenharmony_ci} // namespace v8 1241cb0ef41Sopenharmony_ci 1251cb0ef41Sopenharmony_ci#endif // V8_COMPILER_DECOMPRESSION_OPTIMIZER_H_ 126