//===- OpBuildGen.cpp - TableGen OpBuildGen Tests -------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Test TableGen generated build() methods on Operations. // //===----------------------------------------------------------------------===// #include "TestDialect.h" #include "TestOps.h" #include "mlir/IR/Attributes.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/Dialect.h" #include "gmock/gmock.h" #include namespace mlir { //===----------------------------------------------------------------------===// // Test Fixture //===----------------------------------------------------------------------===// static MLIRContext &getContext() { static MLIRContext ctx; ctx.getOrLoadDialect(); return ctx; } /// Test fixture for providing basic utilities for testing. class OpBuildGenTest : public ::testing::Test { protected: OpBuildGenTest() : ctx(getContext()), builder(&ctx), loc(builder.getUnknownLoc()), i32Ty(builder.getI32Type()), f32Ty(builder.getF32Type()), cstI32(test::TableGenConstant::create(builder, loc, i32Ty)), cstF32(test::TableGenConstant::create(builder, loc, f32Ty)), noAttrs(), attrStorage{ builder.getNamedAttr("attr0", builder.getBoolAttr(true)), builder.getNamedAttr("attr1", builder.getI32IntegerAttr(33))}, attrs(attrStorage) {} // Verify that `op` has the given set of result types, operands, and // attributes. template void verifyOp(OpTy &&concreteOp, std::vector resultTypes, std::vector operands, std::vector attrs) { ASSERT_NE(concreteOp, nullptr); Operation *op = concreteOp.getOperation(); EXPECT_EQ(op->getNumResults(), resultTypes.size()); for (unsigned idx : llvm::seq(0U, op->getNumResults())) EXPECT_EQ(op->getResult(idx).getType(), resultTypes[idx]); EXPECT_EQ(op->getNumOperands(), operands.size()); for (unsigned idx : llvm::seq(0U, op->getNumOperands())) EXPECT_EQ(op->getOperand(idx), operands[idx]); EXPECT_EQ(op->getAttrs().size(), attrs.size()); for (unsigned idx : llvm::seq(0U, attrs.size())) EXPECT_EQ(op->getAttr(attrs[idx].getName().strref()), attrs[idx].getValue()); EXPECT_TRUE(mlir::succeeded(concreteOp.verify())); concreteOp.erase(); } template void verifyOp(OpTy &&concreteOp, std::vector resultTypes, std::vector operands1, std::vector operands2, std::vector attrs) { ASSERT_NE(concreteOp, nullptr); Operation *op = concreteOp.getOperation(); EXPECT_EQ(op->getNumResults(), resultTypes.size()); for (unsigned idx : llvm::seq(0U, op->getNumResults())) EXPECT_EQ(op->getResult(idx).getType(), resultTypes[idx]); auto operands = llvm::to_vector(llvm::concat(operands1, operands2)); EXPECT_EQ(op->getNumOperands(), operands.size()); for (unsigned idx : llvm::seq(0U, op->getNumOperands())) EXPECT_EQ(op->getOperand(idx), operands[idx]); EXPECT_EQ(op->getAttrs().size(), attrs.size()); if (op->getAttrs().size() != attrs.size()) { // Simple export where there is mismatch count. llvm::errs() << "Op attrs:\n"; for (auto it : op->getAttrs()) llvm::errs() << "\t" << it.getName() << " = " << it.getValue() << "\n"; llvm::errs() << "Expected attrs:\n"; for (auto it : attrs) llvm::errs() << "\t" << it.getName() << " = " << it.getValue() << "\n"; } else { for (unsigned idx : llvm::seq(0U, attrs.size())) EXPECT_EQ(op->getAttr(attrs[idx].getName().strref()), attrs[idx].getValue()); } EXPECT_TRUE(mlir::succeeded(concreteOp.verify())); concreteOp.erase(); } protected: MLIRContext &ctx; OpBuilder builder; Location loc; Type i32Ty; Type f32Ty; OwningOpRef cstI32; OwningOpRef cstF32; ArrayRef noAttrs; std::vector attrStorage; ArrayRef attrs; }; /// Test basic build methods. TEST_F(OpBuildGenTest, BasicBuildMethods) { // Test separate args, separate results build method. auto op = test::TableGenBuildOp0::create(builder, loc, i32Ty, *cstI32); verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); // Test separate args, collective results build method. op = test::TableGenBuildOp0::create(builder, loc, TypeRange{i32Ty}, *cstI32); verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); // Test collective args, collective params build method. op = test::TableGenBuildOp0::create(builder, loc, TypeRange{i32Ty}, ValueRange{*cstI32}); verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); // Test collective args, collective results, non-empty attributes op = test::TableGenBuildOp0::create(builder, loc, TypeRange{i32Ty}, ValueRange{*cstI32}, attrs); verifyOp(op, {i32Ty}, {*cstI32}, attrs); } /// The following 3 tests exercise build methods generated for operations /// with a combination of: /// /// single variadic arg x /// {single variadic result, non-variadic result, multiple variadic results} /// /// Specifically to test that ODS framework does not generate ambiguous /// build() methods that fail to compile. /// Test build methods for an Op with a single varadic arg and a single /// variadic result. TEST_F(OpBuildGenTest, BuildMethodsSingleVariadicArgAndResult) { // Test collective args, collective results method, building a unary op. auto op = test::TableGenBuildOp1::create(builder, loc, TypeRange{i32Ty}, ValueRange{*cstI32}); verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); // Test collective args, collective results method, building a unary op with // named attributes. op = test::TableGenBuildOp1::create(builder, loc, TypeRange{i32Ty}, ValueRange{*cstI32}, attrs); verifyOp(op, {i32Ty}, {*cstI32}, attrs); // Test collective args, collective results method, building a binary op. op = test::TableGenBuildOp1::create(builder, loc, TypeRange{i32Ty, f32Ty}, ValueRange{*cstI32, *cstF32}); verifyOp(op, {i32Ty, f32Ty}, {*cstI32, *cstF32}, noAttrs); // Test collective args, collective results method, building a binary op with // named attributes. op = test::TableGenBuildOp1::create(builder, loc, TypeRange{i32Ty, f32Ty}, ValueRange{*cstI32, *cstF32}, attrs); verifyOp(op, {i32Ty, f32Ty}, {*cstI32, *cstF32}, attrs); } /// Test build methods for an Op with a single varadic arg and a non-variadic /// result. TEST_F(OpBuildGenTest, BuildMethodsSingleVariadicArgNonVariadicResults) { // Test separate arg, separate param build method. auto op = test::TableGenBuildOp1::create(builder, loc, i32Ty, ValueRange{*cstI32}); verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); // Test collective params build method, no attributes. op = test::TableGenBuildOp1::create(builder, loc, TypeRange{i32Ty}, ValueRange{*cstI32}); verifyOp(op, {i32Ty}, {*cstI32}, noAttrs); // Test collective params build method no attributes, 2 inputs. op = test::TableGenBuildOp1::create(builder, loc, TypeRange{i32Ty}, ValueRange{*cstI32, *cstF32}); verifyOp(op, {i32Ty}, {*cstI32, *cstF32}, noAttrs); // Test collective params build method, non-empty attributes. op = test::TableGenBuildOp1::create(builder, loc, TypeRange{i32Ty}, ValueRange{*cstI32, *cstF32}, attrs); verifyOp(op, {i32Ty}, {*cstI32, *cstF32}, attrs); } /// Test build methods for an Op with a single varadic arg and multiple variadic /// result. TEST_F(OpBuildGenTest, BuildMethodsSingleVariadicArgAndMultipleVariadicResults) { // Test separate arg, separate param build method. auto op = test::TableGenBuildOp3::create( builder, loc, TypeRange{i32Ty}, TypeRange{f32Ty}, ValueRange{*cstI32}); verifyOp(op, {i32Ty, f32Ty}, {*cstI32}, noAttrs); // Test collective params build method, no attributes. op = test::TableGenBuildOp3::create(builder, loc, TypeRange{i32Ty, f32Ty}, ValueRange{*cstI32}); verifyOp(op, {i32Ty, f32Ty}, {*cstI32}, noAttrs); // Test collective params build method, with attributes. op = test::TableGenBuildOp3::create(builder, loc, TypeRange{i32Ty, f32Ty}, ValueRange{*cstI32}, attrs); verifyOp(op, {i32Ty, f32Ty}, {*cstI32}, attrs); } // The next test checks suppression of ambiguous build methods for ops that // have a single variadic input, and single non-variadic result, and which // support the SameOperandsAndResultType trait and optionally the // InferOpTypeInterface interface. For such ops, the ODS framework generates // build methods with no result types as they are inferred from the input types. TEST_F(OpBuildGenTest, BuildMethodsSameOperandsAndResultTypeSuppression) { // Test separate arg, separate param build method. auto op = test::TableGenBuildOp4::create(builder, loc, i32Ty, ValueRange{*cstI32, *cstI32}); verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, noAttrs); // Test collective params build method. op = test::TableGenBuildOp4::create(builder, loc, TypeRange{i32Ty}, ValueRange{*cstI32, *cstI32}); verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, noAttrs); // Test build method with no result types, default value of attributes. op = test::TableGenBuildOp4::create(builder, loc, ValueRange{*cstI32, *cstI32}); verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, noAttrs); // Test build method with no result types and supplied attributes. op = test::TableGenBuildOp4::create(builder, loc, ValueRange{*cstI32, *cstI32}, attrs); verifyOp(std::move(op), {i32Ty}, {*cstI32, *cstI32}, attrs); } TEST_F(OpBuildGenTest, BuildMethodsRegionsAndInferredType) { auto op = test::TableGenBuildOp5::create( builder, loc, ValueRange{*cstI32, *cstF32}, /*attributes=*/noAttrs); ASSERT_EQ(op->getNumRegions(), 1u); verifyOp(op, {i32Ty}, {*cstI32, *cstF32}, noAttrs); } TEST_F(OpBuildGenTest, BuildMethodsVariadicProperties) { // Account for conversion as part of getAttrs(). std::vector noAttrsStorage; auto segmentSize = builder.getNamedAttr("operandSegmentSizes", builder.getDenseI32ArrayAttr({1, 1})); noAttrsStorage.push_back(segmentSize); ArrayRef noAttrs(noAttrsStorage); std::vector attrsStorage = this->attrStorage; attrsStorage.push_back(segmentSize); ArrayRef attrs(attrsStorage); // Test separate arg, separate param build method. auto op = test::TableGenBuildOp6::create( builder, loc, f32Ty, ValueRange{*cstI32}, ValueRange{*cstI32}); verifyOp(std::move(op), {f32Ty}, {*cstI32}, {*cstI32}, noAttrs); // Test build method with no result types, default value of attributes. op = test::TableGenBuildOp6::create(builder, loc, ValueRange{*cstI32}, ValueRange{*cstI32}); verifyOp(std::move(op), {f32Ty}, {*cstI32}, {*cstI32}, noAttrs); // Test collective params build method. op = test::TableGenBuildOp6::create(builder, loc, TypeRange{f32Ty}, ValueRange{*cstI32}, ValueRange{*cstI32}); verifyOp(std::move(op), {f32Ty}, {*cstI32}, {*cstI32}, noAttrs); // Test build method with result types, supplied attributes. op = test::TableGenBuildOp6::create(builder, loc, TypeRange{f32Ty}, ValueRange{*cstI32, *cstI32}, attrs); verifyOp(std::move(op), {f32Ty}, {*cstI32}, {*cstI32}, attrs); // Test build method with no result types and supplied attributes. op = test::TableGenBuildOp6::create(builder, loc, ValueRange{*cstI32, *cstI32}, attrs); verifyOp(std::move(op), {f32Ty}, {*cstI32}, {*cstI32}, attrs); } TEST_F(OpBuildGenTest, BuildMethodsInherentDiscardableAttrs) { test::TableGenBuildOp7::Properties props; props.attr0 = cast(attrs[0].getValue()); ArrayRef discardableAttrs = attrs.drop_front(); auto op7 = test::TableGenBuildOp7::create( builder, loc, TypeRange{}, ValueRange{}, props, discardableAttrs); verifyOp(op7, {}, {}, attrs); // Check that the old-style builder where all the attributes go in the same // place works. auto op7b = test::TableGenBuildOp7::create(builder, loc, TypeRange{}, ValueRange{}, attrs); // Note: this goes before verifyOp() because verifyOp() calls erase(), causing // use-after-free. ASSERT_EQ(op7b.getProperties().getAttr0(), attrs[0].getValue()); verifyOp(op7b, {}, {}, attrs); } } // namespace mlir