| // Copyright 2021 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/Device.h" |
| |
| #include "iree/builtins/device/libdevice_bitcode.h" |
| #include "llvm/Bitcode/BitcodeReader.h" |
| #include "llvm/IR/Constants.h" |
| #include "llvm/Support/MemoryBufferRef.h" |
| #include "mlir/Support/LLVM.h" |
| |
| namespace mlir::iree_compiler::IREE::HAL { |
| |
| static const iree_file_toc_t *lookupDeviceFile(StringRef filename) { |
| for (size_t i = 0; i < iree_builtins_libdevice_bitcode_size(); ++i) { |
| const auto &file_toc = iree_builtins_libdevice_bitcode_create()[i]; |
| if (filename == file_toc.name) |
| return &file_toc; |
| } |
| return nullptr; |
| } |
| |
| static const iree_file_toc_t * |
| lookupDeviceFile(llvm::TargetMachine *targetMachine) { |
| const auto &triple = targetMachine->getTargetTriple(); |
| |
| // NOTE: other arch-specific checks go here. |
| |
| if (triple.isWasm()) { |
| // TODO(benvanik): feature detect simd and such. |
| // auto features = targetMachine->getTargetFeatureString(); |
| if (triple.isArch32Bit()) { |
| return lookupDeviceFile("libdevice_wasm32_generic.bc"); |
| } else if (triple.isArch64Bit()) { |
| return lookupDeviceFile("libdevice_wasm64_generic.bc"); |
| } |
| } |
| |
| // Fallback path using the generic wasm variants as they are largely |
| // machine-agnostic. |
| if (triple.isArch32Bit()) { |
| return lookupDeviceFile("libdevice_wasm32_generic.bc"); |
| } else if (triple.isArch64Bit() && |
| targetMachine->getTargetFeatureString().contains("+sme")) { |
| return lookupDeviceFile("libdevice_aarch64_sme.bc"); |
| } else if (triple.isArch64Bit()) { |
| return lookupDeviceFile("libdevice_wasm64_generic.bc"); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| llvm::Expected<std::unique_ptr<llvm::Module>> |
| loadDeviceBitcode(llvm::TargetMachine *targetMachine, |
| llvm::LLVMContext &context) { |
| // Find a bitcode file for the current architecture. |
| const auto *file = lookupDeviceFile(targetMachine); |
| if (!file) { |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), |
| "no matching architecture bitcode file"); |
| } |
| |
| // Load the generic bitcode file contents. |
| llvm::MemoryBufferRef bitcodeBufferRef( |
| llvm::StringRef(file->data, file->size), file->name); |
| auto bitcodeModuleValue = llvm::parseBitcodeFile(bitcodeBufferRef, context); |
| if (!bitcodeModuleValue) |
| return bitcodeModuleValue; |
| auto bitcodeModule = std::move(bitcodeModuleValue.get()); |
| |
| // Clang adds its own per-function attributes that we need to strip so that |
| // our current executable variant target is used instead. |
| for (auto &func : bitcodeModule->functions()) { |
| func.removeFnAttr("target-cpu"); |
| func.removeFnAttr("tune-cpu"); |
| func.removeFnAttr("target-features"); |
| } |
| |
| return std::move(bitcodeModule); |
| } |
| |
| static void overridePlatformGlobal(llvm::Module &module, StringRef globalName, |
| uint32_t newValue) { |
| // NOTE: the global will not be defined if it is not used in the module. |
| auto *globalValue = module.getNamedGlobal(globalName); |
| if (!globalValue) |
| return; |
| globalValue->setLinkage(llvm::GlobalValue::PrivateLinkage); |
| globalValue->setDSOLocal(true); |
| globalValue->setConstant(true); |
| globalValue->setInitializer( |
| llvm::ConstantInt::get(globalValue->getValueType(), APInt(32, newValue))); |
| } |
| |
| void specializeDeviceModule(IREE::HAL::ExecutableVariantOp variantOp, |
| llvm::Module &module, |
| llvm::TargetMachine &targetMachine) { |
| overridePlatformGlobal(module, "libdevice_platform_example_flag", 0u); |
| } |
| |
| } // namespace mlir::iree_compiler::IREE::HAL |