| //===- llvm/unittest/unittests/MC/AMDGPU/Disassembler.cpp -----------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm-c/Disassembler.h" |
| #include "llvm/MC/MCAsmInfo.h" |
| #include "llvm/MC/MCContext.h" |
| #include "llvm/MC/MCDisassembler/MCDisassembler.h" |
| #include "llvm/MC/MCDisassembler/MCSymbolizer.h" |
| #include "llvm/MC/MCInst.h" |
| #include "llvm/MC/MCInstPrinter.h" |
| #include "llvm/MC/MCInstrInfo.h" |
| #include "llvm/MC/MCRegisterInfo.h" |
| #include "llvm/MC/MCSubtargetInfo.h" |
| #include "llvm/MC/MCSymbol.h" |
| #include "llvm/MC/MCTargetOptions.h" |
| #include "llvm/MC/TargetRegistry.h" |
| #include "llvm/Support/TargetSelect.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| |
| static const char *symbolLookupCallback(void *DisInfo, uint64_t ReferenceValue, |
| uint64_t *ReferenceType, |
| uint64_t ReferencePC, |
| const char **ReferenceName) { |
| *ReferenceType = LLVMDisassembler_ReferenceType_InOut_None; |
| return nullptr; |
| } |
| |
| static const char *TripleName = "amdgcn--amdpal"; |
| static const char *CPUName = "gfx1030"; |
| |
| // Basic smoke test. |
| TEST(AMDGPUDisassembler, Basic) { |
| LLVMInitializeAMDGPUTargetInfo(); |
| LLVMInitializeAMDGPUTargetMC(); |
| LLVMInitializeAMDGPUDisassembler(); |
| |
| uint8_t Bytes[] = {0x04, 0x00, 0x80, 0xb0}; |
| uint8_t *BytesP = Bytes; |
| const char OutStringSize = 100; |
| char OutString[OutStringSize]; |
| LLVMDisasmContextRef DCR = LLVMCreateDisasmCPU( |
| TripleName, CPUName, nullptr, 0, nullptr, symbolLookupCallback); |
| |
| // Skip test if AMDGPU not built. |
| if (!DCR) |
| GTEST_SKIP(); |
| |
| size_t InstSize; |
| unsigned NumBytes = sizeof(Bytes); |
| unsigned PC = 0U; |
| |
| InstSize = LLVMDisasmInstruction(DCR, BytesP, NumBytes, PC, OutString, |
| OutStringSize); |
| EXPECT_EQ(InstSize, 4U); |
| EXPECT_EQ(StringRef(OutString), "\ts_version UC_VERSION_GFX10"); |
| |
| LLVMDisasmDispose(DCR); |
| } |
| |
| // Check multiple disassemblers in same MCContext. |
| TEST(AMDGPUDisassembler, MultiDisassembler) { |
| LLVMInitializeAMDGPUTargetInfo(); |
| LLVMInitializeAMDGPUTargetMC(); |
| LLVMInitializeAMDGPUDisassembler(); |
| |
| std::string Error; |
| const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error); |
| |
| // Skip test if AMDGPU not built. |
| if (!TheTarget) |
| GTEST_SKIP(); |
| |
| std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); |
| std::unique_ptr<MCAsmInfo> MAI( |
| TheTarget->createMCAsmInfo(*MRI, TripleName, MCTargetOptions())); |
| std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo()); |
| std::unique_ptr<MCSubtargetInfo> STI( |
| TheTarget->createMCSubtargetInfo(TripleName, CPUName, "")); |
| auto Ctx = std::make_unique<MCContext>(Triple(TripleName), MAI.get(), |
| MRI.get(), STI.get()); |
| |
| int AsmPrinterVariant = MAI->getAssemblerDialect(); |
| std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter( |
| Triple(TripleName), AsmPrinterVariant, *MAI, *MII, *MRI)); |
| |
| SmallVector<char, 64> InsnStr, AnnoStr; |
| raw_svector_ostream OS(InsnStr); |
| raw_svector_ostream Annotations(AnnoStr); |
| formatted_raw_ostream FormattedOS(OS); |
| |
| char StrBuffer[128]; |
| |
| uint8_t Bytes[] = {0x04, 0x00, 0x80, 0xb0}; |
| uint64_t InstSize = 0U; |
| MCInst Inst1, Inst2; |
| MCDisassembler::DecodeStatus Status; |
| |
| // Test disassembler works as expected. |
| AnnoStr.clear(); |
| InsnStr.clear(); |
| std::unique_ptr<MCDisassembler> DisAsm1( |
| TheTarget->createMCDisassembler(*STI, *Ctx)); |
| Status = DisAsm1->getInstruction(Inst1, InstSize, Bytes, 0, Annotations); |
| ASSERT_TRUE(Status == MCDisassembler::Success); |
| EXPECT_EQ(InstSize, 4U); |
| |
| IP->printInst(&Inst1, 0U, Annotations.str(), *STI, FormattedOS); |
| ASSERT_TRUE(InsnStr.size() < (sizeof(StrBuffer) - 1)); |
| std::memcpy(StrBuffer, InsnStr.data(), InsnStr.size()); |
| StrBuffer[InsnStr.size()] = '\0'; |
| EXPECT_EQ(StringRef(StrBuffer), "\ts_version UC_VERSION_GFX10"); |
| |
| // Test that second disassembler in same context works as expected. |
| AnnoStr.clear(); |
| InsnStr.clear(); |
| std::unique_ptr<MCDisassembler> DisAsm2( |
| TheTarget->createMCDisassembler(*STI, *Ctx)); |
| Status = DisAsm2->getInstruction(Inst2, InstSize, Bytes, 0, Annotations); |
| ASSERT_TRUE(Status == MCDisassembler::Success); |
| EXPECT_EQ(InstSize, 4U); |
| |
| IP->printInst(&Inst2, 0U, Annotations.str(), *STI, FormattedOS); |
| ASSERT_TRUE(InsnStr.size() < (sizeof(StrBuffer) - 1)); |
| std::memcpy(StrBuffer, InsnStr.data(), InsnStr.size()); |
| StrBuffer[InsnStr.size()] = '\0'; |
| EXPECT_EQ(StringRef(StrBuffer), "\ts_version UC_VERSION_GFX10"); |
| } |
| |
| // Test UC_VERSION symbols can be overriden without crashing. |
| // There is no valid behaviour if symbols are redefined in this way. |
| TEST(AMDGPUDisassembler, UCVersionOverride) { |
| LLVMInitializeAMDGPUTargetInfo(); |
| LLVMInitializeAMDGPUTargetMC(); |
| LLVMInitializeAMDGPUDisassembler(); |
| |
| std::string Error; |
| const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error); |
| |
| // Skip test if AMDGPU not built. |
| if (!TheTarget) |
| GTEST_SKIP(); |
| |
| std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); |
| std::unique_ptr<MCAsmInfo> MAI( |
| TheTarget->createMCAsmInfo(*MRI, TripleName, MCTargetOptions())); |
| std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo()); |
| std::unique_ptr<MCSubtargetInfo> STI( |
| TheTarget->createMCSubtargetInfo(TripleName, CPUName, "")); |
| auto Ctx = std::make_unique<MCContext>(Triple(TripleName), MAI.get(), |
| MRI.get(), STI.get()); |
| |
| // Define custom UC_VERSION before initializing disassembler. |
| const uint8_t UC_VERSION_GFX10_DEFAULT = 0x04; |
| const uint8_t UC_VERSION_GFX10_NEW = 0x99; |
| auto Sym = Ctx->getOrCreateSymbol("UC_VERSION_GFX10"); |
| Sym->setVariableValue(MCConstantExpr::create(UC_VERSION_GFX10_NEW, *Ctx)); |
| |
| int AsmPrinterVariant = MAI->getAssemblerDialect(); |
| std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter( |
| Triple(TripleName), AsmPrinterVariant, *MAI, *MII, *MRI)); |
| |
| testing::internal::CaptureStderr(); |
| std::unique_ptr<MCDisassembler> DisAsm( |
| TheTarget->createMCDisassembler(*STI, *Ctx)); |
| std::string Output = testing::internal::GetCapturedStderr(); |
| EXPECT_TRUE(Output.find("<unknown>:0: warning: unsupported redefinition of " |
| "UC_VERSION_GFX10") != std::string::npos); |
| |
| SmallVector<char, 64> InsnStr, AnnoStr; |
| raw_svector_ostream OS(InsnStr); |
| raw_svector_ostream Annotations(AnnoStr); |
| formatted_raw_ostream FormattedOS(OS); |
| |
| char StrBuffer[128]; |
| |
| // Decode S_VERSION instruction with original or custom version. |
| uint8_t Versions[] = {UC_VERSION_GFX10_DEFAULT, UC_VERSION_GFX10_NEW}; |
| for (uint8_t Version : Versions) { |
| uint8_t Bytes[] = {Version, 0x00, 0x80, 0xb0}; |
| uint64_t InstSize = 0U; |
| MCInst Inst; |
| |
| AnnoStr.clear(); |
| InsnStr.clear(); |
| MCDisassembler::DecodeStatus Status = |
| DisAsm->getInstruction(Inst, InstSize, Bytes, 0, Annotations); |
| ASSERT_TRUE(Status == MCDisassembler::Success); |
| EXPECT_EQ(InstSize, 4U); |
| |
| IP->printInst(&Inst, 0, Annotations.str(), *STI, FormattedOS); |
| ASSERT_TRUE(InsnStr.size() < (sizeof(StrBuffer) - 1)); |
| std::memcpy(StrBuffer, InsnStr.data(), InsnStr.size()); |
| StrBuffer[InsnStr.size()] = '\0'; |
| |
| if (Version == UC_VERSION_GFX10_DEFAULT) |
| EXPECT_EQ(StringRef(StrBuffer), "\ts_version UC_VERSION_GFX10"); |
| else |
| EXPECT_EQ(StringRef(StrBuffer), "\ts_version 153"); |
| } |
| } |