312 lines
13 KiB
C++
312 lines
13 KiB
C++
//===- 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 <vector>
|
|
|
|
namespace mlir {
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Test Fixture
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static MLIRContext &getContext() {
|
|
static MLIRContext ctx;
|
|
ctx.getOrLoadDialect<test::TestDialect>();
|
|
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 <typename OpTy>
|
|
void verifyOp(OpTy &&concreteOp, std::vector<Type> resultTypes,
|
|
std::vector<Value> operands,
|
|
std::vector<NamedAttribute> 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<unsigned>(0U, attrs.size()))
|
|
EXPECT_EQ(op->getAttr(attrs[idx].getName().strref()),
|
|
attrs[idx].getValue());
|
|
|
|
EXPECT_TRUE(mlir::succeeded(concreteOp.verify()));
|
|
concreteOp.erase();
|
|
}
|
|
|
|
template <typename OpTy>
|
|
void verifyOp(OpTy &&concreteOp, std::vector<Type> resultTypes,
|
|
std::vector<Value> operands1, std::vector<Value> operands2,
|
|
std::vector<NamedAttribute> 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<Value>(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<unsigned>(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<test::TableGenConstant> cstI32;
|
|
OwningOpRef<test::TableGenConstant> cstF32;
|
|
|
|
ArrayRef<NamedAttribute> noAttrs;
|
|
std::vector<NamedAttribute> attrStorage;
|
|
ArrayRef<NamedAttribute> 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<NamedAttribute> noAttrsStorage;
|
|
auto segmentSize = builder.getNamedAttr("operandSegmentSizes",
|
|
builder.getDenseI32ArrayAttr({1, 1}));
|
|
noAttrsStorage.push_back(segmentSize);
|
|
ArrayRef<NamedAttribute> noAttrs(noAttrsStorage);
|
|
std::vector<NamedAttribute> attrsStorage = this->attrStorage;
|
|
attrsStorage.push_back(segmentSize);
|
|
ArrayRef<NamedAttribute> 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<BoolAttr>(attrs[0].getValue());
|
|
ArrayRef<NamedAttribute> 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
|