decompression-optimizer.h 4.98 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_COMPILER_DECOMPRESSION_OPTIMIZER_H_
#define V8_COMPILER_DECOMPRESSION_OPTIMIZER_H_

8
#include "src/compiler/common-operator.h"
9 10 11 12 13 14 15 16 17 18
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-marker.h"

namespace v8 {
namespace internal {
namespace compiler {

// Forward declare.
class Graph;

19 20 21 22 23
// DecompressionOptimizer purpose is to hide the distinction between 32 bit and
// 64 bit tagged values, while being able to use the compressed version of nodes
// whenever possible. Its scope is narrowed down to loads of TaggedPointer and
// AnyTagged (since TaggedSigned avoids full decompression always), and
// HeapConstants.
24

25
// DecompressionOptimizer will run only when pointer compression is enabled.
26 27 28 29 30 31 32 33 34 35 36 37 38

// The phase needs to be run when Machine are present in the graph, i.e
// at the very end of the pipeline. Also, since this phase may change
// the load's MachineRepresentation from Tagged to Compressed, it's best
// to run it as late as possible in order to keep the phases that know
// about Compressed MachineRepresentation to a minimum.

// As an example, if we Load a Tagged value only to Store it back again (i.e
// Load -> Store nodes, with the Load's value being the Store's value) we don't
// need to fully decompress it since the Store will ignore the top bits.
class V8_EXPORT_PRIVATE DecompressionOptimizer final {
 public:
  DecompressionOptimizer(Zone* zone, Graph* graph,
39
                         CommonOperatorBuilder* common,
40 41
                         MachineOperatorBuilder* machine);
  ~DecompressionOptimizer() = default;
42 43
  DecompressionOptimizer(const DecompressionOptimizer&) = delete;
  DecompressionOptimizer& operator=(const DecompressionOptimizer&) = delete;
44

45 46
  // Assign States to the nodes, and then change the node's Operator to use the
  // compressed version if possible.
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
  void Reduce();

 private:
  // State refers to the node's state as follows:
  // * kUnvisited === This node has yet to be visited.
  // * kOnly32BitsObserved === This node either has been visited, or is on
  // to_visit_. We couldn't find a node that observes the upper bits.
  // * kEverythingObserved === This node either has been visited, or is on
  // to_visit_. We found at least one node that observes the upper bits.
  enum class State : uint8_t {
    kUnvisited = 0,
    kOnly32BitsObserved,
    kEverythingObserved,
    kNumberOfStates
  };

63 64 65
  // Change node's op from HeapConstant to CompressedHeapConstant.
  void ChangeHeapConstant(Node* const node);

66 67 68
  // Change the phi's representation from Tagged to Compressed.
  void ChangePhi(Node* const node);

69 70 71 72 73 74
  // Change node's load into a compressed one.
  void ChangeLoad(Node* const node);

  // Go through the already marked nodes and changed the operation for the nodes
  // that can use compressed outputs.
  void ChangeNodes();
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89

  // Goes through the nodes to mark them all as appropriate. It will visit each
  // node at most twice: only when the node was unvisited, then marked as
  // kOnly32BitsObserved and visited, and finally marked as kEverythingObserved
  // and visited.
  void MarkNodes();

  // Mark node's input as appropriate, according to node's opcode. Some input
  // State may be updated, and therefore has to be revisited.
  void MarkNodeInputs(Node* node);

  // Mark node's State to be state. We only do this if we have new information,
  // i.e either if:
  // * We are marking an unvisited node, or
  // * We are marking a node as needing 64 bits when we previously had the
90 91 92
  // information that it could output 32 bits. Also, we store the HeapConstant
  // and TaggedPointer and AnyTagged loads that have their state set as
  // kOnly32BitsObserved. If the node's state changes, we queue it for revisit.
93 94 95 96 97 98
  void MaybeMarkAndQueueForRevisit(Node* const node, State state);

  bool IsEverythingObserved(Node* const node) {
    return states_.Get(node) == State::kEverythingObserved;
  }

99 100 101 102
  bool IsOnly32BitsObserved(Node* const node) {
    return states_.Get(node) == State::kOnly32BitsObserved;
  }

103
  Graph* graph() const { return graph_; }
104
  CommonOperatorBuilder* common() const { return common_; }
105 106 107
  MachineOperatorBuilder* machine() const { return machine_; }

  Graph* const graph_;
108
  CommonOperatorBuilder* const common_;
109 110 111 112 113 114
  MachineOperatorBuilder* const machine_;
  NodeMarker<State> states_;
  // to_visit_ is a Deque but it's used as if it were a Queue. The reason why we
  // are using NodeDeque is because it attempts to reuse 'freed' zone memory
  // instead of always allocating a new region.
  NodeDeque to_visit_;
115 116
  // Contains the nodes that can be changed into a compressed version of
  // themselves. In a way, it functions as a NodeSet since each node will be
117
  // contained at most once. It's a Vector since we care about insertion speed.
118
  NodeVector compressed_candidate_nodes_;
119 120 121 122 123 124 125
};

}  // namespace compiler
}  // namespace internal
}  // namespace v8

#endif  // V8_COMPILER_DECOMPRESSION_OPTIMIZER_H_