propagator.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. // Copyright (c) 2017 Google Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #ifndef SOURCE_OPT_PROPAGATOR_H_
  15. #define SOURCE_OPT_PROPAGATOR_H_
  16. #include <functional>
  17. #include <queue>
  18. #include <set>
  19. #include <unordered_map>
  20. #include <unordered_set>
  21. #include <utility>
  22. #include <vector>
  23. #include "source/opt/ir_context.h"
  24. #include "source/opt/module.h"
  25. namespace spvtools {
  26. namespace opt {
  27. // Represents a CFG control edge.
  28. struct Edge {
  29. Edge(BasicBlock* b1, BasicBlock* b2) : source(b1), dest(b2) {
  30. assert(source && "CFG edges cannot have a null source block.");
  31. assert(dest && "CFG edges cannot have a null destination block.");
  32. }
  33. BasicBlock* source;
  34. BasicBlock* dest;
  35. bool operator<(const Edge& o) const {
  36. return std::make_pair(source->id(), dest->id()) <
  37. std::make_pair(o.source->id(), o.dest->id());
  38. }
  39. };
  40. // This class implements a generic value propagation algorithm based on the
  41. // conditional constant propagation algorithm proposed in
  42. //
  43. // Constant propagation with conditional branches,
  44. // Wegman and Zadeck, ACM TOPLAS 13(2):181-210.
  45. //
  46. // A Propagation Engine for GCC
  47. // Diego Novillo, GCC Summit 2005
  48. // http://ols.fedoraproject.org/GCC/Reprints-2005/novillo-Reprint.pdf
  49. //
  50. // The purpose of this implementation is to act as a common framework for any
  51. // transformation that needs to propagate values from statements producing new
  52. // values to statements using those values. Simulation proceeds as follows:
  53. //
  54. // 1- Initially, all edges of the CFG are marked not executable and the CFG
  55. // worklist is seeded with all the statements in the entry basic block.
  56. //
  57. // 2- Every instruction I is simulated by calling a pass-provided function
  58. // |visit_fn|. This function is responsible for three things:
  59. //
  60. // (a) Keep a value table of interesting values. This table maps SSA IDs to
  61. // their values. For instance, when implementing constant propagation,
  62. // given a store operation 'OpStore %f %int_3', |visit_fn| should assign
  63. // the value 3 to the table slot for %f.
  64. //
  65. // In general, |visit_fn| will need to use the value table to replace its
  66. // operands, fold the result and decide whether a new value needs to be
  67. // stored in the table. |visit_fn| should only create a new mapping in
  68. // the value table if all the operands in the instruction are known and
  69. // present in the value table.
  70. //
  71. // (b) Return a status indicator to direct the propagator logic. Once the
  72. // instruction is simulated, the propagator needs to know whether this
  73. // instruction produced something interesting. This is indicated via
  74. // |visit_fn|'s return value:
  75. //
  76. // SSAPropagator::kNotInteresting: Instruction I produces nothing of
  77. // interest and does not affect any of the work lists. The
  78. // propagator will visit the statement again if any of its operands
  79. // produce an interesting value in the future.
  80. //
  81. // |visit_fn| should always return this value when it is not sure
  82. // whether the instruction will produce an interesting value in the
  83. // future or not. For instance, for constant propagation, an OpIAdd
  84. // instruction may produce a constant if its two operands are
  85. // constant, but the first time we visit the instruction, we still
  86. // may not have its operands in the value table.
  87. //
  88. // SSAPropagator::kVarying: The value produced by I cannot be determined
  89. // at compile time. Further simulation of I is not required. The
  90. // propagator will not visit this instruction again. Additionally,
  91. // the propagator will add all the instructions at the end of SSA
  92. // def-use edges to be simulated again.
  93. //
  94. // If I is a basic block terminator, it will mark all outgoing edges
  95. // as executable so they are traversed one more time. Eventually
  96. // the kVarying attribute will be spread out to all the data and
  97. // control dependents for I.
  98. //
  99. // It is important for propagation to use kVarying as a bottom value
  100. // for the propagation lattice. It should never be possible for an
  101. // instruction to return kVarying once and kInteresting on a second
  102. // visit. Otherwise, propagation would not stabilize.
  103. //
  104. // SSAPropagator::kInteresting: Instruction I produces a value that can
  105. // be computed at compile time. In this case, |visit_fn| should
  106. // create a new mapping between I's result ID and the produced
  107. // value. Much like the kNotInteresting case, the propagator will
  108. // visit this instruction again if any of its operands changes.
  109. // This is useful when the statement changes from one interesting
  110. // state to another.
  111. //
  112. // (c) For conditional branches, |visit_fn| may decide which edge to take out
  113. // of I's basic block. For example, if the operand for an OpSwitch is
  114. // known to take a specific constant value, |visit_fn| should figure out
  115. // the destination basic block and pass it back by setting the second
  116. // argument to |visit_fn|.
  117. //
  118. // At the end of propagation, values in the value table are guaranteed to be
  119. // stable and can be replaced in the IR.
  120. //
  121. // 3- The propagator keeps two work queues. Instructions are only added to
  122. // these queues if they produce an interesting or varying value. None of this
  123. // should be handled by |visit_fn|. The propagator keeps track of this
  124. // automatically (see SSAPropagator::Simulate for implementation).
  125. //
  126. // CFG blocks: contains the queue of blocks to be simulated.
  127. // Blocks are added to this queue if their incoming edges are
  128. // executable.
  129. //
  130. // SSA Edges: An SSA edge is a def-use edge between a value-producing
  131. // instruction and its use instruction. The SSA edges list
  132. // contains the statements at the end of a def-use edge that need
  133. // to be re-visited when an instruction produces a kVarying or
  134. // kInteresting result.
  135. //
  136. // 4- Simulation terminates when all work queues are drained.
  137. //
  138. //
  139. // EXAMPLE: Basic constant store propagator.
  140. //
  141. // Suppose we want to propagate all constant assignments of the form "OpStore
  142. // %id %cst" where "%id" is some variable and "%cst" an OpConstant. The
  143. // following code builds a table |values| where every id that was assigned a
  144. // constant value is mapped to the constant value it was assigned.
  145. //
  146. // auto ctx = BuildModule(...);
  147. // std::map<uint32_t, uint32_t> values;
  148. // const auto visit_fn = [&ctx, &values](Instruction* instr,
  149. // BasicBlock** dest_bb) {
  150. // if (instr->opcode() == spv::Op::OpStore) {
  151. // uint32_t rhs_id = instr->GetSingleWordOperand(1);
  152. // Instruction* rhs_def = ctx->get_def_use_mgr()->GetDef(rhs_id);
  153. // if (rhs_def->opcode() == spv::Op::OpConstant) {
  154. // uint32_t val = rhs_def->GetSingleWordOperand(2);
  155. // values[rhs_id] = val;
  156. // return SSAPropagator::kInteresting;
  157. // }
  158. // }
  159. // return SSAPropagator::kVarying;
  160. // };
  161. // SSAPropagator propagator(ctx.get(), &cfg, visit_fn);
  162. // propagator.Run(&fn);
  163. //
  164. // Given the code:
  165. //
  166. // %int_4 = OpConstant %int 4
  167. // %int_3 = OpConstant %int 3
  168. // %int_1 = OpConstant %int 1
  169. // OpStore %x %int_4
  170. // OpStore %y %int_3
  171. // OpStore %z %int_1
  172. //
  173. // After SSAPropagator::Run returns, the |values| map will contain the entries:
  174. // values[%x] = 4, values[%y] = 3, and, values[%z] = 1.
  175. class SSAPropagator {
  176. public:
  177. // Lattice values used for propagation. See class documentation for
  178. // a description.
  179. enum PropStatus { kNotInteresting, kInteresting, kVarying };
  180. using VisitFunction = std::function<PropStatus(Instruction*, BasicBlock**)>;
  181. SSAPropagator(IRContext* context, const VisitFunction& visit_fn)
  182. : ctx_(context), visit_fn_(visit_fn) {}
  183. // Runs the propagator on function |fn|. Returns true if changes were made to
  184. // the function. Otherwise, it returns false.
  185. bool Run(Function* fn);
  186. // Returns true if the |i|th argument for |phi| comes through a CFG edge that
  187. // has been marked executable. |i| should be an index value accepted by
  188. // Instruction::GetSingleWordOperand.
  189. bool IsPhiArgExecutable(Instruction* phi, uint32_t i) const;
  190. // Returns true if |inst| has a recorded status. This will be true once |inst|
  191. // has been simulated once.
  192. bool HasStatus(Instruction* inst) const { return statuses_.count(inst); }
  193. // Returns the current propagation status of |inst|. Assumes
  194. // |HasStatus(inst)| returns true.
  195. PropStatus Status(Instruction* inst) const {
  196. return statuses_.find(inst)->second;
  197. }
  198. // Records the propagation status |status| for |inst|. Returns true if the
  199. // status for |inst| has changed or set was set for the first time.
  200. bool SetStatus(Instruction* inst, PropStatus status);
  201. private:
  202. // Initialize processing.
  203. void Initialize(Function* fn);
  204. // Simulate the execution |block| by calling |visit_fn_| on every instruction
  205. // in it.
  206. bool Simulate(BasicBlock* block);
  207. // Simulate the execution of |instr| by replacing all the known values in
  208. // every operand and determining whether the result is interesting for
  209. // propagation. This invokes the callback function |visit_fn_| to determine
  210. // the value computed by |instr|.
  211. bool Simulate(Instruction* instr);
  212. // Returns true if |instr| should be simulated again.
  213. bool ShouldSimulateAgain(Instruction* instr) const {
  214. return do_not_simulate_.find(instr) == do_not_simulate_.end();
  215. }
  216. // Add |instr| to the set of instructions not to simulate again.
  217. void DontSimulateAgain(Instruction* instr) { do_not_simulate_.insert(instr); }
  218. // Returns true if |block| has been simulated already.
  219. bool BlockHasBeenSimulated(BasicBlock* block) const {
  220. return simulated_blocks_.find(block) != simulated_blocks_.end();
  221. }
  222. // Marks block |block| as simulated.
  223. void MarkBlockSimulated(BasicBlock* block) {
  224. simulated_blocks_.insert(block);
  225. }
  226. // Marks |edge| as executable. Returns false if the edge was already marked
  227. // as executable.
  228. bool MarkEdgeExecutable(const Edge& edge) {
  229. return executable_edges_.insert(edge).second;
  230. }
  231. // Returns true if |edge| has been marked as executable.
  232. bool IsEdgeExecutable(const Edge& edge) const {
  233. return executable_edges_.find(edge) != executable_edges_.end();
  234. }
  235. // Returns a pointer to the def-use manager for |ctx_|.
  236. analysis::DefUseManager* get_def_use_mgr() const {
  237. return ctx_->get_def_use_mgr();
  238. }
  239. // If the CFG edge |e| has not been executed, this function adds |e|'s
  240. // destination block to the work list.
  241. void AddControlEdge(const Edge& e);
  242. // Adds all the instructions that use the result of |instr| to the SSA edges
  243. // work list. If |instr| produces no result id, this does nothing.
  244. void AddSSAEdges(Instruction* instr);
  245. // IR context to use.
  246. IRContext* ctx_;
  247. // Function that visits instructions during simulation. The output of this
  248. // function is used to determine if the simulated instruction produced a value
  249. // interesting for propagation. The function is responsible for keeping
  250. // track of interesting values by storing them in some user-provided map.
  251. VisitFunction visit_fn_;
  252. // SSA def-use edges to traverse. Each entry is a destination statement for an
  253. // SSA def-use edge as returned by |def_use_manager_|.
  254. std::queue<Instruction*> ssa_edge_uses_;
  255. // Blocks to simulate.
  256. std::queue<BasicBlock*> blocks_;
  257. // Blocks simulated during propagation.
  258. std::unordered_set<BasicBlock*> simulated_blocks_;
  259. // Set of instructions that should not be simulated again because they have
  260. // been found to be in the kVarying state.
  261. std::unordered_set<Instruction*> do_not_simulate_;
  262. // Map between a basic block and its predecessor edges.
  263. // TODO(dnovillo): Move this to CFG and always build them. Alternately,
  264. // move it to IRContext and build CFG preds/succs on-demand.
  265. std::unordered_map<BasicBlock*, std::vector<Edge>> bb_preds_;
  266. // Map between a basic block and its successor edges.
  267. // TODO(dnovillo): Move this to CFG and always build them. Alternately,
  268. // move it to IRContext and build CFG preds/succs on-demand.
  269. std::unordered_map<BasicBlock*, std::vector<Edge>> bb_succs_;
  270. // Set of executable CFG edges.
  271. std::set<Edge> executable_edges_;
  272. // Tracks instruction propagation status.
  273. std::unordered_map<Instruction*, SSAPropagator::PropStatus> statuses_;
  274. };
  275. std::ostream& operator<<(std::ostream& str,
  276. const SSAPropagator::PropStatus& status);
  277. } // namespace opt
  278. } // namespace spvtools
  279. #endif // SOURCE_OPT_PROPAGATOR_H_