Index: llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp =================================================================== --- llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp +++ llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp @@ -142,6 +142,78 @@ return false; } + + bool isTerminator(const MCInst &Inst) const override { + if (MCInstrAnalysis::isTerminator(Inst)) + return true; + + switch (Inst.getOpcode()) { + default: + return false; + case RISCV::JAL: + case RISCV::JALR: + return Inst.getOperand(0).getReg() == RISCV::X0; + } + } + + bool isCall(const MCInst &Inst) const override { + if (MCInstrAnalysis::isCall(Inst)) + return true; + + switch (Inst.getOpcode()) { + default: + return false; + case RISCV::JAL: + case RISCV::JALR: + return Inst.getOperand(0).getReg() != RISCV::X0; + } + } + + bool isReturn(const MCInst &Inst) const override { + if (MCInstrAnalysis::isReturn(Inst)) + return true; + + switch (Inst.getOpcode()) { + default: + return false; + case RISCV::JALR: + return Inst.getOperand(0).getReg() == RISCV::X0 && + maybeReturnAddress(Inst.getOperand(1).getReg()); + case RISCV::C_JR: + return maybeReturnAddress(Inst.getOperand(0).getReg()); + } + } + + bool isBranch(const MCInst &Inst) const override { + if (MCInstrAnalysis::isBranch(Inst)) + return true; + + switch (Inst.getOpcode()) { + default: + return false; + case RISCV::JAL: + case RISCV::JALR: + return Inst.getOperand(0).getReg() == RISCV::X0; + } + } + + bool isIndirectBranch(const MCInst &Inst) const override { + if (MCInstrAnalysis::isIndirectBranch(Inst)) + return true; + + switch (Inst.getOpcode()) { + default: + return false; + case RISCV::JALR: + return Inst.getOperand(0).getReg() == RISCV::X0; + } + } + +private: + static bool maybeReturnAddress(unsigned Reg) { + // X1 is used for normal returns, X5 for returns from outlined functions. + return Reg == RISCV::X1 || Reg == RISCV::X5; + } }; } // end anonymous namespace Index: llvm/unittests/Target/RISCV/CMakeLists.txt =================================================================== --- /dev/null +++ llvm/unittests/Target/RISCV/CMakeLists.txt @@ -0,0 +1,18 @@ +include_directories( + ${LLVM_MAIN_SRC_DIR}/lib/Target/RISCV + ${LLVM_BINARY_DIR}/lib/Target/RISCV + ) + +set(LLVM_LINK_COMPONENTS + RISCVCodeGen + RISCVDesc + RISCVInfo + TargetParser + MC + ) + +add_llvm_target_unittest(RISCVTests + MCInstrAnalysisTest.cpp + ) + +set_property(TARGET RISCVTests PROPERTY FOLDER "Tests/UnitTests/TargetTests") Index: llvm/unittests/Target/RISCV/MCInstrAnalysisTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/Target/RISCV/MCInstrAnalysisTest.cpp @@ -0,0 +1,132 @@ +#include "llvm/MC/MCInstrAnalysis.h" +#include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "llvm/MC/MCInstBuilder.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include + +using namespace llvm; +using testing::IsFalse; +using testing::IsTrue; + +namespace { + +class InstrAnalysisTest : public testing::TestWithParam { +protected: + std::unique_ptr Info; + std::unique_ptr Analysis; + + static void SetUpTestSuite() { + LLVMInitializeRISCVTargetInfo(); + LLVMInitializeRISCVTarget(); + LLVMInitializeRISCVTargetMC(); + } + + InstrAnalysisTest() { + std::string Error; + const Target *TheTarget = + TargetRegistry::lookupTarget(Triple::normalize(GetParam()), Error); + Info = std::unique_ptr(TheTarget->createMCInstrInfo()); + Analysis = std::unique_ptr( + TheTarget->createMCInstrAnalysis(Info.get())); + } +}; + +MCInst jal(unsigned RD) { + return MCInstBuilder(RISCV::JAL).addReg(RD).addImm(16); +} + +MCInst jalr(unsigned RD, unsigned RS1 = RISCV::X10) { + return MCInstBuilder(RISCV::JALR).addReg(RD).addReg(RS1).addImm(16); +} + +MCInst cj() { return MCInstBuilder(RISCV::C_J).addImm(16); } +MCInst cjr(unsigned RS1) { return MCInstBuilder(RISCV::C_JR).addReg(RS1); } +MCInst cjal() { return MCInstBuilder(RISCV::C_JAL).addImm(16); } +MCInst cjalr(unsigned RS1) { return MCInstBuilder(RISCV::C_JALR).addReg(RS1); } + +MCInst beq() { + return MCInstBuilder(RISCV::BEQ) + .addReg(RISCV::X0) + .addReg(RISCV::X1) + .addImm(32); +} + +MCInst cbeqz() { + return MCInstBuilder(RISCV::C_BEQZ).addReg(RISCV::X1).addImm(32); +} + +TEST_P(InstrAnalysisTest, IsTerminator) { + ASSERT_THAT(Analysis->isTerminator(beq()), IsTrue()); + ASSERT_THAT(Analysis->isTerminator(cbeqz()), IsTrue()); + ASSERT_THAT(Analysis->isTerminator(jal(RISCV::X0)), IsTrue()); + ASSERT_THAT(Analysis->isTerminator(jal(RISCV::X5)), IsFalse()); + ASSERT_THAT(Analysis->isTerminator(jalr(RISCV::X0)), IsTrue()); + ASSERT_THAT(Analysis->isTerminator(jalr(RISCV::X5)), IsFalse()); + ASSERT_THAT(Analysis->isTerminator(cj()), IsTrue()); + ASSERT_THAT(Analysis->isTerminator(cjal()), IsFalse()); +} + +TEST_P(InstrAnalysisTest, IsCall) { + ASSERT_THAT(Analysis->isCall(beq()), IsFalse()); + ASSERT_THAT(Analysis->isCall(cbeqz()), IsFalse()); + ASSERT_THAT(Analysis->isCall(jal(RISCV::X0)), IsFalse()); + ASSERT_THAT(Analysis->isCall(jal(RISCV::X1)), IsTrue()); + ASSERT_THAT(Analysis->isCall(jalr(RISCV::X1, RISCV::X1)), IsTrue()); + ASSERT_THAT(Analysis->isCall(jalr(RISCV::X0, RISCV::X5)), IsFalse()); + ASSERT_THAT(Analysis->isCall(cj()), IsFalse()); + ASSERT_THAT(Analysis->isCall(cjr(RISCV::X5)), IsFalse()); + ASSERT_THAT(Analysis->isCall(cjal()), IsTrue()); + ASSERT_THAT(Analysis->isCall(cjalr(RISCV::X5)), IsTrue()); +} + +TEST_P(InstrAnalysisTest, IsReturn) { + ASSERT_THAT(Analysis->isReturn(beq()), IsFalse()); + ASSERT_THAT(Analysis->isReturn(cbeqz()), IsFalse()); + ASSERT_THAT(Analysis->isReturn(jal(RISCV::X0)), IsFalse()); + ASSERT_THAT(Analysis->isReturn(jalr(RISCV::X0, RISCV::X1)), IsTrue()); + ASSERT_THAT(Analysis->isReturn(jalr(RISCV::X1, RISCV::X1)), IsFalse()); + ASSERT_THAT(Analysis->isReturn(jalr(RISCV::X0, RISCV::X5)), IsTrue()); + ASSERT_THAT(Analysis->isReturn(cj()), IsFalse()); + ASSERT_THAT(Analysis->isReturn(cjr(RISCV::X1)), IsTrue()); + ASSERT_THAT(Analysis->isReturn(cjr(RISCV::X2)), IsFalse()); + ASSERT_THAT(Analysis->isReturn(cjr(RISCV::X5)), IsTrue()); + ASSERT_THAT(Analysis->isReturn(cjal()), IsFalse()); + ASSERT_THAT(Analysis->isReturn(cjalr(RISCV::X1)), IsFalse()); + ASSERT_THAT(Analysis->isReturn(cjalr(RISCV::X5)), IsFalse()); +} + +TEST_P(InstrAnalysisTest, IsBranch) { + ASSERT_THAT(Analysis->isBranch(beq()), IsTrue()); + ASSERT_THAT(Analysis->isBranch(cbeqz()), IsTrue()); + ASSERT_THAT(Analysis->isBranch(jal(RISCV::X0)), IsTrue()); + ASSERT_THAT(Analysis->isBranch(jal(RISCV::X1)), IsFalse()); + ASSERT_THAT(Analysis->isBranch(jalr(RISCV::X0)), IsTrue()); + ASSERT_THAT(Analysis->isBranch(jalr(RISCV::X1)), IsFalse()); + ASSERT_THAT(Analysis->isBranch(cj()), IsTrue()); + ASSERT_THAT(Analysis->isBranch(cjr(RISCV::X5)), IsTrue()); + ASSERT_THAT(Analysis->isBranch(cjal()), IsFalse()); + ASSERT_THAT(Analysis->isBranch(cjalr(RISCV::X5)), IsFalse()); +} + +TEST_P(InstrAnalysisTest, IsIndirectBranch) { + ASSERT_THAT(Analysis->isIndirectBranch(beq()), IsFalse()); + ASSERT_THAT(Analysis->isIndirectBranch(cbeqz()), IsFalse()); + ASSERT_THAT(Analysis->isIndirectBranch(jal(RISCV::X0)), IsFalse()); + ASSERT_THAT(Analysis->isIndirectBranch(jal(RISCV::X1)), IsFalse()); + ASSERT_THAT(Analysis->isIndirectBranch(jalr(RISCV::X0)), IsTrue()); + ASSERT_THAT(Analysis->isIndirectBranch(jalr(RISCV::X1)), IsFalse()); + ASSERT_THAT(Analysis->isIndirectBranch(cj()), IsFalse()); + ASSERT_THAT(Analysis->isIndirectBranch(cjr(RISCV::X5)), IsTrue()); + ASSERT_THAT(Analysis->isIndirectBranch(cjal()), IsFalse()); + ASSERT_THAT(Analysis->isIndirectBranch(cjalr(RISCV::X5)), IsFalse()); +} + +INSTANTIATE_TEST_SUITE_P(RV32And64, InstrAnalysisTest, + testing::Values("riscv32", "riscv64")); + +} // namespace