1 //===- CommonFolders.h - Common Operation Folders----------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This header file declares various common operation folders. These folders
10 // are intended to be used by dialects to support common folding behavior
11 // without requiring each dialect to provide its own implementation.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #ifndef MLIR_DIALECT_COMMONFOLDERS_H
16 #define MLIR_DIALECT_COMMONFOLDERS_H
17
18 #include "mlir/IR/BuiltinAttributes.h"
19 #include "mlir/IR/BuiltinTypes.h"
20 #include "llvm/ADT/ArrayRef.h"
21 #include "llvm/ADT/STLExtras.h"
22
23 namespace mlir {
24 /// Performs constant folding `calculate` with element-wise behavior on the two
25 /// attributes in `operands` and returns the result if possible.
26 template <
27 class AttrElementT, class ElementValueT = typename AttrElementT::ValueType,
28 class CalculationT =
29 function_ref<Optional<ElementValueT>(ElementValueT, ElementValueT)>>
constFoldBinaryOpConditional(ArrayRef<Attribute> operands,const CalculationT & calculate)30 Attribute constFoldBinaryOpConditional(ArrayRef<Attribute> operands,
31 const CalculationT &calculate) {
32 assert(operands.size() == 2 && "binary op takes two operands");
33 if (!operands[0] || !operands[1])
34 return {};
35 if (operands[0].getType() != operands[1].getType())
36 return {};
37
38 if (operands[0].isa<AttrElementT>() && operands[1].isa<AttrElementT>()) {
39 auto lhs = operands[0].cast<AttrElementT>();
40 auto rhs = operands[1].cast<AttrElementT>();
41
42 auto calRes = calculate(lhs.getValue(), rhs.getValue());
43
44 if (!calRes)
45 return {};
46
47 return AttrElementT::get(lhs.getType(), *calRes);
48 }
49
50 if (operands[0].isa<SplatElementsAttr>() &&
51 operands[1].isa<SplatElementsAttr>()) {
52 // Both operands are splats so we can avoid expanding the values out and
53 // just fold based on the splat value.
54 auto lhs = operands[0].cast<SplatElementsAttr>();
55 auto rhs = operands[1].cast<SplatElementsAttr>();
56
57 auto elementResult = calculate(lhs.getSplatValue<ElementValueT>(),
58 rhs.getSplatValue<ElementValueT>());
59 if (!elementResult)
60 return {};
61
62 return DenseElementsAttr::get(lhs.getType(), *elementResult);
63 } else if (operands[0].isa<ElementsAttr>() &&
64 operands[1].isa<ElementsAttr>()) {
65 // Operands are ElementsAttr-derived; perform an element-wise fold by
66 // expanding the values.
67 auto lhs = operands[0].cast<ElementsAttr>();
68 auto rhs = operands[1].cast<ElementsAttr>();
69
70 auto lhsIt = lhs.value_begin<ElementValueT>();
71 auto rhsIt = rhs.value_begin<ElementValueT>();
72 SmallVector<ElementValueT, 4> elementResults;
73 elementResults.reserve(lhs.getNumElements());
74 for (size_t i = 0, e = lhs.getNumElements(); i < e; ++i, ++lhsIt, ++rhsIt) {
75 auto elementResult = calculate(*lhsIt, *rhsIt);
76 if (!elementResult)
77 return {};
78 elementResults.push_back(*elementResult);
79 }
80
81 return DenseElementsAttr::get(lhs.getType(), elementResults);
82 }
83 return {};
84 }
85
86 template <class AttrElementT,
87 class ElementValueT = typename AttrElementT::ValueType,
88 class CalculationT =
89 function_ref<ElementValueT(ElementValueT, ElementValueT)>>
constFoldBinaryOp(ArrayRef<Attribute> operands,const CalculationT & calculate)90 Attribute constFoldBinaryOp(ArrayRef<Attribute> operands,
91 const CalculationT &calculate) {
92 return constFoldBinaryOpConditional<AttrElementT>(
93 operands,
94 [&](ElementValueT a, ElementValueT b) -> Optional<ElementValueT> {
95 return calculate(a, b);
96 });
97 }
98
99 /// Performs constant folding `calculate` with element-wise behavior on the one
100 /// attributes in `operands` and returns the result if possible.
101 template <
102 class AttrElementT, class ElementValueT = typename AttrElementT::ValueType,
103 class CalculationT = function_ref<Optional<ElementValueT>(ElementValueT)>>
constFoldUnaryOpConditional(ArrayRef<Attribute> operands,const CalculationT && calculate)104 Attribute constFoldUnaryOpConditional(ArrayRef<Attribute> operands,
105 const CalculationT &&calculate) {
106 assert(operands.size() == 1 && "unary op takes one operands");
107 if (!operands[0])
108 return {};
109
110 if (operands[0].isa<AttrElementT>()) {
111 auto op = operands[0].cast<AttrElementT>();
112
113 auto res = calculate(op.getValue());
114 if (!res)
115 return {};
116 return AttrElementT::get(op.getType(), *res);
117 }
118 if (operands[0].isa<SplatElementsAttr>()) {
119 // Both operands are splats so we can avoid expanding the values out and
120 // just fold based on the splat value.
121 auto op = operands[0].cast<SplatElementsAttr>();
122
123 auto elementResult = calculate(op.getSplatValue<ElementValueT>());
124 if (!elementResult)
125 return {};
126 return DenseElementsAttr::get(op.getType(), *elementResult);
127 } else if (operands[0].isa<ElementsAttr>()) {
128 // Operands are ElementsAttr-derived; perform an element-wise fold by
129 // expanding the values.
130 auto op = operands[0].cast<ElementsAttr>();
131
132 auto opIt = op.value_begin<ElementValueT>();
133 SmallVector<ElementValueT> elementResults;
134 elementResults.reserve(op.getNumElements());
135 for (size_t i = 0, e = op.getNumElements(); i < e; ++i, ++opIt) {
136 auto elementResult = calculate(*opIt);
137 if (!elementResult)
138 return {};
139 elementResults.push_back(*elementResult);
140 }
141 return DenseElementsAttr::get(op.getType(), elementResults);
142 }
143 return {};
144 }
145
146 template <class AttrElementT,
147 class ElementValueT = typename AttrElementT::ValueType,
148 class CalculationT = function_ref<ElementValueT(ElementValueT)>>
constFoldUnaryOp(ArrayRef<Attribute> operands,const CalculationT && calculate)149 Attribute constFoldUnaryOp(ArrayRef<Attribute> operands,
150 const CalculationT &&calculate) {
151 return constFoldUnaryOpConditional<AttrElementT>(
152 operands,
153 [&](ElementValueT a) -> Optional<ElementValueT> { return calculate(a); });
154 }
155
156 template <
157 class AttrElementT, class TargetAttrElementT,
158 class ElementValueT = typename AttrElementT::ValueType,
159 class TargetElementValueT = typename TargetAttrElementT::ValueType,
160 class CalculationT = function_ref<TargetElementValueT(ElementValueT, bool)>>
constFoldCastOp(ArrayRef<Attribute> operands,Type resType,const CalculationT & calculate)161 Attribute constFoldCastOp(ArrayRef<Attribute> operands, Type resType,
162 const CalculationT &calculate) {
163 assert(operands.size() == 1 && "Cast op takes one operand");
164 if (!operands[0])
165 return {};
166
167 if (operands[0].isa<AttrElementT>()) {
168 auto op = operands[0].cast<AttrElementT>();
169 bool castStatus = true;
170 auto res = calculate(op.getValue(), castStatus);
171 if (!castStatus)
172 return {};
173 return TargetAttrElementT::get(resType, res);
174 }
175 if (operands[0].isa<SplatElementsAttr>()) {
176 // The operand is a splat so we can avoid expanding the values out and
177 // just fold based on the splat value.
178 auto op = operands[0].cast<SplatElementsAttr>();
179 bool castStatus = true;
180 auto elementResult =
181 calculate(op.getSplatValue<ElementValueT>(), castStatus);
182 if (!castStatus)
183 return {};
184 return DenseElementsAttr::get(resType, elementResult);
185 }
186 if (operands[0].isa<ElementsAttr>()) {
187 // Operand is ElementsAttr-derived; perform an element-wise fold by
188 // expanding the value.
189 auto op = operands[0].cast<ElementsAttr>();
190 bool castStatus = true;
191 auto opIt = op.value_begin<ElementValueT>();
192 SmallVector<TargetElementValueT> elementResults;
193 elementResults.reserve(op.getNumElements());
194 for (size_t i = 0, e = op.getNumElements(); i < e; ++i, ++opIt) {
195 auto elt = calculate(*opIt, castStatus);
196 if (!castStatus)
197 return {};
198 elementResults.push_back(elt);
199 }
200
201 return DenseElementsAttr::get(resType, elementResults);
202 }
203 return {};
204 }
205
206 } // namespace mlir
207
208 #endif // MLIR_DIALECT_COMMONFOLDERS_H
209