blob: bbb29228fd7c691eb06ac090c836a8938672ac4a [file] [log] [blame]
// 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