Index: lib/Target/AMDGPU/Disassembler/CMakeLists.txt =================================================================== --- lib/Target/AMDGPU/Disassembler/CMakeLists.txt +++ lib/Target/AMDGPU/Disassembler/CMakeLists.txt @@ -2,6 +2,8 @@ add_llvm_library(LLVMAMDGPUDisassembler AMDGPUDisassembler.cpp + HSAObjCodeDisassembler.cpp ) add_dependencies(LLVMAMDGPUDisassembler AMDGPUCommonTableGen) +add_dependencies(LLVMAMDGPUDisassembler AMDGPUUtils) Index: lib/Target/AMDGPU/Disassembler/HSAObjCodeDisassembler.cpp =================================================================== --- /dev/null +++ lib/Target/AMDGPU/Disassembler/HSAObjCodeDisassembler.cpp @@ -0,0 +1,230 @@ +//===--------------------- HSAObjCodeDisassembler.cpp ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +//===----------------------------------------------------------------------===// +// +/// \file - disassebly of HSA Code Object file. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/AMDGPUMCTargetDesc.h" +#include "Utils/AMDKernelCodeTUtils.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include +#include +#include +#include +#include +#include "llvm/MC/MCRegisterInfo.h" +#include +#include + + +using namespace llvm; +using namespace object; + +class HSACodeObject { +public: + typedef ELF64LEObjectFile MyELF; + const MyELF& Obj; + + HSACodeObject(const MyELF *Obj_) : Obj(*Obj_) {} + + std::pair> + getKernel(SymbolRef Symbol) const; + +private: + mutable SmallVector StartMarkers; + + const decltype(StartMarkers)& getStartMarkers() const; +}; + +std::pair> +HSACodeObject::getKernel(SymbolRef Symbol) const { + // TODO: ELF error handling + + auto ElfSym = Obj.getSymbol(Symbol.getRawDataRefImpl()); + if (ElfSym->getType() != ELF::STT_AMDGPU_HSA_KERNEL) { + return decltype(this->getKernel(Symbol))(); + } + + auto ELF = Obj.getELFFile(); + auto SecBytes = *ELF->getSectionContentsAsArray( + *ELF->getSection(ElfSym->st_shndx)); + + uint64_t const Ofs = ElfSym->getValue(); + auto const C = + reinterpret_cast(SecBytes.slice(Ofs).begin()); + + auto& M = getStartMarkers(); + uint64_t const CodeStart = Ofs + C->kernel_code_entry_byte_offset; + uint64_t const CodeEnd = *std::upper_bound(M.begin(), M.end(), CodeStart); + + return std::make_pair(C, SecBytes.slice(CodeStart, CodeEnd - CodeStart)); +} + +// StartMarkers is a sorted array of every entity's begin in the code section. +// We determine an entity's ending using upper bound on this array because +// there'is no kernel code size specified by design +const decltype(HSACodeObject::StartMarkers)& +HSACodeObject::getStartMarkers() const { + // TODO: ELF error handling + + if (!StartMarkers.empty()) return StartMarkers; + + auto ELF = Obj.getELFFile(); + int HsaTextSecIdx = -1; + ArrayRef SecBytes; + + for (auto& Symbol : Obj.symbols()) { + auto ElfSym = Obj.getSymbol(Symbol.getRawDataRefImpl()); + if (HsaTextSecIdx != -1 && ElfSym->st_shndx != HsaTextSecIdx) continue; + switch (ElfSym->getType()) { + case ELF::STT_AMDGPU_HSA_KERNEL: { + if (HsaTextSecIdx == -1) { + HsaTextSecIdx = ElfSym->st_shndx; + SecBytes = *ELF->getSectionContentsAsArray( + *ELF->getSection(HsaTextSecIdx)); + } + uint64_t Ofs = ElfSym->getValue(); + StartMarkers.push_back(Ofs); + auto C = reinterpret_cast( + SecBytes.slice(Ofs).begin()); + StartMarkers.push_back(Ofs + C->kernel_code_entry_byte_offset); + break; + } + default: break; + }; + } + + StartMarkers.push_back(SecBytes.size()); + array_pod_sort(StartMarkers.begin(), StartMarkers.end()); + + return StartMarkers; +} + +class OwningMCContext : MCContext { + std::unique_ptr MRI; + std::unique_ptr AsmInfo; + + OwningMCContext(decltype(MRI)&& MRI_, + decltype(AsmInfo)&& AsmInfo_) + : MCContext(AsmInfo_.get(), MRI_.get(), nullptr) + , MRI(std::move(MRI_)) + , AsmInfo(std::move(AsmInfo_)) {} + + friend std::unique_ptr createMCContext( + const Target *TheTarget, + StringRef TripleName); +}; + +std::unique_ptr createMCContext(const Target *TheTarget, + StringRef TripleName) { + decltype(OwningMCContext::MRI) + MRI(TheTarget->createMCRegInfo(TripleName)); + if (!MRI) + report_fatal_error("error: no register info for target " + TripleName); + + decltype(OwningMCContext::AsmInfo) + AsmInfo(TheTarget->createMCAsmInfo(*MRI, TripleName)); + if (!AsmInfo) + report_fatal_error("error: no assembly info for target " + TripleName); + + return std::unique_ptr( + new OwningMCContext(std::move(MRI), std::move(AsmInfo))); +} + +namespace llvm { + void DisassembleHSACodeObject(const ObjectFile *Obj); +} + +void llvm::DisassembleHSACodeObject(const ObjectFile *Obj) +{ +#ifdef NDEBUG + bool const DebugFlag = false; +#endif + + // TODO: should we report error, at least false? + if (Obj->getArch() != Triple::amdgcn) return; + + const Target * const TheTarget = &TheGCNTarget; + StringRef const TripleName = "amdgpu-unknown-unknown"; + + auto Ctx = createMCContext(TheTarget, TripleName); + + // MCDisassembler + // TODO: get CPU name from file + std::unique_ptr + STI(TheTarget->createMCSubtargetInfo(TripleName, "fiji", "")); + if (!STI) + report_fatal_error("error: no subtarget info for target " + TripleName); + + std::unique_ptr + DisAsm(TheTarget->createMCDisassembler(*STI, *Ctx)); + if (!DisAsm) + report_fatal_error("error: no disassembler for target " + TripleName); + + // MCInstPrinter + std::unique_ptr MII(TheTarget->createMCInstrInfo()); + if (!MII) + report_fatal_error("error: no instruction info for target " + TripleName); + + std::unique_ptr + IP(TheTarget->createMCInstPrinter(Triple(TripleName), + Ctx->getAsmInfo()->getAssemblerDialect(), + *Ctx->getAsmInfo(), *MII, *Ctx->getRegisterInfo())); + if (!IP) + report_fatal_error("error: no instruction printer for target " + TripleName); + + // TODO: get this out of obj file + outs() << ".hsa_code_object_version 1, 0\n"; + outs() << ".hsa_code_object_isa 7, 0, 0, \"AMD\", \"AMDGPU\"\n\n"; + + outs() << ".hsatext\n"; + + HSACodeObject CObj(static_cast(Obj)); + for (auto Symbol : Obj->symbols()) { // Disassemble symbol by symbol. + auto Kernel = CObj.getKernel(Symbol); + if (!Kernel.first) continue; + + outs() << ".amdgpu_hsa_kernel " << *Symbol.getName() << "\n"; + outs() << *Symbol.getName() << ":\n"; + outs() << " .amd_kernel_code_t\n"; + dumpAmdKernelCode(Kernel.first, outs(), " "); + outs() << " .end_amd_kernel_code_t\n"; + + auto& Code = Kernel.second; + if (Code.empty()) continue; + + uint64_t Index = 0; + while(Index < Code.size()) { + MCInst Inst; uint64_t EatenBytesNum = 0; + if (!DisAsm->getInstruction(Inst, EatenBytesNum, + Code.slice(Index), Index, + DebugFlag ? dbgs() : nulls(), + nulls())) { + auto BadBytes = Code.slice(Index, + EatenBytesNum ? (int)EatenBytesNum : 4); + assert((BadBytes.size() % 4) == 0); + for (auto D : makeArrayRef((const uint32_t*)BadBytes.begin(), + (const uint32_t*)BadBytes.end())) { + outs() << " //.dword " << IP->formatHex((uint64_t)D) << '\n'; + } + Index += BadBytes.size(); + continue; + } + // TODO: address: inst dwords annotation + IP->printInst(&Inst, outs(), "", *STI); + outs() << "\n"; + Index += EatenBytesNum; + } + outs() << "\n"; + } +} Index: tools/llvm-objdump/llvm-objdump.cpp =================================================================== --- tools/llvm-objdump/llvm-objdump.cpp +++ tools/llvm-objdump/llvm-objdump.cpp @@ -830,7 +830,17 @@ return false; } +namespace llvm { +void DisassembleHSACodeObject(const ObjectFile *Obj); +} + static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { + + if (Obj->getArch() == Triple::amdgcn) { + DisassembleHSACodeObject(Obj); + return; + } + const Target *TheTarget = getTarget(Obj); // Package up features to be passed to target/subtarget