Fixes https://github.com/llvm/llvm-project/issues/158034 For the input ```mlir irdl.dialect @conditional_dialect { // A conditional operation with regions irdl.operation @conditional { // Create region constraints %r0 = irdl.region // Unconstrained region %r1 = irdl.region() // Region with no entry block arguments %v0 = irdl.any %r2 = irdl.region(%v0) // Region with one i1 entry block argument irdl.regions(cond: %r2, then: %r0, else: %r1) } } ``` This produces the following cpp: https://gist.github.com/j2kun/d2095f108efbd8d403576d5c460e0c00 Summary of changes: - The op class and adaptor get named accessors to the regions `Region &get<RegionName>()` and `getRegions()` - The op now gets `OpTrait::NRegions<3>` and `OpInvariants` to trigger the region verification - Support for region block argument constraints is added, but not working for all constraints until codegen for `irdl.is` is added (filed https://github.com/llvm/llvm-project/issues/161018 and left a TODO). - Helper functions for the individual verification steps are added, following mlir-tblgen's format (in the above gist, `__mlir_irdl_local_region_constraint_ConditionalOp_cond` and similar), and `verifyInvariantsImpl` that calls them. - Regions are added in the builder ## Questions for the reviewer ### What is the "correct" interface for verification? I used `mlir-tblgen` on an analogous version of the example `ConditionalOp` in this PR, and I see an `::mlir::OpTrait::OpInvariants` trait as well as ```cpp ::llvm::LogicalResult ConditionalOp::verifyInvariantsImpl() { { unsigned index = 0; (void)index; for (auto ®ion : ::llvm::MutableArrayRef((*this)->getRegion(0))) if (::mlir::failed(__mlir_ods_local_region_constraint_test1(*this, region, "cond", index++))) return ::mlir::failure(); for (auto ®ion : ::llvm::MutableArrayRef((*this)->getRegion(1))) if (::mlir::failed(__mlir_ods_local_region_constraint_test1(*this, region, "then", index++))) return ::mlir::failure(); for (auto ®ion : ::llvm::MutableArrayRef((*this)->getRegion(2))) if (::mlir::failed(__mlir_ods_local_region_constraint_test1(*this, region, "else", index++))) return ::mlir::failure(); } return ::mlir::success(); } ::llvm::LogicalResult ConditionalOp::verifyInvariants() { if(::mlir::succeeded(verifyInvariantsImpl()) && ::mlir::succeeded(verify())) return ::mlir::success(); return ::mlir::failure(); } ``` However, `OpInvariants` only seems to need `verifyInvariantsImpl`, so it's not clear to me what is the purpose of the `verifyInvariants` function, or, if I leave out `verifyInvariants`, whether I need to call `verify()` in my implementation of `verifyInvariantsImpl`. In this PR, I omitted `verifyInvariants` and generated `verifyInvariantsImpl`. ### Is testing sufficient? I am not certain I implemented the builders properly, and it's unclear to me to what extent the existing tests check this (which look like they compile the generated cpp, but don't actually use it). Did I omit some standard function or overload? --------- Co-authored-by: Jeremy Kun <j2kun@users.noreply.github.com>
112 lines
4.1 KiB
C++
112 lines
4.1 KiB
C++
//===- TestIRDLToCppDialect.cpp - MLIR Test Dialect Types ---------------*-===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file includes TestIRDLToCpp dialect.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// #include "mlir/IR/Dialect.h"
|
|
#include "mlir/IR/Region.h"
|
|
|
|
#include "mlir/Dialect/SCF/IR/SCF.h"
|
|
#include "mlir/IR/BuiltinTypes.h"
|
|
#include "mlir/IR/DialectImplementation.h"
|
|
#include "mlir/Interfaces/InferTypeOpInterface.h"
|
|
#include "mlir/Pass/Pass.h"
|
|
#include "mlir/Target/LLVMIR/Dialect/Builtin/BuiltinToLLVMIRTranslation.h"
|
|
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
|
|
#include "mlir/Target/LLVMIR/LLVMTranslationInterface.h"
|
|
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
|
|
#include "mlir/Tools/mlir-translate/Translation.h"
|
|
#include "mlir/Transforms/DialectConversion.h"
|
|
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
|
|
#include "llvm/ADT/DenseSet.h"
|
|
#include "llvm/ADT/TypeSwitch.h"
|
|
|
|
#include "TestIRDLToCppDialect.h"
|
|
|
|
#define GEN_DIALECT_DEF
|
|
#include "test_irdl_to_cpp.irdl.mlir.cpp.inc"
|
|
|
|
namespace test {
|
|
using namespace mlir;
|
|
struct TestOpConversion : public OpConversionPattern<test_irdl_to_cpp::BeefOp> {
|
|
using OpConversionPattern::OpConversionPattern;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(mlir::test_irdl_to_cpp::BeefOp op, OpAdaptor adaptor,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
assert(adaptor.getStructuredOperands(0).size() == 1);
|
|
assert(adaptor.getStructuredOperands(1).size() == 1);
|
|
|
|
auto bar = rewriter.replaceOpWithNewOp<test_irdl_to_cpp::BarOp>(
|
|
op, op->getResultTypes().front());
|
|
rewriter.setInsertionPointAfter(bar);
|
|
|
|
test_irdl_to_cpp::HashOp::create(rewriter, bar.getLoc(),
|
|
rewriter.getIntegerType(32),
|
|
adaptor.getLhs(), adaptor.getRhs());
|
|
return success();
|
|
}
|
|
};
|
|
|
|
struct TestRegionConversion
|
|
: public OpConversionPattern<test_irdl_to_cpp::ConditionalOp> {
|
|
using OpConversionPattern::OpConversionPattern;
|
|
|
|
LogicalResult
|
|
matchAndRewrite(mlir::test_irdl_to_cpp::ConditionalOp op, OpAdaptor adaptor,
|
|
ConversionPatternRewriter &rewriter) const override {
|
|
// Just exercising the C++ API even though these are not enforced in the
|
|
// dialect definition
|
|
assert(op.getThen().getBlocks().size() == 1);
|
|
assert(adaptor.getElse().getBlocks().size() == 1);
|
|
auto ifOp = scf::IfOp::create(rewriter, op.getLoc(), op.getInput());
|
|
rewriter.replaceOp(op, ifOp);
|
|
return success();
|
|
}
|
|
};
|
|
|
|
struct ConvertTestDialectToSomethingPass
|
|
: PassWrapper<ConvertTestDialectToSomethingPass, OperationPass<ModuleOp>> {
|
|
void runOnOperation() override {
|
|
MLIRContext *ctx = &getContext();
|
|
RewritePatternSet patterns(ctx);
|
|
patterns.add<TestOpConversion, TestRegionConversion>(ctx);
|
|
ConversionTarget target(getContext());
|
|
target.addIllegalOp<test_irdl_to_cpp::BeefOp,
|
|
test_irdl_to_cpp::ConditionalOp>();
|
|
target.addLegalOp<test_irdl_to_cpp::BarOp, test_irdl_to_cpp::HashOp,
|
|
scf::IfOp, scf::YieldOp>();
|
|
if (failed(applyPartialConversion(getOperation(), target,
|
|
std::move(patterns))))
|
|
signalPassFailure();
|
|
}
|
|
|
|
StringRef getArgument() const final { return "test-irdl-conversion-check"; }
|
|
StringRef getDescription() const final {
|
|
return "Checks the convertability of an irdl dialect";
|
|
}
|
|
|
|
void getDependentDialects(DialectRegistry ®istry) const override {
|
|
registry.insert<scf::SCFDialect>();
|
|
}
|
|
};
|
|
|
|
void registerIrdlTestDialect(mlir::DialectRegistry ®istry) {
|
|
registry.insert<mlir::test_irdl_to_cpp::TestIrdlToCppDialect>();
|
|
}
|
|
|
|
} // namespace test
|
|
|
|
namespace mlir::test {
|
|
void registerTestIrdlTestDialectConversionPass() {
|
|
PassRegistration<::test::ConvertTestDialectToSomethingPass>();
|
|
}
|
|
} // namespace mlir::test
|