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 @@ -166,6 +166,15 @@ evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size, uint64_t &Target) const; + /// Given a branch instruction try to get the address the branch targets. + /// Return true on success, and the address in Target. If the previous + /// instruction is known, it may be passed in PrevInst, in which case Addr is + /// still the address of Inst. This is useful for targets like RISC-V where + /// some branches take up two instructions (e.g. auipc+jalr). + virtual bool evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size, + uint64_t &Target, + const std::optional &PrevInst) const; + /// Given an instruction tries to get the address of a memory operand. Returns /// the address on success. virtual std::optional diff --git a/llvm/lib/MC/MCInstrAnalysis.cpp b/llvm/lib/MC/MCInstrAnalysis.cpp --- a/llvm/lib/MC/MCInstrAnalysis.cpp +++ b/llvm/lib/MC/MCInstrAnalysis.cpp @@ -30,6 +30,12 @@ return false; } +bool MCInstrAnalysis::evaluateBranch( + const MCInst &Inst, uint64_t Addr, uint64_t Size, uint64_t &Target, + const std::optional &PrevInst) const { + return evaluateBranch(Inst, Addr, Size, Target); +} + std::optional MCInstrAnalysis::evaluateMemoryOperandAddress( const MCInst &Inst, const MCSubtargetInfo *STI, uint64_t Addr, uint64_t Size) const { diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp @@ -120,6 +120,12 @@ bool evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size, uint64_t &Target) const override { + return evaluateBranch(Inst, Addr, Size, Target, std::nullopt); + } + + bool evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size, + uint64_t &Target, + const std::optional &PrevInst) const override { if (isConditionalBranch(Inst)) { int64_t Imm; if (Size == 2) @@ -140,6 +146,16 @@ return true; } + // Detect auipc xi, a; jalr b(xi). Note that Addr is the address of Inst + // (jalr) so we have to subtract 4 from it. + if (PrevInst && PrevInst->getOpcode() == RISCV::AUIPC && + Inst.getOpcode() == RISCV::JALR && + PrevInst->getOperand(0).getReg() == Inst.getOperand(1).getReg()) { + Target = Addr - 4 + (PrevInst->getOperand(1).getImm() << 12) + + Inst.getOperand(2).getImm(); + return true; + } + return false; } diff --git a/llvm/test/tools/llvm-objdump/ELF/RISCV/branches.s b/llvm/test/tools/llvm-objdump/ELF/RISCV/branches.s --- a/llvm/test/tools/llvm-objdump/ELF/RISCV/branches.s +++ b/llvm/test/tools/llvm-objdump/ELF/RISCV/branches.s @@ -57,11 +57,11 @@ c.j bar # CHECK: auipc ra, 0 -# CHECK: jalr ra, 16(ra){{$}} +# CHECK: jalr ra, 16(ra) call .Llocal # CHECK: auipc ra, 0 -# CHECK: jalr ra, 16(ra){{$}} +# CHECK: jalr ra, 16(ra) call bar .Llocal: diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -1911,6 +1911,7 @@ BBAddrMapLabels); } + std::optional PrevInst; while (Index < End) { // ARM and AArch64 ELF binaries can interleave data and text in the // same section. We rely on the markers introduced to understand what @@ -2023,7 +2024,7 @@ llvm::raw_ostream *TargetOS = &FOS; uint64_t Target; bool PrintTarget = DT->InstrAnalysis->evaluateBranch( - Inst, SectionAddr + Index, Size, Target); + Inst, SectionAddr + Index, Size, Target, PrevInst); if (!PrintTarget) if (std::optional MaybeTarget = DT->InstrAnalysis->evaluateMemoryOperandAddress( @@ -2128,6 +2129,8 @@ *TargetOS << "\n"; } } + + PrevInst = Inst; } assert(DT->Context->getAsmInfo());