Skip to content

[LLVMCPU] Fix linking between modules with different target attributes #16000

Description

@bjacob

Just recording thoughts before holidays...

Around LLVMCPUTarget.cpp, when we load and link different bitcode modules, we are currently just removing some function attributes to make it more or less work, https://github.com/openxla/iree/blob/9cde4e3dc589ab783757b863a747477bc6fafc91/compiler/src/iree/compiler/Dialect/HAL/Target/LLVMCPU/Builtins/UKernel.cpp#L35-L44 , but it's not very rigorous, and it needs to become more rigorous in order to overcome two issues we're having:

  • When we remove all function target attributes including target CPU features, and the code inside a function was actually relying on a CPU feature (e.g. was using an intrinsic provided by that CPU feature), we are effectively introducing undefined behavior. It mostly just works, until this week it didn't --- one of the bugs I've dealt with this week was, in int4-quantized Llama2, some avx512vnni LLVM intrinsic was causing a llvm_unreachable to be reached in the LLVM x86 backend.
  • What we are currently doing with the attributes-stripping isn't entirely successful at one of its main goals, which is to enable perfect inlining of ukernel code into dispatch functions. It's working on x86 but not on arm64. It's probably a matter of being more rigorous about how we bring attributes in good alignment between caller and callee.

I'm thinking that the solution is, rather than dropping attributes, we just need to add all the attributes from the llvm::TargetMachine. Then, by construction, all functions that are actually legal to call on that target machine will have exactly the same attribute (give or take any preexisting difference in the attributes that they had before). And functions that have additional attributes are not legal to call on this target machine anyway.

As a first approximation to make this concrete, I'm toying with a change like this:

-static void removeTargetAttributes(llvm::Module &module) {
+static void overrideTargetAttributes(llvm::Module &module, const llvm::TargetMachine& targetMachine) {
   for (auto &func : module.functions()) {
-    func.removeFnAttr("target-cpu");
-    func.removeFnAttr("tune-cpu");
-    func.removeFnAttr("target-features");
+    func.addFnAttr("target-cpu", targetMachine.getTargetCPU());
+    func.addFnAttr("target-features", targetMachine.getTargetFeatureString());
   }
 }

I'll get back to this in early 2024... I have been mulling something like this for months as I knew about the issue about ukernels not getting fully inlined on arm64, but now that I know that it's also causing UB manifesting as compiler crashes on Llama2 on x86, this became high priority.

FYI @benvanik @Max191

Metadata

Metadata

Assignees

Labels

codegen/llvmLLVM code generation compiler backend

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions