Skip to content

Commit be69402

Browse files
committed
[MLIR] Add a BlobAttr interface for attribute to wrap arbitrary content and use it as linkLibs for ModuleToObject
This change allows to expose through an interface attributes wrapping content as external resources, and the usage inside the ModuleToObject show how we will be able to provide runtime libraries without relying on the filesystem.
1 parent a036ce6 commit be69402

File tree

9 files changed

+154
-6
lines changed

9 files changed

+154
-6
lines changed

mlir/include/mlir/IR/BuiltinAttributeInterfaces.td

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,24 @@ def TypedAttrInterface : AttrInterface<"TypedAttr"> {
3434
>];
3535
}
3636

37+
//===----------------------------------------------------------------------===//
38+
// BlobAttrInterface
39+
//===----------------------------------------------------------------------===//
40+
41+
def BlobAttrInterface : AttrInterface<"BlobAttr"> {
42+
let cppNamespace = "::mlir";
43+
let description = [{
44+
This interface allows an attribute to expose a blob of data without more
45+
information. The data must be stored so that it can be accessed as a
46+
contiguous ArrayRef.
47+
}];
48+
49+
let methods = [InterfaceMethod<
50+
"Get the attribute's data",
51+
"::llvm::ArrayRef<char>", "getData"
52+
>];
53+
}
54+
3755
//===----------------------------------------------------------------------===//
3856
// ElementsAttrInterface
3957
//===----------------------------------------------------------------------===//

mlir/include/mlir/IR/BuiltinAttributes.td

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ def Builtin_DenseArrayRawDataParameter : ArrayRefParameter<
153153
}];
154154
}
155155

