1//===- OperationSupportTest.cpp - Operation support unit 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#include "mlir/IR/OperationSupport.h"
10#include "../../test/lib/Dialect/Test/TestDialect.h"
11#include "../../test/lib/Dialect/Test/TestOps.h"
12#include "mlir/IR/Builders.h"
13#include "mlir/IR/BuiltinTypes.h"
14#include "llvm/ADT/BitVector.h"
15#include "llvm/Support/FormatVariadic.h"
16#include "gtest/gtest.h"
17
18using namespace mlir;
19using namespace mlir::detail;
20
21static Operation *createOp(MLIRContext *context,
22 ArrayRef<Value> operands = std::nullopt,
23 ArrayRef<Type> resultTypes = std::nullopt,
24 unsigned int numRegions = 0) {
25 context->allowUnregisteredDialects();
26 return Operation::create(
27 UnknownLoc::get(context), OperationName("foo.bar", context), resultTypes,
28 operands, std::nullopt, nullptr, std::nullopt, numRegions);
29}
30
31namespace {
32TEST(OperandStorageTest, NonResizable) {
33 MLIRContext context;
34 Builder builder(&context);
35
36 Operation *useOp =
37 createOp(&context, /*operands=*/std::nullopt, builder.getIntegerType(16));
38 Value operand = useOp->getResult(idx: 0);
39
40 // Create a non-resizable operation with one operand.
41 Operation *user = createOp(context: &context, operands: operand);
42
43 // The same number of operands is okay.
44 user->setOperands(operand);
45 EXPECT_EQ(user->getNumOperands(), 1u);
46
47 // Removing is okay.
48 user->setOperands(std::nullopt);
49 EXPECT_EQ(user->getNumOperands(), 0u);
50
51 // Destroy the operations.
52 user->destroy();
53 useOp->destroy();
54}
55
56TEST(OperandStorageTest, Resizable) {
57 MLIRContext context;
58 Builder builder(&context);
59
60 Operation *useOp =
61 createOp(&context, /*operands=*/std::nullopt, builder.getIntegerType(16));
62 Value operand = useOp->getResult(idx: 0);
63
64 // Create a resizable operation with one operand.
65 Operation *user = createOp(context: &context, operands: operand);
66
67 // The same number of operands is okay.
68 user->setOperands(operand);
69 EXPECT_EQ(user->getNumOperands(), 1u);
70
71 // Removing is okay.
72 user->setOperands(std::nullopt);
73 EXPECT_EQ(user->getNumOperands(), 0u);
74
75 // Adding more operands is okay.
76 user->setOperands({operand, operand, operand});
77 EXPECT_EQ(user->getNumOperands(), 3u);
78
79 // Destroy the operations.
80 user->destroy();
81 useOp->destroy();
82}
83
84TEST(OperandStorageTest, RangeReplace) {
85 MLIRContext context;
86 Builder builder(&context);
87
88 Operation *useOp =
89 createOp(&context, /*operands=*/std::nullopt, builder.getIntegerType(16));
90 Value operand = useOp->getResult(idx: 0);
91
92 // Create a resizable operation with one operand.
93 Operation *user = createOp(context: &context, operands: operand);
94
95 // Check setting with the same number of operands.
96 user->setOperands(/*start=*/0, /*length=*/1, operands: operand);
97 EXPECT_EQ(user->getNumOperands(), 1u);
98
99 // Check setting with more operands.
100 user->setOperands(/*start=*/0, /*length=*/1, operands: {operand, operand, operand});
101 EXPECT_EQ(user->getNumOperands(), 3u);
102
103 // Check setting with less operands.
104 user->setOperands(/*start=*/1, /*length=*/2, operands: {operand});
105 EXPECT_EQ(user->getNumOperands(), 2u);
106
107 // Check inserting without replacing operands.
108 user->setOperands(/*start=*/2, /*length=*/0, operands: {operand});
109 EXPECT_EQ(user->getNumOperands(), 3u);
110
111 // Check erasing operands.
112 user->setOperands(/*start=*/0, /*length=*/3, operands: {});
113 EXPECT_EQ(user->getNumOperands(), 0u);
114
115 // Destroy the operations.
116 user->destroy();
117 useOp->destroy();
118}
119
120TEST(OperandStorageTest, MutableRange) {
121 MLIRContext context;
122 Builder builder(&context);
123
124 Operation *useOp =
125 createOp(&context, /*operands=*/std::nullopt, builder.getIntegerType(16));
126 Value operand = useOp->getResult(idx: 0);
127
128 // Create a resizable operation with one operand.
129 Operation *user = createOp(context: &context, operands: operand);
130
131 // Check setting with the same number of operands.
132 MutableOperandRange mutableOperands(user);
133 mutableOperands.assign(value: operand);
134 EXPECT_EQ(mutableOperands.size(), 1u);
135 EXPECT_EQ(user->getNumOperands(), 1u);
136
137 // Check setting with more operands.
138 mutableOperands.assign(values: {operand, operand, operand});
139 EXPECT_EQ(mutableOperands.size(), 3u);
140 EXPECT_EQ(user->getNumOperands(), 3u);
141
142 // Check with inserting a new operand.
143 mutableOperands.append(values: {operand, operand});
144 EXPECT_EQ(mutableOperands.size(), 5u);
145 EXPECT_EQ(user->getNumOperands(), 5u);
146
147 // Check erasing operands.
148 mutableOperands.clear();
149 EXPECT_EQ(mutableOperands.size(), 0u);
150 EXPECT_EQ(user->getNumOperands(), 0u);
151
152 // Destroy the operations.
153 user->destroy();
154 useOp->destroy();
155}
156
157TEST(OperandStorageTest, RangeErase) {
158 MLIRContext context;
159 Builder builder(&context);
160
161 Type type = builder.getNoneType();
162 Operation *useOp =
163 createOp(context: &context, /*operands=*/std::nullopt, resultTypes: {type, type});
164 Value operand1 = useOp->getResult(idx: 0);
165 Value operand2 = useOp->getResult(idx: 1);
166
167 // Create an operation with operands to erase.
168 Operation *user =
169 createOp(context: &context, operands: {operand2, operand1, operand2, operand1});
170 BitVector eraseIndices(user->getNumOperands());
171
172 // Check erasing no operands.
173 user->eraseOperands(eraseIndices);
174 EXPECT_EQ(user->getNumOperands(), 4u);
175
176 // Check erasing disjoint operands.
177 eraseIndices.set(0);
178 eraseIndices.set(3);
179 user->eraseOperands(eraseIndices);
180 EXPECT_EQ(user->getNumOperands(), 2u);
181 EXPECT_EQ(user->getOperand(0), operand1);
182 EXPECT_EQ(user->getOperand(1), operand2);
183
184 // Destroy the operations.
185 user->destroy();
186 useOp->destroy();
187}
188
189TEST(OperationOrderTest, OrderIsAlwaysValid) {
190 MLIRContext context;
191 Builder builder(&context);
192
193 Operation *containerOp = createOp(context: &context, /*operands=*/std::nullopt,
194 /*resultTypes=*/std::nullopt,
195 /*numRegions=*/1);
196 Region &region = containerOp->getRegion(index: 0);
197 Block *block = new Block();
198 region.push_back(block);
199
200 // Insert two operations, then iteratively add more operations in the middle
201 // of them. Eventually we will insert more than kOrderStride operations and
202 // the block order will need to be recomputed.
203 Operation *frontOp = createOp(context: &context);
204 Operation *backOp = createOp(context: &context);
205 block->push_back(op: frontOp);
206 block->push_back(op: backOp);
207
208 // Chosen to be larger than Operation::kOrderStride.
209 int kNumOpsToInsert = 10;
210 for (int i = 0; i < kNumOpsToInsert; ++i) {
211 Operation *op = createOp(context: &context);
212 block->getOperations().insert(backOp->getIterator(), op);
213 ASSERT_TRUE(op->isBeforeInBlock(backOp));
214 // Note verifyOpOrder() returns false if the order is valid.
215 ASSERT_FALSE(block->verifyOpOrder());
216 }
217
218 containerOp->destroy();
219}
220
221TEST(OperationFormatPrintTest, CanUseVariadicFormat) {
222 MLIRContext context;
223 Builder builder(&context);
224
225 Operation *op = createOp(context: &context);
226
227 std::string str = formatv(Fmt: "{0}", Vals&: *op).str();
228 ASSERT_STREQ(str.c_str(), "\"foo.bar\"() : () -> ()");
229
230 op->destroy();
231}
232
233TEST(OperationFormatPrintTest, CanPrintNameAsPrefix) {
234 MLIRContext context;
235 Builder builder(&context);
236
237 context.allowUnregisteredDialects();
238 Operation *op = Operation::create(
239 NameLoc::get(StringAttr::get(&context, "my_named_loc")),
240 OperationName("t.op", &context), builder.getIntegerType(16), std::nullopt,
241 std::nullopt, nullptr, std::nullopt, 0);
242
243 std::string str;
244 OpPrintingFlags flags;
245 flags.printNameLocAsPrefix(enable: true);
246 llvm::raw_string_ostream os(str);
247 op->print(os, flags);
248 ASSERT_STREQ(str.c_str(), "%my_named_loc = \"t.op\"() : () -> i16\n");
249
250 op->destroy();
251}
252
253TEST(NamedAttrListTest, TestAppendAssign) {
254 MLIRContext ctx;
255 NamedAttrList attrs;
256 Builder b(&ctx);
257
258 attrs.append(b.getStringAttr("foo"), b.getStringAttr("bar"));
259 attrs.append("baz", b.getStringAttr("boo"));
260
261 {
262 auto *it = attrs.begin();
263 EXPECT_EQ(it->getName(), b.getStringAttr("foo"));
264 EXPECT_EQ(it->getValue(), b.getStringAttr("bar"));
265 ++it;
266 EXPECT_EQ(it->getName(), b.getStringAttr("baz"));
267 EXPECT_EQ(it->getValue(), b.getStringAttr("boo"));
268 }
269
270 attrs.append("foo", b.getStringAttr("zoo"));
271 {
272 auto dup = attrs.findDuplicate();
273 ASSERT_TRUE(dup.has_value());
274 }
275
276 SmallVector<NamedAttribute> newAttrs = {
277 b.getNamedAttr("foo", b.getStringAttr("f")),
278 b.getNamedAttr("zoo", b.getStringAttr("z")),
279 };
280 attrs.assign(range: newAttrs);
281
282 auto dup = attrs.findDuplicate();
283 ASSERT_FALSE(dup.has_value());
284
285 {
286 auto *it = attrs.begin();
287 EXPECT_EQ(it->getName(), b.getStringAttr("foo"));
288 EXPECT_EQ(it->getValue(), b.getStringAttr("f"));
289 ++it;
290 EXPECT_EQ(it->getName(), b.getStringAttr("zoo"));
291 EXPECT_EQ(it->getValue(), b.getStringAttr("z"));
292 }
293
294 attrs.assign(range: {});
295 ASSERT_TRUE(attrs.empty());
296}
297
298TEST(OperandStorageTest, PopulateDefaultAttrs) {
299 MLIRContext context;
300 context.getOrLoadDialect<test::TestDialect>();
301 Builder builder(&context);
302
303 OpBuilder b(&context);
304 auto req1 = b.getI32IntegerAttr(10);
305 auto req2 = b.getI32IntegerAttr(60);
306 // Verify default attributes populated post op creation.
307 Operation *op = b.create<test::OpAttrMatch1>(b.getUnknownLoc(), req1, nullptr,
308 nullptr, req2);
309 auto opt = op->getInherentAttr(name: "default_valued_attr");
310 EXPECT_NE(opt, nullptr) << *op;
311
312 op->destroy();
313}
314
315TEST(OperationEquivalenceTest, HashWorksWithFlags) {
316 MLIRContext context;
317 context.getOrLoadDialect<test::TestDialect>();
318 OpBuilder b(&context);
319
320 auto *op1 = createOp(context: &context);
321 // `op1` has an unknown loc.
322 auto *op2 = createOp(context: &context);
323 op2->setLoc(NameLoc::get(StringAttr::get(&context, "foo")));
324 auto getHash = [](Operation *op, OperationEquivalence::Flags flags) {
325 return OperationEquivalence::computeHash(
326 op, hashOperands: OperationEquivalence::ignoreHashValue,
327 hashResults: OperationEquivalence::ignoreHashValue, flags);
328 };
329 // Check ignore location.
330 EXPECT_EQ(getHash(op1, OperationEquivalence::IgnoreLocations),
331 getHash(op2, OperationEquivalence::IgnoreLocations));
332 EXPECT_NE(getHash(op1, OperationEquivalence::None),
333 getHash(op2, OperationEquivalence::None));
334 op1->setLoc(NameLoc::get(StringAttr::get(&context, "foo")));
335 // Check ignore discardable dictionary attributes.
336 SmallVector<NamedAttribute> newAttrs = {
337 b.getNamedAttr("foo", b.getStringAttr("f"))};
338 op1->setAttrs(newAttrs);
339 EXPECT_EQ(getHash(op1, OperationEquivalence::IgnoreDiscardableAttrs),
340 getHash(op2, OperationEquivalence::IgnoreDiscardableAttrs));
341 EXPECT_NE(getHash(op1, OperationEquivalence::None),
342 getHash(op2, OperationEquivalence::None));
343 op1->destroy();
344 op2->destroy();
345
346 // Check ignore properties.
347 auto req1 = b.getI32IntegerAttr(10);
348 Operation *opWithProperty1 = b.create<test::OpAttrMatch1>(
349 b.getUnknownLoc(), req1, nullptr, nullptr, req1);
350 auto req2 = b.getI32IntegerAttr(60);
351 Operation *opWithProperty2 = b.create<test::OpAttrMatch1>(
352 b.getUnknownLoc(), req2, nullptr, nullptr, req2);
353 EXPECT_EQ(getHash(opWithProperty1, OperationEquivalence::IgnoreProperties),
354 getHash(opWithProperty2, OperationEquivalence::IgnoreProperties));
355 EXPECT_NE(getHash(opWithProperty1, OperationEquivalence::None),
356 getHash(opWithProperty2, OperationEquivalence::None));
357 opWithProperty1->destroy();
358 opWithProperty2->destroy();
359}
360
361} // namespace
362

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of mlir/unittests/IR/OperationSupportTest.cpp