//===- 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(A); const auto &RISCVExprB = cast(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) const override { MemLocInstr = nullptr; BaseRegNum = 0; IndexRegNum = 0; DispValue = 0; DispExpr = nullptr; PCRelBaseOut = 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(Expr); if (RISCVExpr && RISCVExpr->getSubExpr()) return getTargetSymbol(RISCVExpr->getSubExpr()); auto *BinExpr = dyn_cast(Expr); if (BinExpr) return getTargetSymbol(BinExpr->getLHS()); auto *SymExpr = dyn_cast(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(ImmExpr)) return false; switch (cast(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