156-
def Builtin_DenseArray : Builtin_Attr<"DenseArray", "dense_array"> {
156+
def Builtin_DenseArray : Builtin_Attr<"DenseArray", "dense_array",
157+
[BlobAttrInterface]> {
157158
let summary = "A dense array of integer or floating point elements.";
158159
let description = [{
159160
A dense array attribute is an attribute that represents a dense array of
@@ -211,6 +212,10 @@ def Builtin_DenseArray : Builtin_Attr<"DenseArray", "dense_array"> {
211212
int64_t size() const { return getSize(); }
212213
/// Return true if there are no elements in the dense array.
213214
bool empty() const { return !size(); }
215+
/// BlobAttrInterface method.
216+
ArrayRef<char> getData() {
217+
return getRawData();
218+
}
214219
}];
215220
}
216221

@@ -431,7 +436,7 @@ def Builtin_DenseStringElementsAttr : Builtin_Attr<
431436
//===----------------------------------------------------------------------===//
432437

433438
def Builtin_DenseResourceElementsAttr : Builtin_Attr<"DenseResourceElements",
434-
"dense_resource_elements", [ElementsAttrInterface]> {
439+
"dense_resource_elements", [ElementsAttrInterface, BlobAttrInterface]> {
435440
let summary = "An Attribute containing a dense multi-dimensional array "
436441
"backed by a resource";
437442
let description = [{
@@ -485,6 +490,10 @@ def Builtin_DenseResourceElementsAttr : Builtin_Attr<"DenseResourceElements",
485490
"ShapedType":$type, "StringRef":$blobName, "AsmResourceBlob":$blob
486491
)>
487492
];
493+
let extraClassDeclaration = [{
494+
/// BlobAttrInterface method.
495+
ArrayRef<char> getData();
496+
}];
488497

489498
let skipDefaultBuilders = 1;
490499
}

mlir/lib/Dialect/GPU/Transforms/ModuleToBinary.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ void GpuModuleToBinaryPass::runOnOperation() {
7171
SmallVector<Attribute> librariesToLink;
7272
for (const std::string &path : linkFiles)
7373
librariesToLink.push_back(StringAttr::get(&getContext(), path));
74-
TargetOptions targetOptions(toolkitPath, librariesToLink, cmdOptions, elfSection,
75-
*targetFormat, lazyTableBuilder);
74+
TargetOptions targetOptions(toolkitPath, librariesToLink, cmdOptions,
75+
elfSection, *targetFormat, lazyTableBuilder);
7676
if (failed(transformGpuModulesToBinaries(
7777
getOperation(), OffloadingLLVMTranslationAttrInterface(nullptr),
7878
targetOptions)))

mlir/lib/IR/BuiltinAttributes.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,12 @@ DenseResourceElementsAttr DenseResourceElementsAttr::get(ShapedType type,
15441544
return get(type, manager.insert(blobName, std::move(blob)));
15451545
}
15461546

1547+
ArrayRef<char> DenseResourceElementsAttr::getData() {
1548+
if (AsmResourceBlob *blob = this->getRawHandle().getBlob())
1549+
return blob->getDataAs<char>();
1550+
return {};
1551+
}
1552+
15471553
//===----------------------------------------------------------------------===//
15481554
// DenseResourceElementsAttrBase
15491555

mlir/lib/Target/LLVM/ModuleToObject.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include "mlir/Target/LLVM/ModuleToObject.h"
1515

1616
#include "mlir/ExecutionEngine/OptUtils.h"
17+
#include "mlir/IR/BuiltinAttributeInterfaces.h"
18+
#include "mlir/IR/BuiltinAttributes.h"
1719
#include "mlir/IR/BuiltinOps.h"
1820
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
1921
#include "mlir/Target/LLVMIR/Export.h"
@@ -25,6 +27,7 @@
2527
#include "llvm/Linker/Linker.h"
2628
#include "llvm/MC/TargetRegistry.h"
2729
#include "llvm/Support/FileSystem.h"
30+
#include "llvm/Support/MemoryBuffer.h"
2831
#include "llvm/Support/Path.h"
2932
#include "llvm/Support/SourceMgr.h"
3033
#include "llvm/Support/raw_ostream.h"
@@ -93,6 +96,9 @@ LogicalResult ModuleToObject::loadBitcodeFilesFromList(
9396
SmallVector<std::unique_ptr<llvm::Module>> &llvmModules,
9497
bool failureOnError) {
9598
for (Attribute linkLib : librariesToLink) {
99+
// Attributes in this list can be either list of file paths using
100+
// StringAttr, or a resource attribute pointing to the LLVM bitcode in
101+
// memory.
96102
if (auto filePath = dyn_cast<StringAttr>(linkLib)) {
97103
// Test if the path exists, if it doesn't abort.
98104
if (!llvm::sys::fs::is_regular_file(filePath.strref())) {
@@ -107,6 +113,27 @@ LogicalResult ModuleToObject::loadBitcodeFilesFromList(
107113
return failure();
108114
continue;
109115
}
116+
if (auto blobAttr = dyn_cast<BlobAttr>(linkLib)) {
117+
// Load the file or abort on error.
118+
llvm::SMDiagnostic error;
119+
ArrayRef<char> data = blobAttr.getData();
120+
std::unique_ptr<llvm::MemoryBuffer> buffer =
121+
llvm::MemoryBuffer::getMemBuffer(StringRef(data.data(), data.size()),
122+
"blobLinkedLib",
123+
/*RequiresNullTerminator=*/false);
124+
std::unique_ptr<llvm::Module> mod =
125+
getLazyIRModule(std::move(buffer), error, context);
126+
if (mod) {
127+
if (failed(handleBitcodeFile(*mod)))
128+
return failure();
129+
llvmModules.push_back(std::move(mod));
130+
} else if (failureOnError) {
131+
getOperation().emitError()
132+
<< "Couldn't load LLVM library for linking: " << error.getMessage();
133+
return failure();
134+
}
135+
continue;
136+
}
110137
if (failureOnError) {
111138
getOperation().emitError()
112139
<< "Unknown attribute describing LLVM library to load: " << linkLib;

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ SerializeGPUModuleBase::SerializeGPUModuleBase(
100100
toolkitPath = getCUDAToolkitPath();
101101

102102
// Append the files in the target attribute.
103-
librariesToLink.append(target.getLink().begin(), target.getLink().end());
103+
if (target.getLink())
104+
librariesToLink.append(target.getLink().begin(), target.getLink().end());
104105

105106
// Append libdevice to the files to be loaded.
106107
(void)appendStandardLibs();

mlir/lib/Target/LLVM/ROCDL/Target.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ SerializeGPUModuleBase::SerializeGPUModuleBase(
104104
toolkitPath = getROCMPath();
105105

106106
// Append the files in the target attribute.
107-
librariesToLink.append(target.getLink().begin(), target.getLink().end());
107+
if (target.getLink())
108+
librariesToLink.append(target.getLink().begin(), target.getLink().end());
108109
}
109110

110111
void SerializeGPUModuleBase::init() {

mlir/unittests/Target/LLVM/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
set(LLVM_LINK_COMPONENTS nativecodegen)
22

3+
get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
4+
35
add_mlir_unittest(MLIRTargetLLVMTests
46
SerializeNVVMTarget.cpp
57
SerializeROCDLTarget.cpp
68
SerializeToLLVMBitcode.cpp
9+
DEPENDS
10+
${dialect_libs}
711
)
812

913
mlir_target_link_libraries(MLIRTargetLLVMTests

mlir/unittests/Target/LLVM/SerializeNVVMTarget.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,18 @@
1818
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
1919
#include "mlir/Target/LLVMIR/Dialect/NVVM/NVVMToLLVMIRTranslation.h"
2020

21+
#include "llvm/Bitcode/BitcodeWriter.h"
2122
#include "llvm/Config/llvm-config.h" // for LLVM_HAS_NVPTX_TARGET
2223
#include "llvm/IRReader/IRReader.h"
2324
#include "llvm/Support/MemoryBufferRef.h"
2425
#include "llvm/Support/Process.h"
26+
#include "llvm/Support/SourceMgr.h"
2527
#include "llvm/Support/TargetSelect.h"
2628
#include "llvm/Support/raw_ostream.h"
2729
#include "llvm/TargetParser/Host.h"
2830

2931
#include "gmock/gmock.h"
32+
#include <cstdint>
3033

3134
using namespace mlir;
3235

@@ -215,3 +218,82 @@ TEST_F(MLIRTargetLLVMNVVM,
215218
isaResult.clear();
216219
}
217220
}
221+
222+
// Test linking LLVM IR from a resource attribute.
223+
TEST_F(MLIRTargetLLVMNVVM, SKIP_WITHOUT_NVPTX(LinkedLLVMIRResource)) {
224+
MLIRContext context(registry);
225+
std::string moduleStr = R"mlir(
226+
gpu.module @nvvm_test {
227+
llvm.func @bar()
228+
llvm.func @nvvm_kernel(%arg0: f32) attributes {gpu.kernel, nvvm.kernel} {
229+
llvm.call @bar() : () -> ()
230+
llvm.return
231+
}
232+
}
233+
)mlir";
234+
// Provide the library to link as a serialized bitcode blob.
235+
SmallVector<char> bitcodeToLink;
236+
{
237+
std::string linkedLib = R"llvm(
238+
define void @bar() {
239+
ret void
240+
}
241+
)llvm";
242+
llvm::SMDiagnostic err;
243+
llvm::MemoryBufferRef buffer(linkedLib, "linkedLib");
244+
llvm::LLVMContext llvmCtx;
245+
std::unique_ptr<llvm::Module> module = llvm::parseIR(buffer, err, llvmCtx);
246+
ASSERT_TRUE(module) << " Can't parse IR: " << err.getMessage();
247+
{
248+
llvm::raw_svector_ostream os(bitcodeToLink);
249+
WriteBitcodeToFile(*module, os);
250+
}
251+
}
252+
253+
OwningOpRef<ModuleOp> module =
254+
parseSourceString<ModuleOp>(moduleStr, &context);
255+
ASSERT_TRUE(!!module);
256+
Builder builder(&context);
257+
258+
NVVM::NVVMTargetAttr target = NVVM::NVVMTargetAttr::get(&context);
259+
auto serializer = dyn_cast<gpu::TargetAttrInterface>(target);
260+
261+
// Hook to intercept the LLVM IR after linking external libs.
262+
std::string linkedLLVMIR;
263+
auto linkedCallback = [&linkedLLVMIR](llvm::Module &module) {
264+
llvm::raw_string_ostream ros(linkedLLVMIR);
265+
module.print(ros, nullptr);
266+
};
267+
268+
// Store the bitcode as a DenseI8ArrayAttr.
269+
SmallVector<Attribute> librariesToLink;
270+
librariesToLink.push_back(DenseI8ArrayAttr::get(
271+
&context,
272+
ArrayRef<int8_t>((int8_t *)bitcodeToLink.data(), bitcodeToLink.size())));
273+
gpu::TargetOptions options(
274+
{}, librariesToLink, {}, {},
275+
mlir::gpu::TargetOptions::getDefaultCompilationTarget(), {}, {},
276+
linkedCallback);
277+
for (auto gpuModule : (*module).getBody()->getOps<gpu::GPUModuleOp>()) {
278+
std::optional<SmallVector<char, 0>> object =
279+
serializer.serializeToObject(gpuModule, options);
280+
281+
// Verify that we correctly linked in the library: the external call is
282+
// replaced by the definition.
283+
ASSERT_TRUE(!linkedLLVMIR.empty());
284+
{
285+
llvm::SMDiagnostic err;
286+
llvm::MemoryBufferRef buffer(linkedLLVMIR, "linkedLLVMIR");
287+
llvm::LLVMContext llvmCtx;
288+
std::unique_ptr<llvm::Module> module =
289+
llvm::parseIR(buffer, err, llvmCtx);
290+
ASSERT_TRUE(module) << " Can't parse linkedLLVMIR: " << err.getMessage()
291+
<< " IR: \n\b" << linkedLLVMIR;
292+
llvm::Function *bar = module->getFunction("bar");
293+
ASSERT_TRUE(bar);
294+
ASSERT_FALSE(bar->empty());
295+
}
296+
ASSERT_TRUE(object != std::nullopt);
297+
ASSERT_TRUE(!object->empty());
298+
}
299+
}

0 commit comments

Comments
 (0)