Skip to content

Commit d4b3358

Browse files
committed
[clang-linker-wrapper] Add ELF packaging for spirv64-intel OpenMP images
Signed-off-by: Sarnie, Nick <[email protected]>
1 parent 0c7bd87 commit d4b3358

File tree

8 files changed

+128
-0
lines changed

8 files changed

+128
-0
lines changed
Binary file not shown.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Verify the ELF packaging of OpenMP SPIR-V device images.
2+
// REQUIRES: system-linux
3+
// REQUIRES: spirv-tools
4+
// RUN: mkdir -p %t_tmp
5+
// RUN: cd %t_tmp
6+
// RUN: not clang-linker-wrapper -o a.out %S/Inputs/clang-linker-wrapper-spirv-elf.o --save-temps --linker-path=ld
7+
// RUN: clang-offload-packager --image=triple=spirv64-intel,kind=openmp,file=%t.elf %t_tmp/a.out.openmp.image.wrapper.o
8+
// RUN: llvm-readelf -t %t.elf | FileCheck -check-prefix=CHECK-SECTION %s
9+
// RUN: llvm-readelf -n %t.elf | FileCheck -check-prefix=CHECK-NOTES %s
10+
11+
// CHECK-SECTION: .note.inteloneompoffload
12+
// CHECK-SECTION: __openmp_offload_spirv_0
13+
14+
// CHECK-NOTES-COUNT-3: INTELONEOMPOFFLOAD

clang/test/Tooling/lit.local.cfg

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
if not config.root.clang_staticanalyzer:
22
config.unsupported = True
3+
4+
if config.spirv_tools_tests:
5+
config.available_features.add("spirv-tools")
6+
config.substitutions.append(("spirv-dis", os.path.join(config.llvm_tools_dir, "spirv-dis")))
7+
config.substitutions.append(("spirv-val", os.path.join(config.llvm_tools_dir, "spirv-val")))
8+
config.substitutions.append(("spirv-as", os.path.join(config.llvm_tools_dir, "spirv-as")))

clang/test/lit.site.cfg.py.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ config.llvm_external_lit = path(r"@LLVM_EXTERNAL_LIT@")
4343
config.standalone_build = @CLANG_BUILT_STANDALONE@
4444
config.ppc_linux_default_ieeelongdouble = @PPC_LINUX_DEFAULT_IEEELONGDOUBLE@
4545
config.have_llvm_driver = @LLVM_TOOL_LLVM_DRIVER_BUILD@
46+
config.spirv_tools_tests = "@LLVM_INCLUDE_SPIRV_TOOLS_TESTS@"
4647
config.substitutions.append(("%llvm-version-major", "@LLVM_VERSION_MAJOR@"))
4748

4849
import lit.llvm

clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,17 @@ Expected<StringRef> linkDevice(ArrayRef<StringRef> InputFiles,
605605
}
606606
}
607607

608+
Error containerizeRawImage(std::unique_ptr<MemoryBuffer> &Img, OffloadKind Kind,
609+
const ArgList &Args) {
610+
llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
611+
if (Kind != OFK_OpenMP || !Triple.isSPIRV() ||
612+
Triple.getVendor() != llvm::Triple::Intel)
613+
return Error::success();
614+
if (Error E = offloading::intel::containerizeOpenMPSPIRVImage(Img))
615+
return E;
616+
return Error::success();
617+
}
618+
608619
Expected<StringRef> writeOffloadFile(const OffloadFile &File) {
609620
const OffloadBinary &Binary = *File.getBinary();
610621

@@ -960,6 +971,10 @@ Expected<SmallVector<StringRef>> linkAndWrapDeviceFiles(
960971
return createFileError(*OutputOrErr, EC);
961972
}
962973

974+
// Manually containerize offloading images not in ELF format.
975+
if (Error E = containerizeRawImage(*FileOrErr, Kind, LinkerArgs))
976+
return E;
977+
963978
std::scoped_lock<decltype(ImageMtx)> Guard(ImageMtx);
964979
OffloadingImage TheImage{};
965980
TheImage.TheImageKind =

llvm/include/llvm/Frontend/Offloading/Utility.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLVM_FRONTEND_OFFLOADING_UTILITY_H
1111

1212
#include <cstdint>
13+
#include <memory>
1314

1415
#include "llvm/ADT/StringMap.h"
1516
#include "llvm/ADT/StringRef.h"
@@ -152,6 +153,11 @@ Error getAMDGPUMetaDataFromImage(MemoryBufferRef MemBuffer,
152153
StringMap<AMDGPUKernelMetaData> &KernelInfoMap,
153154
uint16_t &ELFABIVersion);
154155
} // namespace amdgpu
156+
namespace intel {
157+
/// Containerizes an offloading binary into the ELF binary format expected by
158+
/// the Intel runtime offload plugin.
159+
Error containerizeOpenMPSPIRVImage(std::unique_ptr<MemoryBuffer> &Binary);
160+
} // namespace intel
155161
} // namespace offloading
156162
} // namespace llvm
157163

