Skip to content

Commit 6a7d6c5

Browse files
authored
[MLIR] Add a MLIR_NVVM_EMBED_LIBDEVICE CMake option that embeds libdevice in the binary (#120238)
This removes a runtime dependency on the CUDA Toolkit path, instead of looking up the filesystem we use a version of libdevice embedded in the binary at build time.
1 parent d8a0709 commit 6a7d6c5

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

mlir/lib/Target/LLVM/CMakeLists.txt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,45 @@ if ("NVPTX" IN_LIST LLVM_TARGETS_TO_BUILD)
119119
)
120120
endif()
121121

122+
123+
function(embed_binary_to_src file output_file symbol)
124+
file(READ ${file} filedata HEX)
125+
# Convert hex data for C compatibility
126+
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," filedata ${filedata})
127+
# Write data to output file
128+
file(WRITE ${output_file} "const char ${symbol}[] = {${filedata}};\nconst int ${symbol}_size = sizeof(${symbol});\n")
129+
endfunction()
130+
131+
set(MLIR_NVVM_EMBED_LIBDEVICE 0 CACHE BOOL "Embed CUDA libdevice.bc in the binary at build time instead of looking it up at runtime")
132+
if (MLIR_NVVM_EMBED_LIBDEVICE)
133+
if (NOT MLIR_NVVM_LIBDEVICE_PATH)
134+
if(CUDAToolkit_FOUND)
135+
find_file(MLIR_NVVM_LIBDEVICE_PATH libdevice.10.bc
136+
PATHS ${CUDAToolkit_LIBRARY_ROOT}
137+
PATH_SUFFIXES "nvvm/libdevice" NO_DEFAULT_PATH REQUIRED)
138+
else()
139+
message(FATAL_ERROR
140+
"Requested using the `nvptxcompiler` library backend but it couldn't be found.")
141+
endif()
142+
endif()
143+
144+
embed_binary_to_src(${MLIR_NVVM_LIBDEVICE_PATH} ${CMAKE_CURRENT_BINARY_DIR}/libdevice_embedded.c _mlir_embedded_libdevice)
145+
add_mlir_library(MLIRNVVMLibdevice
146+
${CMAKE_CURRENT_BINARY_DIR}/libdevice_embedded.c
147+
)
148+
target_link_libraries(MLIRNVVMTarget PRIVATE MLIRNVVMLibdevice)
149+
target_compile_definitions(obj.MLIRNVVMTarget
150+
PRIVATE
151+
MLIR_NVVM_EMBED_LIBDEVICE=1
152+
)
153+
else()
154+
target_compile_definitions(obj.MLIRNVVMTarget
155+
PRIVATE
156+
MLIR_NVVM_EMBED_LIBDEVICE=0
157+
)
158+
endif()
159+
160+
122161
if (MLIR_ENABLE_ROCM_CONVERSIONS)
123162
set(AMDGPU_LIBS
124163
AMDGPUAsmParser
@@ -169,3 +208,4 @@ if(MLIR_ENABLE_ROCM_CONVERSIONS)
169208
__DEFAULT_ROCM_PATH__="${DEFAULT_ROCM_PATH}"
170209
)
171210
endif()
211+

mlir/lib/Target/LLVM/NVVM/Target.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
#include "mlir/Dialect/GPU/IR/CompilationInterfaces.h"
1717
#include "mlir/Dialect/GPU/IR/GPUDialect.h"
1818
#include "mlir/Dialect/LLVMIR/NVVMDialect.h"
19+
#include "mlir/IR/BuiltinAttributeInterfaces.h"
20+
#include "mlir/IR/BuiltinDialect.h"
21+
#include "mlir/IR/BuiltinTypes.h"
22+
#include "mlir/IR/DialectResourceBlobManager.h"
1923
#include "mlir/Target/LLVM/NVVM/Utils.h"
2024
#include "mlir/Target/LLVMIR/Dialect/GPU/GPUToLLVMIRTranslation.h"
2125
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
@@ -33,6 +37,7 @@
3337
#include "llvm/Support/TargetSelect.h"
3438
#include "llvm/Support/raw_ostream.h"
3539

40+
#include <cstdint>
3641
#include <cstdlib>
3742

3843
using namespace mlir;
@@ -42,6 +47,9 @@ using namespace mlir::NVVM;
4247
#define __DEFAULT_CUDATOOLKIT_PATH__ ""
4348
#endif
4449

50+
extern "C" const char _mlir_embedded_libdevice[];
51+
extern "C" const unsigned _mlir_embedded_libdevice_size;
52+
4553
namespace {
4654
// Implementation of the `TargetAttrInterface` model.
4755
class NVVMTargetAttrImpl
@@ -130,6 +138,33 @@ ArrayRef<Attribute> SerializeGPUModuleBase::getLibrariesToLink() const {
130138

131139
// Try to append `libdevice` from a CUDA toolkit installation.
132140
LogicalResult SerializeGPUModuleBase::appendStandardLibs() {
141+
#if MLIR_NVVM_EMBED_LIBDEVICE
142+
// If libdevice is embedded in the binary, we don't look it up on the
143+
// filesystem.
144+
MLIRContext *ctx = target.getContext();
145+
auto type =
146+
RankedTensorType::get(ArrayRef<int64_t>{_mlir_embedded_libdevice_size},
147+
IntegerType::get(ctx, 8));
148+
auto resourceManager = DenseResourceElementsHandle::getManagerInterface(ctx);
149+
150+
// Lookup if we already loaded the resource, otherwise create it.
151+
DialectResourceBlobManager::BlobEntry *blob =
152+
resourceManager.getBlobManager().lookup("_mlir_embedded_libdevice");
153+
if (blob) {
154+
librariesToLink.push_back(DenseResourceElementsAttr::get(
155+
type, DenseResourceElementsHandle(
156+
blob, ctx->getLoadedDialect<BuiltinDialect>())));
157+
return success();
158+
}
159+
160+
// Allocate a resource using one of the UnManagedResourceBlob method to wrap
161+
// the embedded data.
162+
auto unmanagedBlob = UnmanagedAsmResourceBlob::allocateInferAlign(
163+
ArrayRef<char>{_mlir_embedded_libdevice, _mlir_embedded_libdevice_size});
164+
librariesToLink.push_back(DenseResourceElementsAttr::get(
165+
type, resourceManager.insert("_mlir_embedded_libdevice",
166+
std::move(unmanagedBlob))));
167+
#else
133168
StringRef pathRef = getToolkitPath();
134169
if (!pathRef.empty()) {
135170
SmallVector<char, 256> path;
@@ -149,6 +184,7 @@ LogicalResult SerializeGPUModuleBase::appendStandardLibs() {
149184
}
150185
librariesToLink.push_back(StringAttr::get(target.getContext(), pathRef));
151186
}
187+
#endif
152188
return success();
153189
}
154190

0 commit comments

Comments
 (0)