[MLIR] Make PassPipelineOptions
virtually inheriting from PassOptions to allow diamond inheritance (#146370)
## Problem Given 3 pipelines, A, B, and a superset pipeline AB that runs both the A & B pipelines, it is not easy to manage their options - one needs to manually recreate all options from A and B into AB, and maintain them. This is tedious. ## Proposed solution Ideally, AB options class inherits from both A and B options, making the maintenance effortless. Today though, this causes problems as their base classes `PassPipelineOptions<A>` and `PassPipelineOptions<B>` both inherit from `mlir::detail::PassOptions`, leading to the so called "diamond inheritance problem", i.e. multiple definitions of the same symbol, in this case parseFromString that is defined in mlir::detail::PassOptions. Visually, the inheritance looks like this: ``` mlir::detail::PassOptions ↑ ↑ | | PassPipelineOptions<A> PassPipelineOptions<B> ↑ ↑ | | AOptions BOptions ↑ ↑ +---------+--------+ | ABOptions ``` A proposed fix is to use the common solution to the diamond inheritance problem - virtual inheritance.
This commit is contained in:
parent
bd39ae6125
commit
9349484e8f
@ -377,7 +377,7 @@ private:
|
||||
/// ListOption<int> someListFlag{*this, "flag-name", llvm::cl::desc("...")};
|
||||
/// };
|
||||
template <typename T>
|
||||
class PassPipelineOptions : public detail::PassOptions {
|
||||
class PassPipelineOptions : public virtual detail::PassOptions {
|
||||
public:
|
||||
/// Factory that parses the provided options and returns a unique_ptr to the
|
||||
/// struct.
|
||||
|
@ -13,6 +13,7 @@
|
||||
// RUN: mlir-opt %s -verify-each=false -pass-pipeline='builtin.module(builtin.module(func.func(test-options-pass{list=3}), func.func(test-options-pass{enum=one list=1,2,3,4 string=foo"bar"baz})))' -dump-pass-pipeline 2>&1 | FileCheck --check-prefix=CHECK_6 %s
|
||||
// RUN: mlir-opt %s -verify-each=false '-test-options-super-pass-pipeline=super-list={{enum=zero list=1 string=foo},{enum=one list=2 string="bar"},{enum=two list=3 string={baz}}}' -dump-pass-pipeline 2>&1 | FileCheck --check-prefix=CHECK_7 %s
|
||||
// RUN: mlir-opt %s -verify-each=false -pass-pipeline='builtin.module(func.func(test-options-super-pass{list={{enum=zero list={1} string=foo },{enum=one list={2} string=bar },{enum=two list={3} string=baz }}}))' -dump-pass-pipeline 2>&1 | FileCheck --check-prefix=CHECK_7 %s
|
||||
// RUN: mlir-opt %s -verify-each=false -test-options-super-set-ab-pipeline='foo=true bar=false' -dump-pass-pipeline 2>&1 | FileCheck --check-prefix=CHECK_11 %s
|
||||
|
||||
|
||||
// This test checks that lists-of-nested-options like 'option1={...},{....}' can be parsed
|
||||
@ -106,3 +107,12 @@
|
||||
// CHECK_10-NEXT: test-options-pass{enum=zero string= string-list={,}}
|
||||
// CHECK_10-NEXT: )
|
||||
// CHECK_10-NEXT: )
|
||||
|
||||
// CHECK_11: builtin.module(
|
||||
// CHECK_11-NEXT: func.func(
|
||||
// CHECK_11-NEXT: test-options-pass-a
|
||||
// CHECK_11-NEXT: )
|
||||
// CHECK_11-NEXT: func.func(
|
||||
// CHECK_11-NEXT: test-options-pass-b
|
||||
// CHECK_11-NEXT: )
|
||||
// CHECK_11-NEXT: )
|
||||
|
@ -133,6 +133,51 @@ struct TestOptionsSuperPass
|
||||
llvm::cl::desc("Example list of PassPipelineOptions option")};
|
||||
};
|
||||
|
||||
struct TestOptionsPassA
|
||||
: public PassWrapper<TestOptionsPassA, OperationPass<func::FuncOp>> {
|
||||
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestOptionsPassA)
|
||||
|
||||
struct Options : public PassPipelineOptions<Options> {
|
||||
Option<bool> foo{*this, "foo", llvm::cl::desc("Example boolean option")};
|
||||
};
|
||||
|
||||
TestOptionsPassA() = default;
|
||||
TestOptionsPassA(const TestOptionsPassA &) : PassWrapper() {}
|
||||
TestOptionsPassA(const Options &options) { this->options.foo = options.foo; }
|
||||
|
||||
void runOnOperation() final {}
|
||||
StringRef getArgument() const final { return "test-options-pass-a"; }
|
||||
StringRef getDescription() const final {
|
||||
return "Test superset options parsing capabilities - subset A";
|
||||
}
|
||||
|
||||
Options options;
|
||||
};
|
||||
|
||||
struct TestOptionsPassB
|
||||
: public PassWrapper<TestOptionsPassB, OperationPass<func::FuncOp>> {
|
||||
MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(TestOptionsPassB)
|
||||
|
||||
struct Options : public PassPipelineOptions<Options> {
|
||||
Option<bool> bar{*this, "bar", llvm::cl::desc("Example boolean option")};
|
||||
};
|
||||
|
||||
TestOptionsPassB() = default;
|
||||
TestOptionsPassB(const TestOptionsPassB &) : PassWrapper() {}
|
||||
TestOptionsPassB(const Options &options) { this->options.bar = options.bar; }
|
||||
|
||||
void runOnOperation() final {}
|
||||
StringRef getArgument() const final { return "test-options-pass-b"; }
|
||||
StringRef getDescription() const final {
|
||||
return "Test superset options parsing capabilities - subset B";
|
||||
}
|
||||
|
||||
Options options;
|
||||
};
|
||||
|
||||
struct TestPipelineOptionsSuperSetAB : TestOptionsPassA::Options,
|
||||
TestOptionsPassB::Options {};
|
||||
|
||||
/// A test pass that always aborts to enable testing the crash recovery
|
||||
/// mechanism of the pass manager.
|
||||
struct TestCrashRecoveryPass
|
||||
@ -270,6 +315,9 @@ void registerPassManagerTestPass() {
|
||||
PassRegistration<TestOptionsPass>();
|
||||
PassRegistration<TestOptionsSuperPass>();
|
||||
|
||||
PassRegistration<TestOptionsPassA>();
|
||||
PassRegistration<TestOptionsPassB>();
|
||||
|
||||
PassRegistration<TestModulePass>();
|
||||
|
||||
PassRegistration<TestFunctionPass>();
|
||||
@ -306,5 +354,16 @@ void registerPassManagerTestPass() {
|
||||
[](OpPassManager &pm, const TestOptionsSuperPass::Options &options) {
|
||||
pm.addPass(std::make_unique<TestOptionsSuperPass>(options));
|
||||
});
|
||||
|
||||
PassPipelineRegistration<TestPipelineOptionsSuperSetAB>
|
||||
registerPipelineOptionsSuperSetABPipeline(
|
||||
"test-options-super-set-ab-pipeline",
|
||||
"Parses options of PassPipelineOptions using pass pipeline "
|
||||
"registration",
|
||||
[](OpPassManager &pm, const TestPipelineOptionsSuperSetAB &options) {
|
||||
// Pass superset AB options to subset options A and B
|
||||
pm.addPass(std::make_unique<TestOptionsPassA>(options));
|
||||
pm.addPass(std::make_unique<TestOptionsPassB>(options));
|
||||
});
|
||||
}
|
||||
} // namespace mlir
|
||||
|
Loading…
x
Reference in New Issue
Block a user