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 "mlir/IR/Attributes.h" |
15 | #include "mlir/IR/Builders.h" |
16 | #include "mlir/IR/BuiltinTypes.h" |
17 | #include "mlir/IR/Dialect.h" |
18 | #include "gmock/gmock.h" |
19 | #include <vector> |
20 | |
21 | namespace mlir { |
22 | |
23 | //===----------------------------------------------------------------------===// |
24 | // Test Fixture |
25 | //===----------------------------------------------------------------------===// |
26 | |
27 | static MLIRContext &getContext() { |
28 | static MLIRContext ctx; |
29 | ctx.getOrLoadDialect<test::TestDialect>(); |
30 | return ctx; |
31 | } |
32 | /// Test fixture for providing basic utilities for testing. |
33 | class OpBuildGenTest : public ::testing::Test { |
34 | protected: |
35 | OpBuildGenTest() |
36 | : ctx(getContext()), builder(&ctx), loc(builder.getUnknownLoc()), |
37 | i32Ty(builder.getI32Type()), f32Ty(builder.getF32Type()), |
38 | cstI32(builder.create<test::TableGenConstant>(loc, i32Ty)), |
39 | cstF32(builder.create<test::TableGenConstant>(loc, f32Ty)), |
40 | noAttrs(), attrStorage{builder.getNamedAttr(name: "attr0" , |
41 | val: builder.getBoolAttr(value: true)), |
42 | builder.getNamedAttr( |
43 | "attr1" , builder.getI32IntegerAttr(33))}, |
44 | attrs(attrStorage) {} |
45 | |
46 | // Verify that `op` has the given set of result types, operands, and |
47 | // attributes. |
48 | template <typename OpTy> |
49 | void verifyOp(OpTy &&concreteOp, std::vector<Type> resultTypes, |
50 | std::vector<Value> operands, |
51 | std::vector<NamedAttribute> attrs) { |
52 | ASSERT_NE(concreteOp, nullptr); |
53 | Operation *op = concreteOp.getOperation(); |
54 | |
55 | EXPECT_EQ(op->getNumResults(), resultTypes.size()); |
56 | for (unsigned idx : llvm::seq(Begin: 0U, End: op->getNumResults())) |
57 | EXPECT_EQ(op->getResult(idx).getType(), resultTypes[idx]); |
58 | |
59 | EXPECT_EQ(op->getNumOperands(), operands.size()); |
60 | for (unsigned idx : llvm::seq(Begin: 0U, End: op->getNumOperands())) |
61 | EXPECT_EQ(op->getOperand(idx), operands[idx]); |
62 | |
63 | EXPECT_EQ(op->getAttrs().size(), attrs.size()); |
64 | for (unsigned idx : llvm::seq<unsigned>(Begin: 0U, End: attrs.size())) |
65 | EXPECT_EQ(op->getAttr(attrs[idx].getName().strref()), |
66 | attrs[idx].getValue()); |
67 | |
68 | concreteOp.erase(); |
69 | } |
70 | |
71 | // Helper method to test ops with inferred result types and single variadic |
72 | // input. |
73 | template <typename OpTy> |
74 | void testSingleVariadicInputInferredType() { |
75 | // Test separate arg, separate param build method. |
76 | auto op = builder.create<OpTy>(loc, i32Ty, ValueRange{*cstI32, *cstI32}); |
77 | verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, noAttrs); |
78 | |
79 | // Test collective params build method. |
80 | op = builder.create<OpTy>(loc, TypeRange{i32Ty}, |
81 | ValueRange{*cstI32, *cstI32}); |
82 | verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, noAttrs); |
83 | |
84 | // Test build method with no result types, default value of attributes. |
85 | op = builder.create<OpTy>(loc, ValueRange{*cstI32, *cstI32}); |
86 | verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, noAttrs); |
87 | |
88 | // Test build method with no result types and supplied attributes. |
89 | op = builder.create<OpTy>(loc, ValueRange{*cstI32, *cstI32}, attrs); |
90 | verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, attrs); |
91 | } |
92 | |
93 | protected: |
94 | MLIRContext &ctx; |
95 | OpBuilder builder; |
96 | Location loc; |
97 | Type i32Ty; |
98 | Type f32Ty; |
99 | OwningOpRef<test::TableGenConstant> cstI32; |
100 | OwningOpRef<test::TableGenConstant> cstF32; |
101 | |
102 | ArrayRef<NamedAttribute> noAttrs; |
103 | std::vector<NamedAttribute> attrStorage; |
104 | ArrayRef<NamedAttribute> attrs; |
105 | }; |
106 | |
107 | /// Test basic build methods. |
108 | TEST_F(OpBuildGenTest, BasicBuildMethods) { |
109 | // Test separate args, separate results build method. |
110 | auto op = builder.create<test::TableGenBuildOp0>(loc, i32Ty, *cstI32); |
111 | verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); |
112 | |
113 | // Test separate args, collective results build method. |
114 | op = builder.create<test::TableGenBuildOp0>(loc, TypeRange{i32Ty}, *cstI32); |
115 | verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); |
116 | |
117 | // Test collective args, collective params build method. |
118 | op = builder.create<test::TableGenBuildOp0>(loc, TypeRange{i32Ty}, |
119 | ValueRange{*cstI32}); |
120 | verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); |
121 | |
122 | // Test collective args, collective results, non-empty attributes |
123 | op = builder.create<test::TableGenBuildOp0>(loc, TypeRange{i32Ty}, |
124 | ValueRange{*cstI32}, attrs); |
125 | verifyOp(op, {i32Ty}, {*cstI32}, attrs); |
126 | } |
127 | |
128 | /// The following 3 tests exercise build methods generated for operations |
129 | /// with a combination of: |
130 | /// |
131 | /// single variadic arg x |
132 | /// {single variadic result, non-variadic result, multiple variadic results} |
133 | /// |
134 | /// Specifically to test that ODS framework does not generate ambiguous |
135 | /// build() methods that fail to compile. |
136 | |
137 | /// Test build methods for an Op with a single varadic arg and a single |
138 | /// variadic result. |
139 | TEST_F(OpBuildGenTest, BuildMethodsSingleVariadicArgAndResult) { |
140 | // Test collective args, collective results method, building a unary op. |
141 | auto op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty}, |
142 | ValueRange{*cstI32}); |
143 | verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); |
144 | |
145 | // Test collective args, collective results method, building a unary op with |
146 | // named attributes. |
147 | op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty}, |
148 | ValueRange{*cstI32}, attrs); |
149 | verifyOp(op, {i32Ty}, {*cstI32}, attrs); |
150 | |
151 | // Test collective args, collective results method, building a binary op. |
152 | op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty, f32Ty}, |
153 | ValueRange{*cstI32, *cstF32}); |
154 | verifyOp(op, {i32Ty, f32Ty}, {*cstI32, *cstF32}, noAttrs); |
155 | |
156 | // Test collective args, collective results method, building a binary op with |
157 | // named attributes. |
158 | op = builder.create<test::TableGenBuildOp1>( |
159 | loc, TypeRange{i32Ty, f32Ty}, ValueRange{*cstI32, *cstF32}, attrs); |
160 | verifyOp(op, {i32Ty, f32Ty}, {*cstI32, *cstF32}, attrs); |
161 | } |
162 | |
163 | /// Test build methods for an Op with a single varadic arg and a non-variadic |
164 | /// result. |
165 | TEST_F(OpBuildGenTest, BuildMethodsSingleVariadicArgNonVariadicResults) { |
166 | // Test separate arg, separate param build method. |
167 | auto op = |
168 | builder.create<test::TableGenBuildOp1>(loc, i32Ty, ValueRange{*cstI32}); |
169 | verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); |
170 | |
171 | // Test collective params build method, no attributes. |
172 | op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty}, |
173 | ValueRange{*cstI32}); |
174 | verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); |
175 | |
176 | // Test collective params build method no attributes, 2 inputs. |
177 | op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty}, |
178 | ValueRange{*cstI32, *cstF32}); |
179 | verifyOp(op, {i32Ty}, {*cstI32, *cstF32}, noAttrs); |
180 | |
181 | // Test collective params build method, non-empty attributes. |
182 | op = builder.create<test::TableGenBuildOp1>( |
183 | loc, TypeRange{i32Ty}, ValueRange{*cstI32, *cstF32}, attrs); |
184 | verifyOp(op, {i32Ty}, {*cstI32, *cstF32}, attrs); |
185 | } |
186 | |
187 | /// Test build methods for an Op with a single varadic arg and multiple variadic |
188 | /// result. |
189 | TEST_F(OpBuildGenTest, |
190 | BuildMethodsSingleVariadicArgAndMultipleVariadicResults) { |
191 | // Test separate arg, separate param build method. |
192 | auto op = builder.create<test::TableGenBuildOp3>( |
193 | loc, TypeRange{i32Ty}, TypeRange{f32Ty}, ValueRange{*cstI32}); |
194 | verifyOp(op, {i32Ty, f32Ty}, {*cstI32}, noAttrs); |
195 | |
196 | // Test collective params build method, no attributes. |
197 | op = builder.create<test::TableGenBuildOp3>(loc, TypeRange{i32Ty, f32Ty}, |
198 | ValueRange{*cstI32}); |
199 | verifyOp(op, {i32Ty, f32Ty}, {*cstI32}, noAttrs); |
200 | |
201 | // Test collective params build method, with attributes. |
202 | op = builder.create<test::TableGenBuildOp3>(loc, TypeRange{i32Ty, f32Ty}, |
203 | ValueRange{*cstI32}, attrs); |
204 | verifyOp(op, {i32Ty, f32Ty}, {*cstI32}, attrs); |
205 | } |
206 | |
207 | // The next test checks supression of ambiguous build methods for ops that |
208 | // have a single variadic input, and single non-variadic result, and which |
209 | // support the SameOperandsAndResultType trait and and optionally the |
210 | // InferOpTypeInterface interface. For such ops, the ODS framework generates |
211 | // build methods with no result types as they are inferred from the input types. |
212 | TEST_F(OpBuildGenTest, BuildMethodsSameOperandsAndResultTypeSuppression) { |
213 | testSingleVariadicInputInferredType<test::TableGenBuildOp4>(); |
214 | } |
215 | |
216 | TEST_F(OpBuildGenTest, BuildMethodsRegionsAndInferredType) { |
217 | auto op = builder.create<test::TableGenBuildOp5>( |
218 | loc, ValueRange{*cstI32, *cstF32}, /*attributes=*/noAttrs); |
219 | ASSERT_EQ(op->getNumRegions(), 1u); |
220 | verifyOp(op, {i32Ty}, {*cstI32, *cstF32}, noAttrs); |
221 | } |
222 | |
223 | } // namespace mlir |
224 | |