#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/BuiltinDialect.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/IR/BuiltinTypes.h"
#include "gtest/gtest.h"
#include "../../test/lib/Dialect/Test/TestAttributes.h"
#include "../../test/lib/Dialect/Test/TestDialect.h"
#include "../../test/lib/Dialect/Test/TestOps.h"
#include "../../test/lib/Dialect/Test/TestTypes.h"
#include "mlir/IR/OwningOpRef.h"
using namespace mlir;
using namespace test;
namespace {
struct Model
: public TestExternalTypeInterface::ExternalModel<Model, IntegerType> {
unsigned getBitwidthPlusArg(Type type, unsigned arg) const {
return type.getIntOrFloatBitWidth() + arg;
}
static unsigned staticGetSomeValuePlusArg(unsigned arg) { return 42 + arg; }
};
struct OverridingModel
: public TestExternalTypeInterface::ExternalModel<OverridingModel,
FloatType> {
unsigned getBitwidthPlusArg(Type type, unsigned arg) const {
return type.getIntOrFloatBitWidth() + arg;
}
static unsigned staticGetSomeValuePlusArg(unsigned arg) { return 42 + arg; }
unsigned getBitwidthPlusDoubleArgument(Type type, unsigned arg) const {
return 128;
}
static unsigned staticGetArgument(unsigned arg) { return 420; }
};
TEST(InterfaceAttachment, Type) {
MLIRContext context;
IntegerType i8 = IntegerType::get(&context, 8);
ASSERT_FALSE(isa<TestExternalTypeInterface>(i8));
IntegerType::attachInterface<Model>(context);
TestExternalTypeInterface iface = dyn_cast<TestExternalTypeInterface>(i8);
ASSERT_TRUE(iface != nullptr);
EXPECT_EQ(iface.getBitwidthPlusArg(10), 18u);
EXPECT_EQ(iface.staticGetSomeValuePlusArg(0), 42u);
EXPECT_EQ(iface.getBitwidthPlusDoubleArgument(2), 12u);
EXPECT_EQ(iface.staticGetArgument(17), 17u);
FloatType flt = Float32Type::get(&context);
ASSERT_FALSE(isa<TestExternalTypeInterface>(flt));
Float32Type::attachInterface<OverridingModel>(context);
iface = dyn_cast<TestExternalTypeInterface>(flt);
ASSERT_TRUE(iface != nullptr);
EXPECT_EQ(iface.getBitwidthPlusArg(10), 42u);
EXPECT_EQ(iface.staticGetSomeValuePlusArg(10), 52u);
EXPECT_EQ(iface.getBitwidthPlusDoubleArgument(3), 128u);
EXPECT_EQ(iface.staticGetArgument(17), 420u);
MLIRContext other;
IntegerType i8other = IntegerType::get(&other, 8);
EXPECT_FALSE(isa<TestExternalTypeInterface>(i8other));
}
struct TestTypeModel
: public TestExternalTypeInterface::ExternalModel<TestTypeModel,
test::TestType> {
unsigned getBitwidthPlusArg(Type type, unsigned arg) const { return arg; }
static unsigned staticGetSomeValuePlusArg(unsigned arg) { return 10 + arg; }
};
TEST(InterfaceAttachment, TypeDelayedContextConstruct) {
DialectRegistry registry;
registry.insert<test::TestDialect>();
registry.addExtension(+[](MLIRContext *ctx, test::TestDialect *dialect) {
test::TestType::attachInterface<TestTypeModel>(*ctx);
});
MLIRContext context(registry);
context.loadDialect<test::TestDialect>();
test::TestType testType = test::TestType::get(&context);
auto iface = dyn_cast<TestExternalTypeInterface>(testType);
ASSERT_TRUE(iface != nullptr);
EXPECT_EQ(iface.getBitwidthPlusArg(42), 42u);
EXPECT_EQ(iface.staticGetSomeValuePlusArg(10), 20u);
}
TEST(InterfaceAttachment, TypeDelayedContextAppend) {
DialectRegistry registry;
registry.insert<test::TestDialect>();
registry.addExtension(+[](MLIRContext *ctx, test::TestDialect *dialect) {
test::TestType::attachInterface<TestTypeModel>(*ctx);
});
MLIRContext context;
context.loadDialect<test::TestDialect>();
test::TestType testType = test::TestType::get(&context);
EXPECT_FALSE(isa<TestExternalTypeInterface>(testType));
context.appendDialectRegistry(registry);
EXPECT_TRUE(isa<TestExternalTypeInterface>(testType));
}
TEST(InterfaceAttachment, RepeatedRegistration) {
DialectRegistry registry;
registry.addExtension(+[](MLIRContext *ctx, BuiltinDialect *dialect) {
IntegerType::attachInterface<Model>(*ctx);
});
MLIRContext context(registry);
context.appendDialectRegistry(registry);
}
TEST(InterfaceAttachment, TypeBuiltinDelayed) {
DialectRegistry registry;
registry.addExtension(+[](MLIRContext *ctx, BuiltinDialect *dialect) {
IntegerType::attachInterface<Model>(*ctx);
});
MLIRContext context(registry);
IntegerType i16 = IntegerType::get(&context, 16);
EXPECT_TRUE(isa<TestExternalTypeInterface>(i16));
MLIRContext initiallyEmpty;
IntegerType i32 = IntegerType::get(&initiallyEmpty, 32);
EXPECT_FALSE(isa<TestExternalTypeInterface>(i32));
initiallyEmpty.appendDialectRegistry(registry);
EXPECT_TRUE(isa<TestExternalTypeInterface>(i32));
}
struct TestExternalFallbackTypeIntegerModel
: public TestExternalFallbackTypeInterface::ExternalModel<
TestExternalFallbackTypeIntegerModel, IntegerType> {};
struct TestExternalFallbackTypeVectorModel
: public TestExternalFallbackTypeInterface::FallbackModel<
TestExternalFallbackTypeVectorModel> {
unsigned getBitwidth(Type type) const {
IntegerType elementType =
dyn_cast_or_null<IntegerType>(cast<VectorType>(type).getElementType());
return elementType ? elementType.getWidth() : 0;
}
};
TEST(InterfaceAttachment, Fallback) {
MLIRContext context;
IntegerType i8 = IntegerType::get(&context, 8);
ASSERT_FALSE(isa<TestExternalFallbackTypeInterface>(i8));
IntegerType::attachInterface<TestExternalFallbackTypeIntegerModel>(context);
ASSERT_TRUE(isa<TestExternalFallbackTypeInterface>(i8));
VectorType vec = VectorType::get({42}, i8);
ASSERT_FALSE(isa<TestExternalFallbackTypeInterface>(vec));
VectorType::attachInterface<TestExternalFallbackTypeVectorModel>(context);
ASSERT_TRUE(isa<TestExternalFallbackTypeInterface>(vec));
EXPECT_EQ(cast<TestExternalFallbackTypeInterface>(vec).getBitwidth(), 8u);
}
struct TestExternalIntegerAttrModel
: public TestExternalAttrInterface::ExternalModel<
TestExternalIntegerAttrModel, IntegerAttr> {
const Dialect *getDialectPtr(Attribute attr) const {
return &cast<IntegerAttr>(attr).getDialect();
}
static int getSomeNumber() { return 42; }
};
TEST(InterfaceAttachment, Attribute) {
MLIRContext context;
IntegerAttr attr = IntegerAttr::get(IntegerType::get(&context, 32), 42);
ASSERT_FALSE(isa<TestExternalAttrInterface>(attr));
IntegerAttr::attachInterface<TestExternalIntegerAttrModel>(context);
auto iface = dyn_cast<TestExternalAttrInterface>(attr);
ASSERT_TRUE(iface != nullptr);
EXPECT_EQ(iface.getDialectPtr(), &attr.getDialect());
EXPECT_EQ(iface.getSomeNumber(), 42);
}
struct TestExternalSimpleAAttrModel
: public TestExternalAttrInterface::ExternalModel<
TestExternalSimpleAAttrModel, test::SimpleAAttr> {
const Dialect *getDialectPtr(Attribute attr) const {
return &attr.getDialect();
}
static int getSomeNumber() { return 21; }
};
TEST(InterfaceAttachmentTest, AttributeDelayed) {
DialectRegistry registry;
registry.insert<test::TestDialect>();
registry.addExtension(+[](MLIRContext *ctx, test::TestDialect *dialect) {
test::SimpleAAttr::attachInterface<TestExternalSimpleAAttrModel>(*ctx);
});
MLIRContext context(registry);
context.loadDialect<test::TestDialect>();
auto attr = test::SimpleAAttr::get(&context);
EXPECT_TRUE(isa<TestExternalAttrInterface>(attr));
MLIRContext initiallyEmpty;
initiallyEmpty.loadDialect<test::TestDialect>();
attr = test::SimpleAAttr::get(&initiallyEmpty);
EXPECT_FALSE(isa<TestExternalAttrInterface>(attr));
initiallyEmpty.appendDialectRegistry(registry);
EXPECT_TRUE(isa<TestExternalAttrInterface>(attr));
}
struct TestExternalOpModel
: public TestExternalOpInterface::ExternalModel<TestExternalOpModel,
ModuleOp> {
unsigned getNameLengthPlusArg(Operation *op, unsigned arg) const {
return op->getName().getStringRef().size() + arg;
}
static unsigned getNameLengthPlusArgTwice(unsigned arg) {
return ModuleOp::getOperationName().size() + 2 * arg;
}
};
struct TestExternalOpOverridingModel
: public TestExternalOpInterface::FallbackModel<
TestExternalOpOverridingModel> {
unsigned getNameLengthPlusArg(Operation *op, unsigned arg) const {
return op->getName().getStringRef().size() + arg;
}
static unsigned getNameLengthPlusArgTwice(unsigned arg) {
return UnrealizedConversionCastOp::getOperationName().size() + 2 * arg;
}
unsigned getNameLengthTimesArg(Operation *op, unsigned arg) const {
return 42;
}
static unsigned getNameLengthMinusArg(unsigned arg) { return 21; }
};
TEST(InterfaceAttachment, Operation) {
MLIRContext context;
OpBuilder builder(&context);
OwningOpRef<ModuleOp> moduleOp =
builder.create<ModuleOp>(UnknownLoc::get(&context));
ASSERT_FALSE(isa<TestExternalOpInterface>(moduleOp->getOperation()));
ModuleOp::attachInterface<TestExternalOpModel>(context);
auto iface = dyn_cast<TestExternalOpInterface>(moduleOp->getOperation());
ASSERT_TRUE(iface != nullptr);
EXPECT_EQ(iface.getNameLengthPlusArg(10), 24u);
EXPECT_EQ(iface.getNameLengthTimesArg(3), 42u);
EXPECT_EQ(iface.getNameLengthPlusArgTwice(18), 50u);
EXPECT_EQ(iface.getNameLengthMinusArg(5), 9u);
OwningOpRef<UnrealizedConversionCastOp> castOp =
builder.create<UnrealizedConversionCastOp>(UnknownLoc::get(&context),
TypeRange(), ValueRange());
ASSERT_FALSE(isa<TestExternalOpInterface>(castOp->getOperation()));
UnrealizedConversionCastOp::attachInterface<TestExternalOpOverridingModel>(
context);
iface = dyn_cast<TestExternalOpInterface>(castOp->getOperation());
ASSERT_TRUE(iface != nullptr);
EXPECT_EQ(iface.getNameLengthPlusArg(10), 44u);
EXPECT_EQ(iface.getNameLengthTimesArg(0), 42u);
EXPECT_EQ(iface.getNameLengthPlusArgTwice(8), 50u);
EXPECT_EQ(iface.getNameLengthMinusArg(1000), 21u);
MLIRContext other;
OwningOpRef<ModuleOp> otherModuleOp =
ModuleOp::create(UnknownLoc::get(&other));
ASSERT_FALSE(isa<TestExternalOpInterface>(otherModuleOp->getOperation()));
}
template <class ConcreteOp>
struct TestExternalTestOpModel
: public TestExternalOpInterface::ExternalModel<
TestExternalTestOpModel<ConcreteOp>, ConcreteOp> {
unsigned getNameLengthPlusArg(Operation *op, unsigned arg) const {
return op->getName().getStringRef().size() + arg;
}
static unsigned getNameLengthPlusArgTwice(unsigned arg) {
return ConcreteOp::getOperationName().size() + 2 * arg;
}
};
TEST(InterfaceAttachment, OperationDelayedContextConstruct) {
DialectRegistry registry;
registry.insert<test::TestDialect>();
registry.addExtension(+[](MLIRContext *ctx, BuiltinDialect *dialect) {
ModuleOp::attachInterface<TestExternalOpModel>(*ctx);
});
registry.addExtension(+[](MLIRContext *ctx, test::TestDialect *dialect) {
test::OpJ::attachInterface<TestExternalTestOpModel<test::OpJ>>(*ctx);
test::OpH::attachInterface<TestExternalTestOpModel<test::OpH>>(*ctx);
});
MLIRContext context(registry);
context.loadDialect<test::TestDialect>();
OwningOpRef<ModuleOp> module = ModuleOp::create(UnknownLoc::get(&context));
OpBuilder builder(module->getBody(), module->getBody()->begin());
auto opJ =
builder.create<test::OpJ>(builder.getUnknownLoc(), builder.getI32Type());
auto opH =
builder.create<test::OpH>(builder.getUnknownLoc(), opJ.getResult());
auto opI =
builder.create<test::OpI>(builder.getUnknownLoc(), opJ.getResult());
EXPECT_TRUE(isa<TestExternalOpInterface>(module->getOperation()));
EXPECT_TRUE(isa<TestExternalOpInterface>(opJ.getOperation()));
EXPECT_TRUE(isa<TestExternalOpInterface>(opH.getOperation()));
EXPECT_FALSE(isa<TestExternalOpInterface>(opI.getOperation()));
}
TEST(InterfaceAttachment, OperationDelayedContextAppend) {
DialectRegistry registry;
registry.insert<test::TestDialect>();
registry.addExtension(+[](MLIRContext *ctx, BuiltinDialect *dialect) {
ModuleOp::attachInterface<TestExternalOpModel>(*ctx);
});
registry.addExtension(+[](MLIRContext *ctx, test::TestDialect *dialect) {
test::OpJ::attachInterface<TestExternalTestOpModel<test::OpJ>>(*ctx);
test::OpH::attachInterface<TestExternalTestOpModel<test::OpH>>(*ctx);
});
MLIRContext context;
context.loadDialect<test::TestDialect>();
OwningOpRef<ModuleOp> module = ModuleOp::create(UnknownLoc::get(&context));
OpBuilder builder(module->getBody(), module->getBody()->begin());
auto opJ =
builder.create<test::OpJ>(builder.getUnknownLoc(), builder.getI32Type());
auto opH =
builder.create<test::OpH>(builder.getUnknownLoc(), opJ.getResult());
auto opI =
builder.create<test::OpI>(builder.getUnknownLoc(), opJ.getResult());
EXPECT_FALSE(isa<TestExternalOpInterface>(module->getOperation()));
EXPECT_FALSE(isa<TestExternalOpInterface>(opJ.getOperation()));
EXPECT_FALSE(isa<TestExternalOpInterface>(opH.getOperation()));
EXPECT_FALSE(isa<TestExternalOpInterface>(opI.getOperation()));
context.appendDialectRegistry(registry);
EXPECT_TRUE(isa<TestExternalOpInterface>(module->getOperation()));
EXPECT_TRUE(isa<TestExternalOpInterface>(opJ.getOperation()));
EXPECT_TRUE(isa<TestExternalOpInterface>(opH.getOperation()));
EXPECT_FALSE(isa<TestExternalOpInterface>(opI.getOperation()));
}
TEST(InterfaceAttachmentTest, PromisedInterfaces) {
MLIRContext context;
auto *testDialect = context.getOrLoadDialect<test::TestDialect>();
auto attr = test::SimpleAAttr::get(&context);
EXPECT_FALSE(isa<TestExternalAttrInterface>(attr));
EXPECT_FALSE(
attr.hasPromiseOrImplementsInterface<TestExternalAttrInterface>());
testDialect->declarePromisedInterface<TestExternalAttrInterface,
test::SimpleAAttr>();
EXPECT_TRUE(
attr.hasPromiseOrImplementsInterface<TestExternalAttrInterface>());
test::SimpleAAttr::attachInterface<TestExternalAttrInterface>(context);
EXPECT_TRUE(isa<TestExternalAttrInterface>(attr));
EXPECT_TRUE(
attr.hasPromiseOrImplementsInterface<TestExternalAttrInterface>());
}
}