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("attr0",
41                                                     builder.getBoolAttr(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(0U, 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(0U, 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>(0U, attrs.size()))
65       EXPECT_EQ(op->getAttr(attrs[idx].first.strref()), attrs[idx].second);
66 
67     concreteOp.erase();
68   }
69 
70   // Helper method to test ops with inferred result types and single variadic
71   // input.
72   template <typename OpTy>
73   void testSingleVariadicInputInferredType() {
74     // Test separate arg, separate param build method.
75     auto op = builder.create<OpTy>(loc, i32Ty, ValueRange{*cstI32, *cstI32});
76     verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, noAttrs);
77 
78     // Test collective params build method.
79     op =
80         builder.create<OpTy>(loc, TypeRange{i32Ty}, ValueRange{*cstI32, *cstI32});
81     verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, noAttrs);
82 
83     // Test build method with no result types, default value of attributes.
84     op = builder.create<OpTy>(loc, ValueRange{*cstI32, *cstI32});
85     verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, noAttrs);
86 
87     // Test build method with no result types and supplied attributes.
88     op = builder.create<OpTy>(loc, ValueRange{*cstI32, *cstI32}, attrs);
89     verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, attrs);
90   }
91 
92 protected:
93   MLIRContext &ctx;
94   OpBuilder builder;
95   Location loc;
96   Type i32Ty;
97   Type f32Ty;
98   OwningOpRef<test::TableGenConstant> cstI32;
99   OwningOpRef<test::TableGenConstant> cstF32;
100 
101   ArrayRef<NamedAttribute> noAttrs;
102   std::vector<NamedAttribute> attrStorage;
103   ArrayRef<NamedAttribute> attrs;
104 };
105 
106 /// Test basic build methods.
107 TEST_F(OpBuildGenTest, BasicBuildMethods) {
108   // Test separate args, separate results build method.
109   auto op = builder.create<test::TableGenBuildOp0>(loc, i32Ty, *cstI32);
110   verifyOp(op, {i32Ty}, {*cstI32}, noAttrs);
111 
112   // Test separate args, collective results build method.
113   op = builder.create<test::TableGenBuildOp0>(loc, TypeRange{i32Ty}, *cstI32);
114   verifyOp(op, {i32Ty}, {*cstI32}, noAttrs);
115 
116   // Test collective args, collective params build method.
117   op = builder.create<test::TableGenBuildOp0>(loc, TypeRange{i32Ty},
118                                               ValueRange{*cstI32});
119   verifyOp(op, {i32Ty}, {*cstI32}, noAttrs);
120 
121   // Test collective args, collective results, non-empty attributes
122   op = builder.create<test::TableGenBuildOp0>(loc, TypeRange{i32Ty},
123                                               ValueRange{*cstI32}, attrs);
124   verifyOp(op, {i32Ty}, {*cstI32}, attrs);
125 }
126 
127 /// The following 3 tests exercise build methods generated for operations
128 /// with a combination of:
129 ///
130 /// single variadic arg x
131 /// {single variadic result, non-variadic result, multiple variadic results}
132 ///
133 /// Specifically to test that that ODS framework does not generate ambiguous
134 /// build() methods that fail to compile.
135 
136 /// Test build methods for an Op with a single varadic arg and a single
137 /// variadic result.
138 TEST_F(OpBuildGenTest, BuildMethodsSingleVariadicArgAndResult) {
139   // Test collective args, collective results method, building a unary op.
140   auto op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty},
141                                                    ValueRange{*cstI32});
142   verifyOp(std::move(op), {i32Ty}, {*cstI32}, noAttrs);
143 
144   // Test collective args, collective results method, building a unary op with
145   // named attributes.
146   op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty},
147                                               ValueRange{*cstI32}, attrs);
148   verifyOp(std::move(op), {i32Ty}, {*cstI32}, attrs);
149 
150   // Test collective args, collective results method, building a binary op.
151   op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty, f32Ty},
152                                               ValueRange{*cstI32, *cstF32});
153   verifyOp(std::move(op), {i32Ty, f32Ty}, {*cstI32, *cstF32}, noAttrs);
154 
155   // Test collective args, collective results method, building a binary op with
156   // named attributes.
157   op = builder.create<test::TableGenBuildOp1>(
158       loc, TypeRange{i32Ty, f32Ty}, ValueRange{*cstI32, *cstF32}, attrs);
159   verifyOp(std::move(op), {i32Ty, f32Ty}, {*cstI32, *cstF32}, attrs);
160 }
161 
162 /// Test build methods for an Op with a single varadic arg and a non-variadic
163 /// result.
164 TEST_F(OpBuildGenTest, BuildMethodsSingleVariadicArgNonVariadicResults) {
165   // Test separate arg, separate param build method.
166   auto op =
167       builder.create<test::TableGenBuildOp1>(loc, i32Ty, ValueRange{*cstI32});
168   verifyOp(std::move(op), {i32Ty}, {*cstI32}, noAttrs);
169 
170   // Test collective params build method, no attributes.
171   op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty},
172                                               ValueRange{*cstI32});
173   verifyOp(std::move(op), {i32Ty}, {*cstI32}, noAttrs);
174 
175   // Test collective params build method no attributes, 2 inputs.
176   op = builder.create<test::TableGenBuildOp1>(loc, TypeRange{i32Ty},
177                                               ValueRange{*cstI32, *cstF32});
178   verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstF32}, noAttrs);
179 
180   // Test collective params build method, non-empty attributes.
181   op = builder.create<test::TableGenBuildOp1>(
182       loc, TypeRange{i32Ty}, ValueRange{*cstI32, *cstF32}, attrs);
183   verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstF32}, attrs);
184 }
185 
186 /// Test build methods for an Op with a single varadic arg and multiple variadic
187 /// result.
188 TEST_F(OpBuildGenTest,
189        BuildMethodsSingleVariadicArgAndMultipleVariadicResults) {
190   // Test separate arg, separate param build method.
191   auto op = builder.create<test::TableGenBuildOp3>(
192       loc, TypeRange{i32Ty}, TypeRange{f32Ty}, ValueRange{*cstI32});
193   verifyOp(std::move(op), {i32Ty, f32Ty}, {*cstI32}, noAttrs);
194 
195   // Test collective params build method, no attributes.
196   op = builder.create<test::TableGenBuildOp3>(loc, TypeRange{i32Ty, f32Ty},
197                                               ValueRange{*cstI32});
198   verifyOp(std::move(op), {i32Ty, f32Ty}, {*cstI32}, noAttrs);
199 
200   // Test collective params build method, with attributes.
201   op = builder.create<test::TableGenBuildOp3>(loc, TypeRange{i32Ty, f32Ty},
202                                               ValueRange{*cstI32}, attrs);
203   verifyOp(std::move(op), {i32Ty, f32Ty}, {*cstI32}, attrs);
204 }
205 
206 // The next 2 tests test supression of ambiguous build methods for ops that
207 // have a single variadic input, and single non-variadic result, and which
208 // support the SameOperandsAndResultType trait and and optionally the
209 // InferOpTypeInterface interface. For such ops, the ODS framework generates
210 // build methods with no result types as they are inferred from the input types.
211 TEST_F(OpBuildGenTest, BuildMethodsSameOperandsAndResultTypeSuppression) {
212   testSingleVariadicInputInferredType<test::TableGenBuildOp4>();
213 }
214 
215 TEST_F(
216     OpBuildGenTest,
217     BuildMethodsSameOperandsAndResultTypeAndInferOpTypeInterfaceSuppression) {
218   testSingleVariadicInputInferredType<test::TableGenBuildOp5>();
219 }
220 
221 } // namespace mlir
222