|
18 | 18 | #include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h" |
19 | 19 | #include "mlir/Target/LLVMIR/Dialect/NVVM/NVVMToLLVMIRTranslation.h" |
20 | 20 |
|
| 21 | +#include "llvm/Bitcode/BitcodeWriter.h" |
21 | 22 | #include "llvm/Config/llvm-config.h" // for LLVM_HAS_NVPTX_TARGET |
22 | 23 | #include "llvm/IRReader/IRReader.h" |
23 | 24 | #include "llvm/Support/MemoryBufferRef.h" |
24 | 25 | #include "llvm/Support/Process.h" |
| 26 | +#include "llvm/Support/SourceMgr.h" |
25 | 27 | #include "llvm/Support/TargetSelect.h" |
26 | 28 | #include "llvm/Support/raw_ostream.h" |
27 | 29 | #include "llvm/TargetParser/Host.h" |
28 | 30 |
|
29 | 31 | #include "gmock/gmock.h" |
| 32 | +#include <cstdint> |
30 | 33 |
|
31 | 34 | using namespace mlir; |
32 | 35 |
|
@@ -215,3 +218,81 @@ TEST_F(MLIRTargetLLVMNVVM, |
215 | 218 | isaResult.clear(); |
216 | 219 | } |
217 | 220 | } |
| 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({}, librariesToLink, {}, {}, |
| 274 | + gpu::CompilationTarget::Assembly, {}, {}, |
| 275 | + linkedCallback); |
| 276 | + for (auto gpuModule : (*module).getBody()->getOps<gpu::GPUModuleOp>()) { |
| 277 | + std::optional<SmallVector<char, 0>> object = |
| 278 | + serializer.serializeToObject(gpuModule, options); |
| 279 | + |
| 280 | + // Verify that we correctly linked in the library: the external call is |
| 281 | + // replaced by the definition. |
| 282 | + ASSERT_TRUE(!linkedLLVMIR.empty()); |
| 283 | + { |
| 284 | + llvm::SMDiagnostic err; |
| 285 | + llvm::MemoryBufferRef buffer(linkedLLVMIR, "linkedLLVMIR"); |
| 286 | + llvm::LLVMContext llvmCtx; |
| 287 | + std::unique_ptr<llvm::Module> module = |
| 288 | + llvm::parseIR(buffer, err, llvmCtx); |
| 289 | + ASSERT_TRUE(module) << " Can't parse linkedLLVMIR: " << err.getMessage() |
| 290 | + << " IR: \n\b" << linkedLLVMIR; |
| 291 | + llvm::Function *bar = module->getFunction("bar"); |
| 292 | + ASSERT_TRUE(bar); |
| 293 | + ASSERT_FALSE(bar->empty()); |
| 294 | + } |
| 295 | + ASSERT_TRUE(object != std::nullopt); |
| 296 | + ASSERT_TRUE(!object->empty()); |
| 297 | + } |
| 298 | +} |
0 commit comments