The patch addresses the linkage of the new autogenerated pass constructors, which, being declared as friend functions, resulted in having an inline nature and thus their implementations not being exported.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
Compilation log (a bit trimmed, for clearness):
====================[ Build | check-mlir | Debug ]============================== /Applications/CLion.app/Contents/bin/cmake/mac/bin/cmake --build /Users/mscuttari/Desktop/llvm-project/llvm/cmake-build-debug --target check-mlir -j 8 [127/139] Linking CXX executable tools/mlir/unittests/ExecutionEngine/MLIRExecutionEngineTests Undefined symbols for architecture arm64: "mlir::createConvertArithmeticToLLVMPass()", referenced from: lowerToLLVMDialect(mlir::ModuleOp) in Invoke.cpp.o ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation) [130/139] Linking CXX executable bin/mlir-opt Undefined symbols for architecture arm64: "mlir::createConvertArithmeticToLLVMPass()", referenced from: mlir::registerConvertArithmeticToLLVMPass()::'lambda'()::operator()() const in mlir-opt.cpp.o ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation) [131/139] Linking CXX executable bin/mlir-capi-ir-test Undefined symbols for architecture arm64: "mlir::createConvertArithmeticToLLVMPass()", referenced from: mlir::registerConvertArithmeticToLLVMPass()::'lambda'()::operator()() const in libMLIRCAPIRegisterEverything.a(RegisterEverything.cpp.o) ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation) [132/139] Linking CXX executable bin/mlir-reduce Undefined symbols for architecture arm64: "mlir::createConvertArithmeticToLLVMPass()", referenced from: mlir::registerConvertArithmeticToLLVMPass()::'lambda'()::operator()() const in mlir-reduce.cpp.o ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation) [133/139] Linking CXX executable bin/mlir-capi-execution-engine-test Undefined symbols for architecture arm64: "mlir::createConvertArithmeticToLLVMPass()", referenced from: _mlirCreateConversionConvertArithmeticToLLVMPass in libMLIRCAPIConversion.a(Passes.cpp.o) mlir::registerConvertArithmeticToLLVMPass()::'lambda'()::operator()() const in libMLIRCAPIConversion.a(Passes.cpp.o) mlir::registerConvertArithmeticToLLVMPass()::'lambda'()::operator()() const in libMLIRCAPIRegisterEverything.a(RegisterEverything.cpp.o) ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation) [134/139] Linking CXX executable bin/mlir-cpu-runner ninja: build stopped: subcommand failed.
mlir/lib/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.cpp | ||
---|---|---|
21 ↗ | (On Diff #455260) | Is the base pass class being generated in the mlir namespace? It needs to be (the same as the namespace the create method is in). |
mlir/lib/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.cpp | ||
---|---|---|
21 ↗ | (On Diff #455260) | Already tried (but uploaded the version without, as per original design intentions), didn't make any difference. |
Wrapping the base class in mlir namespace. It is indeed the correct way to go, but still the
problem persists.
The issue is that the friend function in the class gets an inline linkage: it only gets emitted if it used in the translation unit.
I'm not sure if it can be made "not inline", usually you have to define it outside of the class but I don't know what the syntax would be here...
I've made a few tries but none of them solved the problem. What if we just go for the splitting of the macro? It seems to be we are trying to push the standard to its limits, with all the consequent risks.
The inline linkage is specified in the standard it seems (I think): https://en.cppreference.com/w/cpp/language/friend
Defines a non-member function, and makes it a friend of this class at the same time. Such non-member function is always inline
If that's the case the easy fix would be to use an "Impl" function for the friend that is local, and just dispatch to that in the main create method:
std::unique_ptr<Pass> createFooImpl(); template <typename T> class FooBase ... { friend std::unique_ptr<Pass> createFooImpl(...) { return std::make_unique<T>(...); } }; std::unique_ptr<Pass> createFoo() { return createFooImpl(); }
That worked when I did it locally. I think if we really need to we should switch to 3 macros, but I think we should try as much as possible to limit the complexity necessary to define a single pass.
@rriddle thanks for the suggestion, working flawlessly. I have uploaded your fix and removed the changes I was doing to the passes, which I will instead upload in a separate patch in order to avoid a single commit mixing different stuff.
Nice! Do you think we should add a namespace field to the tablegen pass def now? Seems like that would be consistent with everything else (and would remove potential user foot guns as well).
Sounds reasonable. Maybe a implnamespace for both the base class and the createPassNameImplmethods? At that point we can even rename createPassNameImplto just createPassName, as it would live in the implnamespace.
@rriddle I'm sorry for the early ping, but I'm fixing the big patch regarding all the passes and I would like to know if the current namespace separation looks good. I'd like to avoid reiterating over all the passes just for this :D
The impl namespace seems fine to me, though we would need to update the docs to describe this. The docs should describe the ideal way to do things, and we can update the old uses in the codebase in the meantime.
I've updated the docs. Feel free to comment on them if you feel them not to be clear enough.
In the meanwhile, I've uploaded a first draft regarding the patches to the pass declarations, so we can start discussing about them: D132838