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