1 // Copyright (c) 2018 Google LLC
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 
15 #ifndef SOURCE_OPT_FOLDING_RULES_H_
16 #define SOURCE_OPT_FOLDING_RULES_H_
17 
18 #include <cstdint>
19 #include <unordered_map>
20 #include <vector>
21 
22 #include "source/opt/constants.h"
23 
24 namespace spvtools {
25 namespace opt {
26 
27 // Folding Rules:
28 //
29 // The folding mechanism is built around the concept of a |FoldingRule|.  A
30 // folding rule is a function that implements a method of simplifying an
31 // instruction.
32 //
33 // The inputs to a folding rule are:
34 //     |inst| - the instruction to be simplified.
35 //     |constants| - if an in-operands is an id of a constant, then the
36 //                   corresponding value in |constants| contains that
37 //                   constant value.  Otherwise, the corresponding entry in
38 //                   |constants| is |nullptr|.
39 //
40 // A folding rule returns true if |inst| can be simplified using this rule.  If
41 // the instruction can be simplified, then |inst| is changed to the simplified
42 // instruction.  Otherwise, |inst| remains the same.
43 //
44 // See folding_rules.cpp for examples on how to write a folding rule.  It is
45 // important to note that if |inst| can be folded to the result of an
46 // instruction that feed it, then |inst| should be changed to an OpCopyObject
47 // that copies that id.
48 //
49 // Be sure to add new folding rules to the table of folding rules in the
50 // constructor for FoldingRules.  The new rule should be added to the list for
51 // every opcode that it applies to.  Note that earlier rules in the list are
52 // given priority.  That is, if an earlier rule is able to fold an instruction,
53 // the later rules will not be attempted.
54 
55 using FoldingRule = std::function<bool(
56     IRContext* context, Instruction* inst,
57     const std::vector<const analysis::Constant*>& constants)>;
58 
59 class FoldingRules {
60  public:
61   using FoldingRuleSet = std::vector<FoldingRule>;
62 
FoldingRules(IRContext * ctx)63   explicit FoldingRules(IRContext* ctx) : context_(ctx) {}
64   virtual ~FoldingRules() = default;
65 
GetRulesForInstruction(Instruction * inst)66   const FoldingRuleSet& GetRulesForInstruction(Instruction* inst) const {
67     if (inst->opcode() != SpvOpExtInst) {
68       auto it = rules_.find(inst->opcode());
69       if (it != rules_.end()) {
70         return it->second;
71       }
72     } else {
73       uint32_t ext_inst_id = inst->GetSingleWordInOperand(0);
74       uint32_t ext_opcode = inst->GetSingleWordInOperand(1);
75       auto it = ext_rules_.find({ext_inst_id, ext_opcode});
76       if (it != ext_rules_.end()) {
77         return it->second;
78       }
79     }
80     return empty_vector_;
81   }
82 
context()83   IRContext* context() { return context_; }
84 
85   // Adds the folding rules for the object.
86   virtual void AddFoldingRules();
87 
88  protected:
89   // The folding rules for core instructions.
90   std::unordered_map<uint32_t, FoldingRuleSet> rules_;
91 
92   // The folding rules for extended instructions.
93   struct Key {
94     uint32_t instruction_set;
95     uint32_t opcode;
96   };
97 
98   friend bool operator<(const Key& a, const Key& b) {
99     if (a.instruction_set < b.instruction_set) {
100       return true;
101     }
102     if (a.instruction_set > b.instruction_set) {
103       return false;
104     }
105     return a.opcode < b.opcode;
106   }
107 
108   std::map<Key, FoldingRuleSet> ext_rules_;
109 
110  private:
111   IRContext* context_;
112   FoldingRuleSet empty_vector_;
113 };
114 
115 }  // namespace opt
116 }  // namespace spvtools
117 
118 #endif  // SOURCE_OPT_FOLDING_RULES_H_
119