208 lines
7.6 KiB
C++

//===- 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");
}
}