diff --git a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp --- a/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp +++ b/lldb/source/Plugins/Disassembler/LLVMC/DisassemblerLLVMC.cpp @@ -18,6 +18,7 @@ #include "llvm/MC/MCDisassembler/MCRelocationInfo.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrAnalysis.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSubtargetInfo.h" @@ -75,7 +76,8 @@ std::unique_ptr &&asm_info_up, std::unique_ptr &&context_up, std::unique_ptr &&disasm_up, - std::unique_ptr &&instr_printer_up); + std::unique_ptr &&instr_printer_up, + std::unique_ptr &&instr_analysis_up); std::unique_ptr m_instr_info_up; std::unique_ptr m_reg_info_up; @@ -84,6 +86,7 @@ std::unique_ptr m_context_up; std::unique_ptr m_disasm_up; std::unique_ptr m_instr_printer_up; + std::unique_ptr m_instr_analysis_up; }; namespace x86 { @@ -1287,11 +1290,15 @@ if (!instr_printer_up) return Instance(); - return Instance( - new MCDisasmInstance(std::move(instr_info_up), std::move(reg_info_up), - std::move(subtarget_info_up), std::move(asm_info_up), - std::move(context_up), std::move(disasm_up), - std::move(instr_printer_up))); + // Not all targets may have registered createMCInstrAnalysis(). + std::unique_ptr instr_analysis_up( + curr_target->createMCInstrAnalysis(instr_info_up.get())); + + return Instance(new MCDisasmInstance( + std::move(instr_info_up), std::move(reg_info_up), + std::move(subtarget_info_up), std::move(asm_info_up), + std::move(context_up), std::move(disasm_up), std::move(instr_printer_up), + std::move(instr_analysis_up))); } DisassemblerLLVMC::MCDisasmInstance::MCDisasmInstance( @@ -1301,13 +1308,15 @@ std::unique_ptr &&asm_info_up, std::unique_ptr &&context_up, std::unique_ptr &&disasm_up, - std::unique_ptr &&instr_printer_up) + std::unique_ptr &&instr_printer_up, + std::unique_ptr &&instr_analysis_up) : m_instr_info_up(std::move(instr_info_up)), m_reg_info_up(std::move(reg_info_up)), m_subtarget_info_up(std::move(subtarget_info_up)), m_asm_info_up(std::move(asm_info_up)), m_context_up(std::move(context_up)), m_disasm_up(std::move(disasm_up)), - m_instr_printer_up(std::move(instr_printer_up)) { + m_instr_printer_up(std::move(instr_printer_up)), + m_instr_analysis_up(std::move(instr_analysis_up)) { assert(m_instr_info_up && m_reg_info_up && m_subtarget_info_up && m_asm_info_up && m_context_up && m_disasm_up && m_instr_printer_up); } @@ -1365,6 +1374,8 @@ bool DisassemblerLLVMC::MCDisasmInstance::CanBranch( llvm::MCInst &mc_inst) const { + if (m_instr_analysis_up) + return m_instr_analysis_up->mayAffectControlFlow(mc_inst, *m_reg_info_up); return m_instr_info_up->get(mc_inst.getOpcode()) .mayAffectControlFlow(mc_inst, *m_reg_info_up); } @@ -1375,6 +1386,8 @@ } bool DisassemblerLLVMC::MCDisasmInstance::IsCall(llvm::MCInst &mc_inst) const { + if (m_instr_analysis_up) + return m_instr_analysis_up->isCall(mc_inst); return m_instr_info_up->get(mc_inst.getOpcode()).isCall(); } diff --git a/lldb/unittests/Disassembler/CMakeLists.txt b/lldb/unittests/Disassembler/CMakeLists.txt --- a/lldb/unittests/Disassembler/CMakeLists.txt +++ b/lldb/unittests/Disassembler/CMakeLists.txt @@ -5,3 +5,7 @@ if("X86" IN_LIST LLVM_TARGETS_TO_BUILD) add_subdirectory(x86) endif() + +if("RISCV" IN_LIST LLVM_TARGETS_TO_BUILD) + add_subdirectory(RISCV) +endif() diff --git a/lldb/unittests/Disassembler/RISCV/CMakeLists.txt b/lldb/unittests/Disassembler/RISCV/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lldb/unittests/Disassembler/RISCV/CMakeLists.txt @@ -0,0 +1,12 @@ +add_lldb_unittest(MCDisasmInstanceRISCVTests + TestMCDisasmInstanceRISCV.cpp + LINK_LIBS + lldbCore + lldbSymbol + lldbTarget + lldbPluginDisassemblerLLVMC + lldbPluginProcessUtility + LINK_COMPONENTS + Support + ${LLVM_TARGETS_TO_BUILD} + ) diff --git a/lldb/unittests/Disassembler/RISCV/TestMCDisasmInstanceRISCV.cpp b/lldb/unittests/Disassembler/RISCV/TestMCDisasmInstanceRISCV.cpp new file mode 100644 --- /dev/null +++ b/lldb/unittests/Disassembler/RISCV/TestMCDisasmInstanceRISCV.cpp @@ -0,0 +1,90 @@ +//===-- TestMCDisasmInstanceRISCV.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/Support/TargetSelect.h" +#include "gtest/gtest.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Utility/ArchSpec.h" + +#include "Plugins/Disassembler/LLVMC/DisassemblerLLVMC.h" + +using namespace lldb; +using namespace lldb_private; + +class TestMCDisasmInstanceRISCV : public testing::Test { +public: + static void SetUpTestCase(); + static void TearDownTestCase(); + +protected: +}; + +void TestMCDisasmInstanceRISCV::SetUpTestCase() { + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); + DisassemblerLLVMC::Initialize(); +} + +void TestMCDisasmInstanceRISCV::TearDownTestCase() { + DisassemblerLLVMC::Terminate(); +} + +TEST_F(TestMCDisasmInstanceRISCV, TestRISCV32Instruction) { + ArchSpec arch("riscv32-*-linux"); + + const unsigned num_of_instructions = 5; + uint8_t data[] = { + 0xef, 0x00, 0x00, 0x00, // call -- jal x1, 0 + 0xe7, 0x00, 0x00, 0x00, // call -- jalr x1, x0, 0 + 0x6f, 0x00, 0x00, 0x00, // jump -- jal x0, 0 + 0x67, 0x00, 0x00, 0x00, // jump -- jalr x0, x0, 0 + 0x67, 0x80, 0x00, 0x00 // ret -- jalr x0, x1, 0 + }; + + DisassemblerSP disass_sp; + Address start_addr(0x100); + disass_sp = + Disassembler::DisassembleBytes(arch, nullptr, nullptr, start_addr, &data, + sizeof (data), num_of_instructions, false); + + // If we failed to get a disassembler, we can assume it is because + // the llvm we linked against was not built with the riscv target, + // and we should skip these tests without marking anything as failing. + if (!disass_sp) + return; + + const InstructionList inst_list(disass_sp->GetInstructionList()); + EXPECT_EQ(num_of_instructions, inst_list.GetSize()); + + InstructionSP inst_sp; + inst_sp = inst_list.GetInstructionAtIndex(0); + EXPECT_TRUE(inst_sp->IsCall()); + EXPECT_TRUE(inst_sp->DoesBranch()); + + inst_sp = inst_list.GetInstructionAtIndex(1); + EXPECT_TRUE(inst_sp->IsCall()); + EXPECT_TRUE(inst_sp->DoesBranch()); + + inst_sp = inst_list.GetInstructionAtIndex(2); + EXPECT_FALSE(inst_sp->IsCall()); + EXPECT_TRUE(inst_sp->DoesBranch()); + + inst_sp = inst_list.GetInstructionAtIndex(3); + EXPECT_FALSE(inst_sp->IsCall()); + EXPECT_TRUE(inst_sp->DoesBranch()); + + inst_sp = inst_list.GetInstructionAtIndex(4); + EXPECT_FALSE(inst_sp->IsCall()); + EXPECT_TRUE(inst_sp->DoesBranch()); +} diff --git a/lldb/unittests/Disassembler/x86/TestGetControlFlowKindx86.cpp b/lldb/unittests/Disassembler/x86/TestGetControlFlowKindx86.cpp --- a/lldb/unittests/Disassembler/x86/TestGetControlFlowKindx86.cpp +++ b/lldb/unittests/Disassembler/x86/TestGetControlFlowKindx86.cpp @@ -129,17 +129,28 @@ // If we failed to get a disassembler, we can assume it is because // the llvm we linked against was not built with the i386 target, // and we should skip these tests without marking anything as failing. - - if (disass_sp) { - const InstructionList inst_list(disass_sp->GetInstructionList()); - EXPECT_EQ(num_of_instructions, inst_list.GetSize()); - - for (size_t i = 0; i < num_of_instructions; ++i) { - InstructionSP inst_sp; - inst_sp = inst_list.GetInstructionAtIndex(i); - ExecutionContext exe_ctx (nullptr, nullptr, nullptr); - InstructionControlFlowKind kind = inst_sp->GetControlFlowKind(&exe_ctx); - EXPECT_EQ(kind, result[i]); - } + if (!disass_sp) + return; + + const InstructionList inst_list(disass_sp->GetInstructionList()); + EXPECT_EQ(num_of_instructions, inst_list.GetSize()); + + for (size_t i = 0; i < num_of_instructions; ++i) { + InstructionSP inst_sp; + inst_sp = inst_list.GetInstructionAtIndex(i); + ExecutionContext exe_ctx(nullptr, nullptr, nullptr); + InstructionControlFlowKind kind = inst_sp->GetControlFlowKind(&exe_ctx); + EXPECT_EQ(kind, result[i]); + + // Also, test the DisassemblerLLVMC::MCDisasmInstance methods. + if (kind == eInstructionControlFlowKindReturn) + EXPECT_FALSE(inst_sp->IsCall()); + if (kind == eInstructionControlFlowKindCall) + EXPECT_TRUE(inst_sp->IsCall()); + if (kind == eInstructionControlFlowKindCall || + kind == eInstructionControlFlowKindJump || + kind == eInstructionControlFlowKindCondJump || + kind == eInstructionControlFlowKindReturn) + EXPECT_TRUE(inst_sp->DoesBranch()); } } diff --git a/llvm/include/llvm/MC/MCInstrAnalysis.h b/llvm/include/llvm/MC/MCInstrAnalysis.h --- a/llvm/include/llvm/MC/MCInstrAnalysis.h +++ b/llvm/include/llvm/MC/MCInstrAnalysis.h @@ -18,6 +18,7 @@ #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrDesc.h" #include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" #include #include @@ -64,6 +65,17 @@ return Info->get(Inst.getOpcode()).isTerminator(); } + virtual bool mayAffectControlFlow(const MCInst &Inst, + const MCRegisterInfo &MCRI) const { + if (isBranch(Inst) || isCall(Inst) || isReturn(Inst) || + isIndirectBranch(Inst)) + return true; + unsigned PC = MCRI.getProgramCounter(); + if (PC == 0) + return false; + return Info->get(Inst.getOpcode()).hasDefOfPhysReg(Inst, PC, MCRI); + } + /// Returns true if at least one of the register writes performed by /// \param Inst implicitly clears the upper portion of all super-registers. ///