| // Copyright 2023 The IREE Authors |
| // |
| // Licensed 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 |
| |
| #include "compiler/plugins/target/LLVMCPU/Builtins/UKernel.h" |
| |
| #include "iree/builtins/ukernel/ukernel_bitcode.h" |
| #include "iree/compiler/Codegen/Utils/Utils.h" |
| #include "llvm/Bitcode/BitcodeReader.h" |
| #include "llvm/Support/MemoryBufferRef.h" |
| #include "mlir/Support/LLVM.h" |
| |
| namespace mlir::iree_compiler::IREE::HAL { |
| |
| // Note: even if the llvm::Expected status is successful, the enclosed pointer |
| // may still be null, indicating that the file was not found. |
| static llvm::Expected<std::unique_ptr<llvm::Module>> |
| loadUKernelBitcodeFile(StringRef filename, llvm::LLVMContext &context) { |
| const iree_file_toc_t *file_start = iree_ukernel_bitcode_create(); |
| const iree_file_toc_t *file_end = file_start + iree_ukernel_bitcode_size(); |
| for (const iree_file_toc_t *file = file_start; file < file_end; ++file) { |
| if (filename == file->name) { |
| llvm::MemoryBufferRef bitcodeBufferRef( |
| llvm::StringRef(file->data, file->size), file->name); |
| return llvm::parseBitcodeFile(bitcodeBufferRef, context); |
| } |
| } |
| |
| // Some bitcode files are optional: we don't have arch-specific ukernel code |
| // for all architectures. So it's normal to be returning nullptr here. |
| return nullptr; |
| } |
| |
| llvm::Expected<std::unique_ptr<llvm::Module>> |
| loadUKernelBitcode(llvm::TargetMachine *targetMachine, |
| llvm::LLVMContext &context) { |
| const char *archName = |
| getIreeArchNameForTargetTriple(targetMachine->getTargetTriple()); |
| std::string filename = std::string("ukernel_bitcode_") + archName + ".bc"; |
| llvm::Expected<std::unique_ptr<llvm::Module>> module = |
| loadUKernelBitcodeFile(filename, context); |
| if (!module) { |
| // Error. Propagate to the caller. |
| return module; |
| } |
| if (!module.get()) { |
| // File not found. Just means that we don't have bitcode for that |
| // architecture. Return the null module as a success case. |
| return module; |
| } |
| // Ukernels rely fundamentally on always getting inlined, for their logic |
| // to specialize at compile time, including specialization for a specific |
| // combination of data types, a specific SIMD ISA variant, etc. Then all the |
| // unused code paths can get DCE'd. That's why failure to inline a ukernel |
| // can result in a large penalty in both performance and code size. |
| for (auto &func : module.get()->functions()) { |
| func.addFnAttr(llvm::Attribute::AlwaysInline); |
| } |
| return module; |
| } |
| |
| } // namespace mlir::iree_compiler::IREE::HAL |