1 | //===- OpBuildGen.cpp - TableGen OpBuildGen Tests -------------------------===// |
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 | // Test TableGen generated build() methods on Operations. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "TestDialect.h" |
14 | #include "TestOps.h" |
15 | #include "mlir/IR/Attributes.h" |
16 | #include "mlir/IR/Builders.h" |
17 | #include "mlir/IR/BuiltinTypes.h" |
18 | #include "mlir/IR/Dialect.h" |
19 | #include "gmock/gmock.h" |
20 | #include <vector> |
21 | |
22 | namespace mlir { |
23 | |
24 | //===----------------------------------------------------------------------===// |
25 | // Test Fixture |
26 | //===----------------------------------------------------------------------===// |
27 | |
28 | static MLIRContext &getContext() { |
29 | static MLIRContext ctx; |
30 | ctx.getOrLoadDialect<test::TestDialect>(); |
31 | return ctx; |
32 | } |
33 | /// Test fixture for providing basic utilities for testing. |
34 | class OpBuildGenTest : public ::testing::Test { |
35 | protected: |
36 | OpBuildGenTest() |
37 | : ctx(getContext()), builder(&ctx), loc(builder.getUnknownLoc()), |
38 | i32Ty(builder.getI32Type()), f32Ty(builder.getF32Type()), |
39 | cstI32(builder.create<test::TableGenConstant>(loc, i32Ty)), |
40 | cstF32(builder.create<test::TableGenConstant>(loc, f32Ty)), |
41 | noAttrs(), attrStorage{builder.getNamedAttr(name: "attr0" , |
42 | val: builder.getBoolAttr(value: true)), |
43 | builder.getNamedAttr( |
44 | "attr1" , builder.getI32IntegerAttr(33))}, |
45 | attrs(attrStorage) {} |
46 | |
47 | // Verify that `op` has the given set of result types, operands, and |
48 | // attributes. |
49 | template <typename OpTy> |
50 | void verifyOp(OpTy &&concreteOp, std::vector<Type> resultTypes, |
51 | std::vector<Value> operands, |
52 | std::vector<NamedAttribute> attrs) { |
53 | ASSERT_NE(concreteOp, nullptr); |
54 | Operation *op = concreteOp.getOperation(); |
55 | |
56 | EXPECT_EQ(op->getNumResults(), resultTypes.size()); |
57 | for (unsigned idx : llvm::seq(Begin: 0U, End: op->getNumResults())) |
58 | EXPECT_EQ(op->getResult(idx).getType(), resultTypes[idx]); |
59 | |
60 | EXPECT_EQ(op->getNumOperands(), operands.size()); |
61 | for (unsigned idx : llvm::seq(Begin: 0U, End: op->getNumOperands())) |
62 | EXPECT_EQ(op->getOperand(idx), operands[idx]); |
63 | |
64 | EXPECT_EQ(op->getAttrs().size(), attrs.size()); |
65 | for (unsigned idx : llvm::seq<unsigned>(Begin: 0U, End: attrs.size())) |
66 | EXPECT_EQ(op->getAttr(attrs[idx].getName().strref()), |
67 | attrs[idx].getValue()); |
68 | |
69 | EXPECT_TRUE(mlir::succeeded(concreteOp.verify())); |
70 | concreteOp.erase(); |
71 | } |
72 | |
73 | template <typename OpTy> |
74 | void verifyOp(OpTy &&concreteOp, std::vector<Type> resultTypes, |
75 | std::vector<Value> operands1, std::vector<Value> operands2, |
76 | std::vector<NamedAttribute> attrs) { |
77 | ASSERT_NE(concreteOp, nullptr); |
78 | Operation *op = concreteOp.getOperation(); |
79 | |
80 | EXPECT_EQ(op->getNumResults(), resultTypes.size()); |
81 | for (unsigned idx : llvm::seq(Begin: 0U, End: op->getNumResults())) |
82 | EXPECT_EQ(op->getResult(idx).getType(), resultTypes[idx]); |
83 | |
84 | auto operands = llvm::to_vector(Range: llvm::concat<Value>(Ranges&: operands1, Ranges&: operands2)); |
85 | EXPECT_EQ(op->getNumOperands(), operands.size()); |
86 | for (unsigned idx : llvm::seq(Begin: 0U, End: op->getNumOperands())) |
87 | EXPECT_EQ(op->getOperand(idx), operands[idx]); |
88 | |
89 | EXPECT_EQ(op->getAttrs().size(), attrs.size()); |
90 | if (op->getAttrs().size() != attrs.size()) { |
91 | // Simple export where there is mismatch count. |
92 | llvm::errs() << "Op attrs:\n" ; |
93 | for (auto it : op->getAttrs()) |
94 | llvm::errs() << "\t" << it.getName() << " = " << it.getValue() << "\n" ; |
95 | |
96 | llvm::errs() << "Expected attrs:\n" ; |
97 | for (auto it : attrs) |
98 | llvm::errs() << "\t" << it.getName() << " = " << it.getValue() << "\n" ; |
99 | } else { |
100 | for (unsigned idx : llvm::seq<unsigned>(Begin: 0U, End: attrs.size())) |
101 | EXPECT_EQ(op->getAttr(attrs[idx].getName().strref()), |
102 | attrs[idx].getValue()); |
103 | } |
104 | |
105 | EXPECT_TRUE(mlir::succeeded(concreteOp.verify())); |
106 | concreteOp.erase(); |
107 | } |
108 | |
109 | protected: |
110 | MLIRContext &ctx; |
111 | OpBuilder builder; |
112 | Location loc; |
113 | Type i32Ty; |
114 | Type f32Ty; |
115 | OwningOpRef<test::TableGenConstant> cstI32; |
116 | OwningOpRef<test::TableGenConstant> cstF32; |
117 | |
118 | ArrayRef<NamedAttribute> noAttrs; |
119 | std::vector<NamedAttribute> attrStorage; |
120 | ArrayRef<NamedAttribute> attrs; |
121 | }; |
122 | |
123 | /// Test basic build methods. |
124 | TEST_F(OpBuildGenTest, BasicBuildMethods) { |
125 | // Test separate args, separate results build method. |
126 | auto op = builder.create<test::TableGenBuildOp0>(loc, i32Ty, *cstI32); |
127 | verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); |
128 | |
129 | // Test separate args, collective results build method. |
130 | op = builder.create<test::TableGenBuildOp0>(loc, TypeRange{i32Ty}, *cstI32); |
131 | verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); |
132 | |
133 | // Test collective args, collective params build method. |
134 | op = builder.create<test::TableGenBuildOp0>(loc, TypeRange{i32Ty}, |
135 | ValueRange{*cstI32}); |
136 | verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); |
137 | |
138 | // Test collective args, collective results, non-empty attributes |
139 | op = builder.create<test::TableGenBuildOp0>(loc, TypeRange{i32Ty}, |
140 | ValueRange{*cstI32}, attrs); |
141 | verifyOp(op, {i32Ty}, {*cstI32}, attrs); |
142 | } |
143 | |
144 | /// The following 3 tests exercise build methods generated for operations |
145 | /// with a combination of: |
146 | /// |
147 | /// single variadic arg x |
148 | /// {single variadic result, non-variadic result, multiple variadic results} |
149 | /// |
150 | /// Specifically to test that ODS framework does not generate ambiguous |
151 | /// build() methods that fail to compile. |
152 | |
153 | /// Test build methods for an Op with a single varadic arg and a single |
154 | /// variadic result. |
155 | TEST_F(OpBuildGenTest, BuildMethodsSingleVariadicArgAndResult) { |
156 | // Test collective args, collective results method, building a unary op. |
157 | auto op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty}, |
158 | ValueRange{*cstI32}); |
159 | verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); |
160 | |
161 | // Test collective args, collective results method, building a unary op with |
162 | // named attributes. |
163 | op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty}, |
164 | ValueRange{*cstI32}, attrs); |
165 | verifyOp(op, {i32Ty}, {*cstI32}, attrs); |
166 | |
167 | // Test collective args, collective results method, building a binary op. |
168 | op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty, f32Ty}, |
169 | ValueRange{*cstI32, *cstF32}); |
170 | verifyOp(op, {i32Ty, f32Ty}, {*cstI32, *cstF32}, noAttrs); |
171 | |
172 | // Test collective args, collective results method, building a binary op with |
173 | // named attributes. |
174 | op = builder.create<test::TableGenBuildOp1>( |
175 | loc, TypeRange{i32Ty, f32Ty}, ValueRange{*cstI32, *cstF32}, attrs); |
176 | verifyOp(op, {i32Ty, f32Ty}, {*cstI32, *cstF32}, attrs); |
177 | } |
178 | |
179 | /// Test build methods for an Op with a single varadic arg and a non-variadic |
180 | /// result. |
181 | TEST_F(OpBuildGenTest, BuildMethodsSingleVariadicArgNonVariadicResults) { |
182 | // Test separate arg, separate param build method. |
183 | auto op = |
184 | builder.create<test::TableGenBuildOp1>(loc, i32Ty, ValueRange{*cstI32}); |
185 | verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); |
186 | |
187 | // Test collective params build method, no attributes. |
188 | op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty}, |
189 | ValueRange{*cstI32}); |
190 | verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); |
191 | |
192 | // Test collective params build method no attributes, 2 inputs. |
193 | op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty}, |
194 | ValueRange{*cstI32, *cstF32}); |
195 | verifyOp(op, {i32Ty}, {*cstI32, *cstF32}, noAttrs); |
196 | |
197 | // Test collective params build method, non-empty attributes. |
198 | op = builder.create<test::TableGenBuildOp1>( |
199 | loc, TypeRange{i32Ty}, ValueRange{*cstI32, *cstF32}, attrs); |
200 | verifyOp(op, {i32Ty}, {*cstI32, *cstF32}, attrs); |
201 | } |
202 | |
203 | /// Test build methods for an Op with a single varadic arg and multiple variadic |
204 | /// result. |
205 | TEST_F(OpBuildGenTest, |
206 | BuildMethodsSingleVariadicArgAndMultipleVariadicResults) { |
207 | // Test separate arg, separate param build method. |
208 | auto op = builder.create<test::TableGenBuildOp3>( |
209 | loc, TypeRange{i32Ty}, TypeRange{f32Ty}, ValueRange{*cstI32}); |
210 | verifyOp(op, {i32Ty, f32Ty}, {*cstI32}, noAttrs); |
211 | |
212 | // Test collective params build method, no attributes. |
213 | op = builder.create<test::TableGenBuildOp3>(loc, TypeRange{i32Ty, f32Ty}, |
214 | ValueRange{*cstI32}); |
215 | verifyOp(op, {i32Ty, f32Ty}, {*cstI32}, noAttrs); |
216 | |
217 | // Test collective params build method, with attributes. |
218 | op = builder.create<test::TableGenBuildOp3>(loc, TypeRange{i32Ty, f32Ty}, |
219 | ValueRange{*cstI32}, attrs); |
220 | verifyOp(op, {i32Ty, f32Ty}, {*cstI32}, attrs); |
221 | } |
222 | |
223 | // The next test checks suppression of ambiguous build methods for ops that |
224 | // have a single variadic input, and single non-variadic result, and which |
225 | // support the SameOperandsAndResultType trait and optionally the |
226 | // InferOpTypeInterface interface. For such ops, the ODS framework generates |
227 | // build methods with no result types as they are inferred from the input types. |
228 | TEST_F(OpBuildGenTest, BuildMethodsSameOperandsAndResultTypeSuppression) { |
229 | // Test separate arg, separate param build method. |
230 | auto op = builder.create<test::TableGenBuildOp4>( |
231 | loc, i32Ty, ValueRange{*cstI32, *cstI32}); |
232 | verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, noAttrs); |
233 | |
234 | // Test collective params build method. |
235 | op = builder.create<test::TableGenBuildOp4>(loc, TypeRange{i32Ty}, |
236 | ValueRange{*cstI32, *cstI32}); |
237 | verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, noAttrs); |
238 | |
239 | // Test build method with no result types, default value of attributes. |
240 | op = |
241 | builder.create<test::TableGenBuildOp4>(loc, ValueRange{*cstI32, *cstI32}); |
242 | verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, noAttrs); |
243 | |
244 | // Test build method with no result types and supplied attributes. |
245 | op = builder.create<test::TableGenBuildOp4>(loc, ValueRange{*cstI32, *cstI32}, |
246 | attrs); |
247 | verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, attrs); |
248 | } |
249 | |
250 | TEST_F(OpBuildGenTest, BuildMethodsRegionsAndInferredType) { |
251 | auto op = builder.create<test::TableGenBuildOp5>( |
252 | loc, ValueRange{*cstI32, *cstF32}, /*attributes=*/noAttrs); |
253 | ASSERT_EQ(op->getNumRegions(), 1u); |
254 | verifyOp(op, {i32Ty}, {*cstI32, *cstF32}, noAttrs); |
255 | } |
256 | |
257 | TEST_F(OpBuildGenTest, BuildMethodsVariadicProperties) { |
258 | // Account for conversion as part of getAttrs(). |
259 | std::vector<NamedAttribute> ; |
260 | auto segmentSize = builder.getNamedAttr("operandSegmentSizes" , |
261 | builder.getDenseI32ArrayAttr({1, 1})); |
262 | noAttrsStorage.push_back(segmentSize); |
263 | ArrayRef<NamedAttribute> noAttrs(noAttrsStorage); |
264 | std::vector<NamedAttribute> = this->attrStorage; |
265 | attrsStorage.push_back(segmentSize); |
266 | ArrayRef<NamedAttribute> attrs(attrsStorage); |
267 | |
268 | // Test separate arg, separate param build method. |
269 | auto op = builder.create<test::TableGenBuildOp6>( |
270 | loc, f32Ty, ValueRange{*cstI32}, ValueRange{*cstI32}); |
271 | verifyOp(std::move(op), {f32Ty}, {*cstI32}, {*cstI32}, noAttrs); |
272 | |
273 | // Test build method with no result types, default value of attributes. |
274 | op = builder.create<test::TableGenBuildOp6>(loc, ValueRange{*cstI32}, |
275 | ValueRange{*cstI32}); |
276 | verifyOp(std::move(op), {f32Ty}, {*cstI32}, {*cstI32}, noAttrs); |
277 | |
278 | // Test collective params build method. |
279 | op = builder.create<test::TableGenBuildOp6>( |
280 | loc, TypeRange{f32Ty}, ValueRange{*cstI32}, ValueRange{*cstI32}); |
281 | verifyOp(std::move(op), {f32Ty}, {*cstI32}, {*cstI32}, noAttrs); |
282 | |
283 | // Test build method with result types, supplied attributes. |
284 | op = builder.create<test::TableGenBuildOp6>( |
285 | loc, TypeRange{f32Ty}, ValueRange{*cstI32, *cstI32}, attrs); |
286 | verifyOp(std::move(op), {f32Ty}, {*cstI32}, {*cstI32}, attrs); |
287 | |
288 | // Test build method with no result types and supplied attributes. |
289 | op = builder.create<test::TableGenBuildOp6>(loc, ValueRange{*cstI32, *cstI32}, |
290 | attrs); |
291 | verifyOp(std::move(op), {f32Ty}, {*cstI32}, {*cstI32}, attrs); |
292 | } |
293 | |
294 | TEST_F(OpBuildGenTest, BuildMethodsInherentDiscardableAttrs) { |
295 | test::TableGenBuildOp7::Properties props; |
296 | props.attr0 = cast<BoolAttr>(Val: attrs[0].getValue()); |
297 | ArrayRef<NamedAttribute> discardableAttrs = attrs.drop_front(); |
298 | auto op7 = builder.create<test::TableGenBuildOp7>( |
299 | loc, TypeRange{}, ValueRange{}, props, discardableAttrs); |
300 | verifyOp(op7, {}, {}, attrs); |
301 | |
302 | // Check that the old-style builder where all the attributes go in the same |
303 | // place works. |
304 | auto op7b = builder.create<test::TableGenBuildOp7>(loc, TypeRange{}, |
305 | ValueRange{}, attrs); |
306 | // Note: this goes before verifyOp() because verifyOp() calls erase(), causing |
307 | // use-after-free. |
308 | ASSERT_EQ(op7b.getProperties().getAttr0(), attrs[0].getValue()); |
309 | verifyOp(op7b, {}, {}, attrs); |
310 | } |
311 | |
312 | } // namespace mlir |
313 | |