diff --git a/llvm/test/tools/llvm-objdump/Offloading/Inputs/binary.yaml b/llvm/test/tools/llvm-objdump/Offloading/Inputs/binary.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Offloading/Inputs/binary.yaml @@ -0,0 +1,34 @@ +!Offload +Members: + - ImageKind: "bc" + OffloadKind: "openmp" + Flags: 0 + String: + - Key: "triple" + Value: "amdgcn-amd-amdhsa" + - Key: "arch" + Value: "gfx908" + - ImageKind: "bc" + OffloadKind: "openmp" + Flags: 0 + String: + - Key: "triple" + Value: "amdgcn-amd-amdhsa" + - Key: "arch" + Value: "gfx90a" + - ImageKind: "cubin" + OffloadKind: "openmp" + Flags: 0 + String: + - Key: "triple" + Value: "nvptx64-nvidia-cuda" + - Key: "arch" + Value: "sm_52" + - ImageKind: "cubin" + OffloadKind: "openmp" + Flags: 0 + String: + - Key: "triple" + Value: "nvptx64-nvidia-cuda" + - Key: "arch" + Value: "sm_70" diff --git a/llvm/test/tools/llvm-objdump/Offloading/binary.test b/llvm/test/tools/llvm-objdump/Offloading/binary.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Offloading/binary.test @@ -0,0 +1,26 @@ +# RUN: yaml2obj %S/Inputs/binary.yaml -o %t +# RUN: llvm-objdump --offloading %t | FileCheck %s --strict-whitespace --implicit-check-not={{.}} + +# CHECK: OFFLOADING IMAGE [0]: +# CHECK-NEXT: kind llvm ir +# CHECK-NEXT: arch gfx908 +# CHECK-NEXT: triple amdgcn-amd-amdhsa +# CHECK-NEXT: producer openmp +# CHECK-EMPTY: +# CHECK-NEXT: OFFLOADING IMAGE [1]: +# CHECK-NEXT: kind llvm ir +# CHECK-NEXT: arch gfx90a +# CHECK-NEXT: triple amdgcn-amd-amdhsa +# CHECK-NEXT: producer openmp +# CHECK-EMPTY: +# CHECK-NEXT: OFFLOADING IMAGE [2]: +# CHECK-NEXT: kind cubin +# CHECK-NEXT: arch sm_52 +# CHECK-NEXT: triple nvptx64-nvidia-cuda +# CHECK-NEXT: producer openmp +# CHECK-EMPTY: +# CHECK-NEXT: OFFLOADING IMAGE [3]: +# CHECK-NEXT: kind cubin +# CHECK-NEXT: arch sm_70 +# CHECK-NEXT: triple nvptx64-nvidia-cuda +# CHECK-NEXT: producer openmp diff --git a/llvm/test/tools/llvm-objdump/Offloading/elf.test b/llvm/test/tools/llvm-objdump/Offloading/elf.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Offloading/elf.test @@ -0,0 +1,36 @@ +# RUN: yaml2obj %S/Inputs/binary.yaml -o %t.bin +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy --add-section .llvm.offloading=%t.bin %t +# RUN: llvm-objdump --offloading %t | FileCheck %s --strict-whitespace --implicit-check-not={{.}} + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + +# CHECK: {{.*}}file format elf64-unknown +# CHECK-EMPTY: +# CHECK-NEXT: OFFLOADING IMAGE [0]: +# CHECK-NEXT: kind llvm ir +# CHECK-NEXT: arch gfx908 +# CHECK-NEXT: triple amdgcn-amd-amdhsa +# CHECK-NEXT: producer openmp +# CHECK-EMPTY: +# CHECK-NEXT: OFFLOADING IMAGE [1]: +# CHECK-NEXT: kind llvm ir +# CHECK-NEXT: arch gfx90a +# CHECK-NEXT: triple amdgcn-amd-amdhsa +# CHECK-NEXT: producer openmp +# CHECK-EMPTY: +# CHECK-NEXT: OFFLOADING IMAGE [2]: +# CHECK-NEXT: kind cubin +# CHECK-NEXT: arch sm_52 +# CHECK-NEXT: triple nvptx64-nvidia-cuda +# CHECK-NEXT: producer openmp +# CHECK-EMPTY: +# CHECK-NEXT: OFFLOADING IMAGE [3]: +# CHECK-NEXT: kind cubin +# CHECK-NEXT: arch sm_70 +# CHECK-NEXT: triple nvptx64-nvidia-cuda +# CHECK-NEXT: producer openmp diff --git a/llvm/test/tools/llvm-objdump/Offloading/failure.test b/llvm/test/tools/llvm-objdump/Offloading/failure.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/Offloading/failure.test @@ -0,0 +1,17 @@ +# RUN: yaml2obj %s -o %t +# RUN: not llvm-objdump --offloading %t 2>&1 | FileCheck %s + +!ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .llvm.offloading + Type: SHT_PROGBITS + Flags: [ SHF_EXCLUDE ] + Address: 0x0 + AddressAlign: 0x0000000000000008 + Content: "10ffb0ad" + +# CHECK: Invalid data was encountered while parsing the file diff --git a/llvm/tools/llvm-objdump/CMakeLists.txt b/llvm/tools/llvm-objdump/CMakeLists.txt --- a/llvm/tools/llvm-objdump/CMakeLists.txt +++ b/llvm/tools/llvm-objdump/CMakeLists.txt @@ -28,6 +28,7 @@ COFFDump.cpp ELFDump.cpp MachODump.cpp + OffloadDump.cpp WasmDump.cpp XCOFFDump.cpp DEPENDS diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td --- a/llvm/tools/llvm-objdump/ObjdumpOpts.td +++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td @@ -81,6 +81,9 @@ def fault_map_section : Flag<["--"], "fault-map-section">, HelpText<"Display the content of the fault map section">; +def offloading : Flag<["--"], "offloading">, + HelpText<"Display the content of the offloading section">; + def file_headers : Flag<["--"], "file-headers">, HelpText<"Display the contents of the overall file header">; def : Flag<["-"], "f">, Alias, diff --git a/llvm/tools/llvm-objdump/OffloadDump.h b/llvm/tools/llvm-objdump/OffloadDump.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-objdump/OffloadDump.h @@ -0,0 +1,22 @@ +//===-- OffloadDump.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_OBJDUMP_OFFLOADDUMP_H +#define LLVM_TOOLS_LLVM_OBJDUMP_OFFLOADDUMP_H + +#include "llvm/Object/ObjectFile.h" +#include "llvm/Object/OffloadBinary.h" + +namespace llvm { + +void dumpOffloadSections(const object::OffloadBinary *OB); +void dumpOffloadBinary(const object::ObjectFile *O); + +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-objdump/OffloadDump.cpp b/llvm/tools/llvm-objdump/OffloadDump.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-objdump/OffloadDump.cpp @@ -0,0 +1,100 @@ +//===-- OffloadDump.cpp - Offloading dumper ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements the offloading-specific dumper for llvm-objdump. +/// +//===----------------------------------------------------------------------===// +#include "OffloadDump.h" +#include "llvm-objdump.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::objdump; + +#define OFFLOAD_SECTION_MAGIC_STR ".llvm.offloading" + +/// Get the printable name of the image kind. +static StringRef getImageName(const OffloadBinary *OB) { + switch (OB->getImageKind()) { + case IMG_Object: + return "elf"; + case IMG_Bitcode: + return "llvm ir"; + case IMG_Cubin: + return "cubin"; + case IMG_Fatbinary: + return "fatbinary"; + case IMG_PTX: + return "ptx"; + default: + return ""; + } +} + +static void printBinary(const OffloadBinary *OB, uint64_t Index) { + outs() << "\nOFFLOADING IMAGE " + << "[" << Index << "]:\n"; + outs() << left_justify("kind", 16) << getImageName(OB) << "\n"; + outs() << left_justify("arch", 16) << OB->getArch() << "\n"; + outs() << left_justify("triple", 16) << OB->getTriple() << "\n"; + outs() << left_justify("producer", 16) + << getOffloadKindName(OB->getOffloadKind()) << "\n"; +} + +static Error visitAllBinaries(const OffloadBinary *OB) { + uint64_t Offset = 0; + uint64_t Index = 0; + while (Offset < OB->getMemoryBufferRef().getBufferSize()) { + MemoryBufferRef Buffer = + MemoryBufferRef(OB->getData().drop_front(Offset), OB->getFileName()); + auto BinaryOrErr = OffloadBinary::create(Buffer); + if (!BinaryOrErr) + return BinaryOrErr.takeError(); + + OffloadBinary &Binary = **BinaryOrErr; + + printBinary(&Binary, Index++); + + Offset += Binary.getSize(); + } + return Error::success(); +} + +/// Print the embedded offloading contents of an ObjectFile \p O. +void llvm::dumpOffloadBinary(const ObjectFile *O) { + for (const SectionRef &Sec : O->sections()) { + Expected Name = Sec.getName(); + if (!Name || !Name->startswith(OFFLOAD_SECTION_MAGIC_STR)) + continue; + + Expected Contents = Sec.getContents(); + if (!Contents) + reportError(Contents.takeError(), O->getFileName()); + + MemoryBufferRef Buffer = MemoryBufferRef(*Contents, O->getFileName()); + auto BinaryOrErr = OffloadBinary::create(Buffer); + if (!BinaryOrErr) + reportError(BinaryOrErr.takeError(), O->getFileName()); + OffloadBinary &Binary = **BinaryOrErr; + + // Print out all the binaries that could be contained here. If we fail to + // find one after the first, just give up instead of throwing an error. + if (Error Err = visitAllBinaries(&Binary)) + consumeError(std::move(Err)); + } +} + +/// Print the contents of an offload binary file \p OB. This may contain +/// multiple binaries stored in the same buffer. +void llvm::dumpOffloadSections(const OffloadBinary *OB) { + // Print out all the binaries that could be contained here. If we fail to find + // one after the first, just give up instead of throwing an error. + if (Error Err = visitAllBinaries(OB)) + consumeError(std::move(Err)); +} diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -20,6 +20,7 @@ #include "ELFDump.h" #include "MachODump.h" #include "ObjdumpOptID.h" +#include "OffloadDump.h" #include "SourcePrinter.h" #include "WasmDump.h" #include "XCOFFDump.h" @@ -58,6 +59,7 @@ #include "llvm/Object/MachO.h" #include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/OffloadBinary.h" #include "llvm/Object/Wasm.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -198,6 +200,7 @@ std::vector objdump::MAttrs; bool objdump::ShowRawInsn; bool objdump::LeadingAddr; +static bool Offloading; static bool RawClangAST; bool objdump::Relocations; bool objdump::PrintImmHex; @@ -2479,6 +2482,8 @@ printRawClangAST(O); if (FaultMapSection) printFaultMaps(O); + if (Offloading) + dumpOffloadBinary(O); } static void dumpObject(const COFFImportFile *I, const Archive *A, @@ -2542,6 +2547,8 @@ dumpObject(O); else if (MachOUniversalBinary *UB = dyn_cast(&Binary)) parseInputMachO(UB); + else if (OffloadBinary *OB = dyn_cast(&Binary)) + dumpOffloadSections(OB); else reportError(errorCodeToError(object_error::invalid_file_type), file); } @@ -2645,6 +2652,7 @@ } DynamicRelocations = InputArgs.hasArg(OBJDUMP_dynamic_reloc); FaultMapSection = InputArgs.hasArg(OBJDUMP_fault_map_section); + Offloading = InputArgs.hasArg(OBJDUMP_offloading); FileHeaders = InputArgs.hasArg(OBJDUMP_file_headers); SectionContents = InputArgs.hasArg(OBJDUMP_full_contents); PrintLines = InputArgs.hasArg(OBJDUMP_line_numbers); @@ -2812,7 +2820,7 @@ if (!ArchiveHeaders && !Disassemble && DwarfDumpType == DIDT_Null && !DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST && !Relocations && !SectionHeaders && !SectionContents && !SymbolTable && - !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && + !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && !Offloading && !(MachOOpt && (Bind || DataInCode || DyldInfo || DylibId || DylibsUsed || ExportsTrie || FirstPrivateHeader || FunctionStarts || IndirectSymbols || InfoPlist || LazyBind || LinkOptHints ||