/src/hermes/lib/Optimizer/Scalar/CSE.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #define DEBUG_TYPE "cse" |
9 | | #include "hermes/Optimizer/Scalar/CSE.h" |
10 | | #include "hermes/IR/Analysis.h" |
11 | | #include "hermes/IR/CFG.h" |
12 | | #include "hermes/IR/Instrs.h" |
13 | | #include "hermes/Optimizer/Scalar/Utils.h" |
14 | | #include "hermes/Support/Statistic.h" |
15 | | |
16 | | #include "llvh/ADT/DenseMap.h" |
17 | | #include "llvh/ADT/DenseSet.h" |
18 | | #include "llvh/ADT/Hashing.h" |
19 | | #include "llvh/ADT/STLExtras.h" |
20 | | #include "llvh/ADT/ScopedHashTable.h" |
21 | | #include "llvh/Support/RecyclingAllocator.h" |
22 | | |
23 | | using namespace hermes; |
24 | | |
25 | | STATISTIC(NumCSE, "Number of instructions CSE'd"); |
26 | | |
27 | | //===----------------------------------------------------------------------===// |
28 | | // Simple Value |
29 | | //===----------------------------------------------------------------------===// |
30 | | |
31 | | namespace { |
32 | | /// CSEValue - Instances of this struct represent available values in the |
33 | | /// scoped hash table. |
34 | | struct CSEValue { |
35 | | Instruction *inst_; |
36 | | |
37 | 0 | CSEValue(Instruction *I) : inst_(I) { |
38 | 0 | assert((isSentinel() || canHandle(I)) && "Inst can't be handled!"); |
39 | 0 | } |
40 | | |
41 | 0 | bool isSentinel() const { |
42 | 0 | return inst_ == llvh::DenseMapInfo<Instruction *>::getEmptyKey() || |
43 | 0 | inst_ == llvh::DenseMapInfo<Instruction *>::getTombstoneKey(); |
44 | 0 | } |
45 | | |
46 | | /// Return true if we know how to CSE this instruction. |
47 | 0 | static bool canHandle(Instruction *Inst) { |
48 | 0 | return isSimpleSideEffectFreeInstruction(Inst); |
49 | 0 | } |
50 | | }; |
51 | | } // end anonymous namespace |
52 | | |
53 | | namespace llvh { |
54 | | template <> |
55 | | struct DenseMapInfo<CSEValue> { |
56 | 0 | static inline CSEValue getEmptyKey() { |
57 | 0 | return DenseMapInfo<hermes::Instruction *>::getEmptyKey(); |
58 | 0 | } |
59 | 0 | static inline CSEValue getTombstoneKey() { |
60 | 0 | return DenseMapInfo<hermes::Instruction *>::getTombstoneKey(); |
61 | 0 | } |
62 | | static unsigned getHashValue(CSEValue Val); |
63 | | static bool isEqual(CSEValue LHS, CSEValue RHS); |
64 | | }; |
65 | | } // end namespace llvh |
66 | | |
67 | 0 | unsigned llvh::DenseMapInfo<CSEValue>::getHashValue(CSEValue Val) { |
68 | 0 | return Val.inst_->getHashCode(); |
69 | 0 | } |
70 | | |
71 | 0 | bool llvh::DenseMapInfo<CSEValue>::isEqual(CSEValue LHS, CSEValue RHS) { |
72 | 0 | hermes::Instruction *LHSI = LHS.inst_, *RHSI = RHS.inst_; |
73 | 0 | if (LHS.isSentinel() || RHS.isSentinel()) |
74 | 0 | return LHSI == RHSI; |
75 | | |
76 | 0 | return LHSI->getKind() == RHSI->getKind() && LHSI->isIdenticalTo(RHSI); |
77 | 0 | } |
78 | | |
79 | | //===----------------------------------------------------------------------===// |
80 | | // CSE Interface |
81 | | //===----------------------------------------------------------------------===// |
82 | | |
83 | | namespace { |
84 | | |
85 | | class CSEContext; |
86 | | |
87 | | using CSEValueHTType = llvh::ScopedHashTableVal<CSEValue, Value *>; |
88 | | using AllocatorTy = |
89 | | llvh::RecyclingAllocator<llvh::BumpPtrAllocator, CSEValueHTType>; |
90 | | using ScopedHTType = llvh::ScopedHashTable< |
91 | | CSEValue, |
92 | | Value *, |
93 | | llvh::DenseMapInfo<CSEValue>, |
94 | | AllocatorTy>; |
95 | | |
96 | | // StackNode - contains all the needed information to create a stack for doing |
97 | | // a depth first traversal of the tree. This includes scopes for values and |
98 | | // loads as well as the generation. There is a child iterator so that the |
99 | | // children do not need to be store spearately. |
100 | | class StackNode : public DomTreeDFS::StackNode<CSEContext> { |
101 | | public: |
102 | | inline StackNode(CSEContext *ctx, const DominanceInfoNode *n); |
103 | | |
104 | | private: |
105 | | /// RAII to create and pop a scope when the stack node is created and |
106 | | /// destroyed. |
107 | | ScopedHTType::ScopeTy scope_; |
108 | | }; |
109 | | |
110 | | /// CSEContext - This pass does a simple depth-first walk of the dominator |
111 | | /// tree, eliminating trivially redundant instructions. |
112 | | class CSEContext : public DomTreeDFS::Visitor<CSEContext, StackNode> { |
113 | | public: |
114 | | CSEContext(const DominanceInfo &DT) |
115 | 0 | : DomTreeDFS::Visitor<CSEContext, StackNode>(DT) {} |
116 | | |
117 | 0 | bool run() { |
118 | 0 | return DFS(); |
119 | 0 | } |
120 | | |
121 | | bool processNode(StackNode *SN); |
122 | | |
123 | | private: |
124 | | friend StackNode; |
125 | | |
126 | | /// AvailableValues - This scoped hash table contains the current values of |
127 | | /// all of our simple scalar expressions. As we walk down the domtree, we |
128 | | /// look to see if instructions are in this. If so, we replace them with what |
129 | | /// we find, otherwise we insert them so that dominated values can succeed in |
130 | | /// their lookup. |
131 | | ScopedHTType availableValues_{}; |
132 | | }; |
133 | | |
134 | | inline StackNode::StackNode(CSEContext *ctx, const DominanceInfoNode *n) |
135 | | : DomTreeDFS::StackNode<CSEContext>(ctx, n), |
136 | 0 | scope_{ctx->availableValues_} {} |
137 | | } // end anonymous namespace |
138 | | |
139 | | //===----------------------------------------------------------------------===// |
140 | | // CSE Implementation |
141 | | //===----------------------------------------------------------------------===// |
142 | | |
143 | 0 | bool CSEContext::processNode(StackNode *SN) { |
144 | 0 | BasicBlock *BB = SN->node()->getBlock(); |
145 | 0 | bool changed = false; |
146 | | |
147 | | // Keep a list of instructions that should be deleted when the basic block |
148 | | // is processed. |
149 | 0 | IRBuilder::InstructionDestroyer destroyer; |
150 | | |
151 | | // See if any instructions in the block can be eliminated. If so, do it. If |
152 | | // not, add them to AvailableValues. |
153 | 0 | for (auto &Inst : *BB) { |
154 | | // If this is not a simple instruction that we can value number, skip it. |
155 | 0 | if (!CSEValue::canHandle(&Inst)) |
156 | 0 | continue; |
157 | | |
158 | | // Now that we know we have an instruction we understand see if the |
159 | | // instruction has an available value. If so, use it. |
160 | 0 | if (Value *V = availableValues_.lookup(&Inst)) { |
161 | 0 | Inst.replaceAllUsesWith(V); |
162 | 0 | destroyer.add(&Inst); |
163 | 0 | changed = true; |
164 | 0 | ++NumCSE; |
165 | 0 | continue; |
166 | 0 | } |
167 | | |
168 | | // Otherwise, just remember that this value is available. |
169 | 0 | availableValues_.insert(&Inst, &Inst); |
170 | 0 | } |
171 | |
|
172 | 0 | return changed; |
173 | 0 | } |
174 | | |
175 | 0 | bool CSE::runOnFunction(Function *F) { |
176 | 0 | DominanceInfo DT{F}; |
177 | 0 | CSEContext CCtx{DT}; |
178 | 0 | return CCtx.run(); |
179 | 0 | } |
180 | | |
181 | 0 | std::unique_ptr<Pass> hermes::createCSE() { |
182 | 0 | return std::make_unique<CSE>(); |
183 | 0 | } |
184 | | |
185 | | #undef DEBUG_TYPE |