llvm/lib/Frontend/Offloading/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_llvm_component_library(LLVMFrontendOffloading
1212
Core
1313
BinaryFormat
1414
Object
15+
ObjectYAML
1516
Support
1617
TransformUtils
1718
TargetParser

llvm/lib/Frontend/Offloading/Utility.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include "llvm/IR/GlobalVariable.h"
1616
#include "llvm/IR/Value.h"
1717
#include "llvm/Object/ELFObjectFile.h"
18+
#include "llvm/ObjectYAML/ELFYAML.h"
19+
#include "llvm/ObjectYAML/yaml2obj.h"
1820
#include "llvm/Support/MemoryBufferRef.h"
1921
#include "llvm/Transforms/Utils/ModuleUtils.h"
2022

@@ -373,3 +375,86 @@ Error llvm::offloading::amdgpu::getAMDGPUMetaDataFromImage(
373375
}
374376
return Error::success();
375377
}
378+
Error offloading::intel::containerizeOpenMPSPIRVImage(
379+
std::unique_ptr<MemoryBuffer> &Img) {
380+
constexpr char INTEL_ONEOMP_OFFLOAD_VERSION[] = "1.0";
381+
constexpr int NT_INTEL_ONEOMP_OFFLOAD_VERSION = 1;
382+
constexpr int NT_INTEL_ONEOMP_OFFLOAD_IMAGE_COUNT = 2;
383+
constexpr int NT_INTEL_ONEOMP_OFFLOAD_IMAGE_AUX = 3;
384+
385+
// Start creating notes for the ELF container.
386+
std::vector<ELFYAML::NoteEntry> Notes;
387+
std::string Version = toHex(INTEL_ONEOMP_OFFLOAD_VERSION);
388+
Notes.emplace_back(ELFYAML::NoteEntry{"INTELONEOMPOFFLOAD",
389+
yaml::BinaryRef(Version),
390+
NT_INTEL_ONEOMP_OFFLOAD_VERSION});
391+
392+
// The AuxInfo string will hold auxiliary information for the image.
393+
// ELFYAML::NoteEntry structures will hold references to the
394+
// string, so we have to make sure the string is valid.
395+
std::string AuxInfo;
396+
397+
// TODO: Pass compile/link opts
398+
StringRef CompileOpts = "";
399+
StringRef LinkOpts = "";
400+
401+
unsigned ImageFmt = 1; // SPIR-V format
402+
403+
AuxInfo = toHex((Twine(0) + Twine('\0') + Twine(ImageFmt) + Twine('\0') +
404+
CompileOpts + Twine('\0') + LinkOpts)
405+
.str());
406+
Notes.emplace_back(ELFYAML::NoteEntry{"INTELONEOMPOFFLOAD",
407+
yaml::BinaryRef(AuxInfo),
408+
NT_INTEL_ONEOMP_OFFLOAD_IMAGE_AUX});
409+
410+
std::string ImgCount = toHex(Twine(1).str()); // always one image per ELF
411+
Notes.emplace_back(ELFYAML::NoteEntry{"INTELONEOMPOFFLOAD",
412+
yaml::BinaryRef(ImgCount),
413+
NT_INTEL_ONEOMP_OFFLOAD_IMAGE_COUNT});
414+
415+
std::string YamlFile;
416+
llvm::raw_string_ostream YamlFileStream(YamlFile);
417+
418+
// Write YAML template file.
419+
{
420+
// We use 64-bit little-endian ELF currently.
421+
ELFYAML::FileHeader Header{};
422+
Header.Class = ELF::ELFCLASS64;
423+
Header.Data = ELF::ELFDATA2LSB;
424+
Header.Type = ELF::ET_DYN;
425+
// Use an existing Intel machine type as there is not one specifically for
426+
// Intel GPUs.
427+
Header.Machine = ELF::EM_IA_64;
428+
429+
// Create a section with notes.
430+
ELFYAML::NoteSection Section{};
431+
Section.Type = ELF::SHT_NOTE;
432+
Section.AddressAlign = 0;
433+
Section.Name = ".note.inteloneompoffload";
434+
Section.Notes.emplace(std::move(Notes));
435+
436+
ELFYAML::Object Object{};
437+
Object.Header = Header;
438+
Object.Chunks.push_back(
439+
std::make_unique<ELFYAML::NoteSection>(std::move(Section)));
440+
441+
// Create the section that will hold the image
442+
ELFYAML::RawContentSection ImageSection{};
443+
ImageSection.Type = ELF::SHT_PROGBITS;
444+
ImageSection.AddressAlign = 0;
445+
std::string Name = "__openmp_offload_spirv_0";
446+
ImageSection.Name = Name;
447+
ImageSection.Content =
448+
llvm::yaml::BinaryRef(arrayRefFromStringRef(Img->getBuffer()));
449+
Object.Chunks.push_back(
450+
std::make_unique<ELFYAML::RawContentSection>(std::move(ImageSection)));
451+
Error Err = Error::success();
452+
llvm::yaml::yaml2elf(
453+
Object, YamlFileStream,
454+
[&Err](const Twine &Msg) { Err = createStringError(Msg); }, UINT64_MAX);
455+
if (Err)
456+
return Err;
457+
}
458+
Img = MemoryBuffer::getMemBufferCopy(YamlFile);
459+
return Error::success();
460+
}

0 commit comments

Comments
 (0)