| //===- bolt/Target/RISCV/RISCVMCPlusBuilder.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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file provides RISCV-specific MCPlus builder. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "MCTargetDesc/RISCVMCExpr.h" |
| #include "MCTargetDesc/RISCVMCTargetDesc.h" |
| #include "bolt/Core/MCPlusBuilder.h" |
| #include "llvm/BinaryFormat/ELF.h" |
| #include "llvm/MC/MCInst.h" |
| #include "llvm/MC/MCSubtargetInfo.h" |
| #include "llvm/Support/ErrorHandling.h" |
| |
| #define DEBUG_TYPE "mcplus" |
| |
| using namespace llvm; |
| using namespace bolt; |
| |
| namespace { |
| |
| class RISCVMCPlusBuilder : public MCPlusBuilder { |
| public: |
| using MCPlusBuilder::MCPlusBuilder; |
| |
| bool equals(const MCTargetExpr &A, const MCTargetExpr &B, |
| CompFuncTy Comp) const override { |
| const auto &RISCVExprA = cast<RISCVMCExpr>(A); |
| const auto &RISCVExprB = cast<RISCVMCExpr>(B); |
| if (RISCVExprA.getKind() != RISCVExprB.getKind()) |
| return false; |
| |
| return MCPlusBuilder::equals(*RISCVExprA.getSubExpr(), |
| *RISCVExprB.getSubExpr(), Comp); |
| } |
| |
| void getCalleeSavedRegs(BitVector &Regs) const override { |
| Regs |= getAliases(RISCV::X2); |
| Regs |= getAliases(RISCV::X8); |
| Regs |= getAliases(RISCV::X9); |
| Regs |= getAliases(RISCV::X18); |
| Regs |= getAliases(RISCV::X19); |
| Regs |= getAliases(RISCV::X20); |
| Regs |= getAliases(RISCV::X21); |
| Regs |= getAliases(RISCV::X22); |
| Regs |= getAliases(RISCV::X23); |
| Regs |= getAliases(RISCV::X24); |
| Regs |= getAliases(RISCV::X25); |
| Regs |= getAliases(RISCV::X26); |
| Regs |= getAliases(RISCV::X27); |
| } |
| |
| bool shouldRecordCodeRelocation(uint64_t RelType) const override { |
| switch (RelType) { |
| case ELF::R_RISCV_JAL: |
| case ELF::R_RISCV_CALL: |
| case ELF::R_RISCV_CALL_PLT: |
| case ELF::R_RISCV_BRANCH: |
| case ELF::R_RISCV_RVC_BRANCH: |
| case ELF::R_RISCV_RVC_JUMP: |
| case ELF::R_RISCV_GOT_HI20: |
| case ELF::R_RISCV_PCREL_HI20: |
| case ELF::R_RISCV_PCREL_LO12_I: |
| case ELF::R_RISCV_PCREL_LO12_S: |
| case ELF::R_RISCV_HI20: |
| case ELF::R_RISCV_LO12_I: |
| case ELF::R_RISCV_LO12_S: |
| case ELF::R_RISCV_TLS_GOT_HI20: |
| return true; |
| default: |
| llvm_unreachable("Unexpected RISCV relocation type in code"); |
| } |
| } |
| |
| bool isNop(const MCInst &Inst) const { |
| return Inst.getOpcode() == RISCV::ADDI && |
| Inst.getOperand(0).getReg() == RISCV::X0 && |
| Inst.getOperand(1).getReg() == RISCV::X0 && |
| Inst.getOperand(2).getImm() == 0; |
| } |
| |
| bool isCNop(const MCInst &Inst) const { |
| return Inst.getOpcode() == RISCV::C_NOP; |
| } |
| |
| bool isNoop(const MCInst &Inst) const override { |
| return isNop(Inst) || isCNop(Inst); |
| } |
| |
| bool isPseudo(const MCInst &Inst) const override { |
| switch (Inst.getOpcode()) { |
| default: |
| return MCPlusBuilder::isPseudo(Inst); |
| case RISCV::PseudoCALL: |
| case RISCV::PseudoTAIL: |
| return false; |
| } |
| } |
| |
| bool isIndirectCall(const MCInst &Inst) const override { |
| if (!isCall(Inst)) |
| return false; |
| |
| switch (Inst.getOpcode()) { |
| default: |
| return false; |
| case RISCV::JALR: |
| case RISCV::C_JALR: |
| case RISCV::C_JR: |
| return true; |
| } |
| } |
| |
| bool hasPCRelOperand(const MCInst &Inst) const override { |
| switch (Inst.getOpcode()) { |
| default: |
| return false; |
| case RISCV::JAL: |
| case RISCV::AUIPC: |
| return true; |
| } |
| } |
| |
| unsigned getInvertedBranchOpcode(unsigned Opcode) const { |
| switch (Opcode) { |
| default: |
| llvm_unreachable("Failed to invert branch opcode"); |
| return Opcode; |
| case RISCV::BEQ: |
| return RISCV::BNE; |
| case RISCV::BNE: |
| return RISCV::BEQ; |
| case RISCV::BLT: |
| return RISCV::BGE; |
| case RISCV::BGE: |
| return RISCV::BLT; |
| case RISCV::BLTU: |
| return RISCV::BGEU; |
| case RISCV::BGEU: |
| return RISCV::BLTU; |
| case RISCV::C_BEQZ: |
| return RISCV::C_BNEZ; |
| case RISCV::C_BNEZ: |
| return RISCV::C_BEQZ; |
| } |
| } |
| |
| bool reverseBranchCondition(MCInst &Inst, const MCSymbol *TBB, |
| MCContext *Ctx) const override { |
| auto Opcode = getInvertedBranchOpcode(Inst.getOpcode()); |
| Inst.setOpcode(Opcode); |
| return replaceBranchTarget(Inst, TBB, Ctx); |
| } |
| |
| bool replaceBranchTarget(MCInst &Inst, const MCSymbol *TBB, |
| MCContext *Ctx) const override { |
| assert((isCall(Inst) || isBranch(Inst)) && !isIndirectBranch(Inst) && |
| "Invalid instruction"); |
| |
| unsigned SymOpIndex; |
| auto Result = getSymbolRefOperandNum(Inst, SymOpIndex); |
| (void)Result; |
| assert(Result && "unimplemented branch"); |
| |
| Inst.getOperand(SymOpIndex) = MCOperand::createExpr( |
| MCSymbolRefExpr::create(TBB, MCSymbolRefExpr::VK_None, *Ctx)); |
| return true; |
| } |
| |
| IndirectBranchType analyzeIndirectBranch( |
| MCInst &Instruction, InstructionIterator Begin, InstructionIterator End, |
| const unsigned PtrSize, MCInst *&MemLocInstr, unsigned &BaseRegNum, |
| unsigned &IndexRegNum, int64_t &DispValue, const MCExpr *&DispExpr, |
| MCInst *&PCRelBaseOut, MCInst *&FixedEntryLoadInst) const override { |
| MemLocInstr = nullptr; |
| BaseRegNum = 0; |
| IndexRegNum = 0; |
| DispValue = 0; |
| DispExpr = nullptr; |
| PCRelBaseOut = nullptr; |
| FixedEntryLoadInst = nullptr; |
| |
| // Check for the following long tail call sequence: |
| // 1: auipc xi, %pcrel_hi(sym) |
| // jalr zero, %pcrel_lo(1b)(xi) |
| if (Instruction.getOpcode() == RISCV::JALR && Begin != End) { |
| MCInst &PrevInst = *std::prev(End); |
| if (isRISCVCall(PrevInst, Instruction) && |
| Instruction.getOperand(0).getReg() == RISCV::X0) |
| return IndirectBranchType::POSSIBLE_TAIL_CALL; |
| } |
| |
| return IndirectBranchType::UNKNOWN; |
| } |
| |
| bool convertJmpToTailCall(MCInst &Inst) override { |
| if (isTailCall(Inst)) |
| return false; |
| |
| switch (Inst.getOpcode()) { |
| default: |
| llvm_unreachable("unsupported tail call opcode"); |
| case RISCV::JAL: |
| case RISCV::JALR: |
| case RISCV::C_J: |
| case RISCV::C_JR: |
| break; |
| } |
| |
| setTailCall(Inst); |
| return true; |
| } |
| |
| void createReturn(MCInst &Inst) const override { |
| // TODO "c.jr ra" when RVC is enabled |
| Inst.setOpcode(RISCV::JALR); |
| Inst.clear(); |
| Inst.addOperand(MCOperand::createReg(RISCV::X0)); |
| Inst.addOperand(MCOperand::createReg(RISCV::X1)); |
| Inst.addOperand(MCOperand::createImm(0)); |
| } |
| |
| void createUncondBranch(MCInst &Inst, const MCSymbol *TBB, |
| MCContext *Ctx) const override { |
| Inst.setOpcode(RISCV::JAL); |
| Inst.clear(); |
| Inst.addOperand(MCOperand::createReg(RISCV::X0)); |
| Inst.addOperand(MCOperand::createExpr( |
| MCSymbolRefExpr::create(TBB, MCSymbolRefExpr::VK_None, *Ctx))); |
| } |
| |
| StringRef getTrapFillValue() const override { |
| return StringRef("\0\0\0\0", 4); |
| } |
| |
| void createCall(unsigned Opcode, MCInst &Inst, const MCSymbol *Target, |
| MCContext *Ctx) { |
| Inst.setOpcode(Opcode); |
| Inst.clear(); |
| Inst.addOperand(MCOperand::createExpr(RISCVMCExpr::create( |
| MCSymbolRefExpr::create(Target, MCSymbolRefExpr::VK_None, *Ctx), |
| RISCVMCExpr::VK_RISCV_CALL, *Ctx))); |
| } |
| |
| void createCall(MCInst &Inst, const MCSymbol *Target, |
| MCContext *Ctx) override { |
| return createCall(RISCV::PseudoCALL, Inst, Target, Ctx); |
| } |
| |
| void createTailCall(MCInst &Inst, const MCSymbol *Target, |
| MCContext *Ctx) override { |
| return createCall(RISCV::PseudoTAIL, Inst, Target, Ctx); |
| } |
| |
| bool analyzeBranch(InstructionIterator Begin, InstructionIterator End, |
| const MCSymbol *&TBB, const MCSymbol *&FBB, |
| MCInst *&CondBranch, |
| MCInst *&UncondBranch) const override { |
| auto I = End; |
| |
| while (I != Begin) { |
| --I; |
| |
| // Ignore nops and CFIs |
| if (isPseudo(*I) || isNoop(*I)) |
| continue; |
| |
| // Stop when we find the first non-terminator |
| if (!isTerminator(*I) || isTailCall(*I) || !isBranch(*I)) |
| break; |
| |
| // Handle unconditional branches. |
| if (isUnconditionalBranch(*I)) { |
| // If any code was seen after this unconditional branch, we've seen |
| // unreachable code. Ignore them. |
| CondBranch = nullptr; |
| UncondBranch = &*I; |
| const MCSymbol *Sym = getTargetSymbol(*I); |
| assert(Sym != nullptr && |
| "Couldn't extract BB symbol from jump operand"); |
| TBB = Sym; |
| continue; |
| } |
| |
| // Handle conditional branches and ignore indirect branches |
| if (isIndirectBranch(*I)) |
| return false; |
| |
| if (CondBranch == nullptr) { |
| const MCSymbol *TargetBB = getTargetSymbol(*I); |
| if (TargetBB == nullptr) { |
| // Unrecognized branch target |
| return false; |
| } |
| FBB = TBB; |
| TBB = TargetBB; |
| CondBranch = &*I; |
| continue; |
| } |
| |
| llvm_unreachable("multiple conditional branches in one BB"); |
| } |
| |
| return true; |
| } |
| |
| bool getSymbolRefOperandNum(const MCInst &Inst, unsigned &OpNum) const { |
| switch (Inst.getOpcode()) { |
| default: |
| return false; |
| case RISCV::C_J: |
| OpNum = 0; |
| return true; |
| case RISCV::AUIPC: |
| case RISCV::JAL: |
| case RISCV::C_BEQZ: |
| case RISCV::C_BNEZ: |
| OpNum = 1; |
| return true; |
| case RISCV::BEQ: |
| case RISCV::BGE: |
| case RISCV::BGEU: |
| case RISCV::BNE: |
| case RISCV::BLT: |
| case RISCV::BLTU: |
| OpNum = 2; |
| return true; |
| } |
| } |
| |
| const MCSymbol *getTargetSymbol(const MCExpr *Expr) const override { |
| auto *RISCVExpr = dyn_cast<RISCVMCExpr>(Expr); |
| if (RISCVExpr && RISCVExpr->getSubExpr()) |
| return getTargetSymbol(RISCVExpr->getSubExpr()); |
| |
| auto *BinExpr = dyn_cast<MCBinaryExpr>(Expr); |
| if (BinExpr) |
| return getTargetSymbol(BinExpr->getLHS()); |
| |
| auto *SymExpr = dyn_cast<MCSymbolRefExpr>(Expr); |
| if (SymExpr && SymExpr->getKind() == MCSymbolRefExpr::VK_None) |
| return &SymExpr->getSymbol(); |
| |
| return nullptr; |
| } |
| |
| const MCSymbol *getTargetSymbol(const MCInst &Inst, |
| unsigned OpNum = 0) const override { |
| if (!OpNum && !getSymbolRefOperandNum(Inst, OpNum)) |
| return nullptr; |
| |
| const MCOperand &Op = Inst.getOperand(OpNum); |
| if (!Op.isExpr()) |
| return nullptr; |
| |
| return getTargetSymbol(Op.getExpr()); |
| } |
| |
| bool lowerTailCall(MCInst &Inst) override { |
| removeAnnotation(Inst, MCPlus::MCAnnotation::kTailCall); |
| if (getConditionalTailCall(Inst)) |
| unsetConditionalTailCall(Inst); |
| return true; |
| } |
| |
| uint64_t analyzePLTEntry(MCInst &Instruction, InstructionIterator Begin, |
| InstructionIterator End, |
| uint64_t BeginPC) const override { |
| auto I = Begin; |
| |
| assert(I != End); |
| auto &AUIPC = *I++; |
| assert(AUIPC.getOpcode() == RISCV::AUIPC); |
| assert(AUIPC.getOperand(0).getReg() == RISCV::X28); |
| |
| assert(I != End); |
| auto &LD = *I++; |
| assert(LD.getOpcode() == RISCV::LD); |
| assert(LD.getOperand(0).getReg() == RISCV::X28); |
| assert(LD.getOperand(1).getReg() == RISCV::X28); |
| |
| assert(I != End); |
| auto &JALR = *I++; |
| (void)JALR; |
| assert(JALR.getOpcode() == RISCV::JALR); |
| assert(JALR.getOperand(0).getReg() == RISCV::X6); |
| assert(JALR.getOperand(1).getReg() == RISCV::X28); |
| |
| assert(I != End); |
| auto &NOP = *I++; |
| (void)NOP; |
| assert(isNoop(NOP)); |
| |
| assert(I == End); |
| |
| auto AUIPCOffset = AUIPC.getOperand(1).getImm() << 12; |
| auto LDOffset = LD.getOperand(2).getImm(); |
| return BeginPC + AUIPCOffset + LDOffset; |
| } |
| |
| bool replaceImmWithSymbolRef(MCInst &Inst, const MCSymbol *Symbol, |
| int64_t Addend, MCContext *Ctx, int64_t &Value, |
| uint64_t RelType) const override { |
| unsigned ImmOpNo = -1U; |
| |
| for (unsigned Index = 0; Index < MCPlus::getNumPrimeOperands(Inst); |
| ++Index) { |
| if (Inst.getOperand(Index).isImm()) { |
| ImmOpNo = Index; |
| break; |
| } |
| } |
| |
| if (ImmOpNo == -1U) |
| return false; |
| |
| Value = Inst.getOperand(ImmOpNo).getImm(); |
| setOperandToSymbolRef(Inst, ImmOpNo, Symbol, Addend, Ctx, RelType); |
| return true; |
| } |
| |
| const MCExpr *getTargetExprFor(MCInst &Inst, const MCExpr *Expr, |
| MCContext &Ctx, |
| uint64_t RelType) const override { |
| switch (RelType) { |
| default: |
| return Expr; |
| case ELF::R_RISCV_GOT_HI20: |
| case ELF::R_RISCV_TLS_GOT_HI20: |
| // The GOT is reused so no need to create GOT relocations |
| case ELF::R_RISCV_PCREL_HI20: |
| return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_PCREL_HI, Ctx); |
| case ELF::R_RISCV_PCREL_LO12_I: |
| case ELF::R_RISCV_PCREL_LO12_S: |
| return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_PCREL_LO, Ctx); |
| case ELF::R_RISCV_HI20: |
| return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_HI, Ctx); |
| case ELF::R_RISCV_LO12_I: |
| case ELF::R_RISCV_LO12_S: |
| return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_LO, Ctx); |
| case ELF::R_RISCV_CALL: |
| return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_CALL, Ctx); |
| case ELF::R_RISCV_CALL_PLT: |
| return RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_CALL_PLT, Ctx); |
| } |
| } |
| |
| bool evaluateMemOperandTarget(const MCInst &Inst, uint64_t &Target, |
| uint64_t Address, |
| uint64_t Size) const override { |
| return false; |
| } |
| |
| bool isCallAuipc(const MCInst &Inst) const { |
| if (Inst.getOpcode() != RISCV::AUIPC) |
| return false; |
| |
| const auto &ImmOp = Inst.getOperand(1); |
| if (!ImmOp.isExpr()) |
| return false; |
| |
| const auto *ImmExpr = ImmOp.getExpr(); |
| if (!isa<RISCVMCExpr>(ImmExpr)) |
| return false; |
| |
| switch (cast<RISCVMCExpr>(ImmExpr)->getKind()) { |
| default: |
| return false; |
| case RISCVMCExpr::VK_RISCV_CALL: |
| case RISCVMCExpr::VK_RISCV_CALL_PLT: |
| return true; |
| } |
| } |
| |
| bool isRISCVCall(const MCInst &First, const MCInst &Second) const override { |
| if (!isCallAuipc(First)) |
| return false; |
| |
| assert(Second.getOpcode() == RISCV::JALR); |
| return true; |
| } |
| |
| uint16_t getMinFunctionAlignment() const override { |
| if (STI->hasFeature(RISCV::FeatureStdExtC) || |
| STI->hasFeature(RISCV::FeatureStdExtZca)) |
| return 2; |
| return 4; |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| namespace llvm { |
| namespace bolt { |
| |
| MCPlusBuilder *createRISCVMCPlusBuilder(const MCInstrAnalysis *Analysis, |
| const MCInstrInfo *Info, |
| const MCRegisterInfo *RegInfo, |
| const MCSubtargetInfo *STI) { |
| return new RISCVMCPlusBuilder(Analysis, Info, RegInfo, STI); |
| } |
| |
| } // namespace bolt |
| } // namespace llvm |