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_CONST_FOLDING_RULES_H_
16#define SOURCE_OPT_CONST_FOLDING_RULES_H_
17
18#include <unordered_map>
19#include <vector>
20
21#include "source/opt/constants.h"
22
23namespace spvtools {
24namespace opt {
25
26// Constant Folding Rules:
27//
28// The folding mechanism is built around the concept of a |ConstantFoldingRule|.
29// A constant folding rule is a function that implements a method of simplifying
30// an instruction to a constant.
31//
32// The inputs to a folding rule are:
33// |inst| - the instruction to be simplified.
34// |constants| - if an in-operands is an id of a constant, then the
35// corresponding value in |constants| contains that
36// constant value. Otherwise, the corresponding entry in
37// |constants| is |nullptr|.
38//
39// A constant folding rule returns a pointer to an Constant if |inst| can be
40// simplified using this rule. Otherwise, it returns |nullptr|.
41//
42// See const_folding_rules.cpp for examples on how to write a constant folding
43// rule.
44//
45// Be sure to add new constant folding rules to the table of constant folding
46// rules in the constructor for ConstantFoldingRules. The new rule should be
47// added to the list for every opcode that it applies to. Note that earlier
48// rules in the list are given priority. That is, if an earlier rule is able to
49// fold an instruction, the later rules will not be attempted.
50
51using ConstantFoldingRule = std::function<const analysis::Constant*(
52 IRContext* ctx, Instruction* inst,
53 const std::vector<const analysis::Constant*>& constants)>;
54
55class ConstantFoldingRules {
56 protected:
57 // The |Key| and |Value| structs are used to by-pass a "decorated name length
58 // exceeded, name was truncated" warning on VS2013 and VS2015.
59 struct Key {
60 uint32_t instruction_set;
61 uint32_t opcode;
62 };
63
64 friend bool operator<(const Key& a, const Key& b) {
65 if (a.instruction_set < b.instruction_set) {
66 return true;
67 }
68 if (a.instruction_set > b.instruction_set) {
69 return false;
70 }
71 return a.opcode < b.opcode;
72 }
73
74 struct Value {
75 std::vector<ConstantFoldingRule> value;
76 void push_back(ConstantFoldingRule rule) { value.push_back(rule); }
77 };
78
79 public:
80 ConstantFoldingRules(IRContext* ctx) : context_(ctx) {}
81 virtual ~ConstantFoldingRules() = default;
82
83 // Returns true if there is at least 1 folding rule for |opcode|.
84 bool HasFoldingRule(const Instruction* inst) const {
85 return !GetRulesForInstruction(inst).empty();
86 }
87
88 // Returns true if there is at least 1 folding rule for |inst|.
89 const std::vector<ConstantFoldingRule>& GetRulesForInstruction(
90 const Instruction* inst) const {
91 if (inst->opcode() != SpvOpExtInst) {
92 auto it = rules_.find(inst->opcode());
93 if (it != rules_.end()) {
94 return it->second.value;
95 }
96 } else {
97 uint32_t ext_inst_id = inst->GetSingleWordInOperand(0);
98 uint32_t ext_opcode = inst->GetSingleWordInOperand(1);
99 auto it = ext_rules_.find({ext_inst_id, ext_opcode});
100 if (it != ext_rules_.end()) {
101 return it->second.value;
102 }
103 }
104 return empty_vector_;
105 }
106
107 // Add the folding rules.
108 virtual void AddFoldingRules();
109
110 protected:
111 // |rules[opcode]| is the set of rules that can be applied to instructions
112 // with |opcode| as the opcode.
113 std::unordered_map<uint32_t, Value> rules_;
114
115 // The folding rules for extended instructions.
116 std::map<Key, Value> ext_rules_;
117
118 private:
119 // The context that the instruction to be folded will be a part of.
120 IRContext* context_;
121
122 // The empty set of rules to be used as the default return value in
123 // |GetRulesForInstruction|.
124 std::vector<ConstantFoldingRule> empty_vector_;
125};
126
127} // namespace opt
128} // namespace spvtools
129
130#endif // SOURCE_OPT_CONST_FOLDING_RULES_H_
131