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,13 @@ evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size, uint64_t &Target) const; + /// Given two consecutive instructions try to get the address the branch + /// targets. Return true on success, and the address in Target. + /// This overload is useful for targets like RISC-V where some branches take + /// up two instructions (e.g. auipc+jalr). + virtual bool evaluateBranch(const MCInst &Inst1, const MCInst &Inst2, + uint64_t Addr, uint64_t &Target) 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,13 @@ return false; } +bool MCInstrAnalysis::evaluateBranch(const MCInst & /*Inst1*/, + const MCInst & /*Inst2*/, + uint64_t /*Addr*/, + uint64_t & /*Target*/) const { + return false; +} + 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 @@ -143,6 +143,18 @@ return false; } + bool evaluateBranch(const MCInst &Inst1, const MCInst &Inst2, uint64_t Addr, + uint64_t &Target) const override { + if (Inst1.getOpcode() != RISCV::AUIPC || Inst2.getOpcode() != RISCV::JALR) + return false; + if (Inst1.getOperand(0).getReg() != Inst2.getOperand(1).getReg()) + return false; + + Target = Addr + (Inst1.getOperand(1).getImm() << 12) + + Inst2.getOperand(2).getImm(); + return true; + } + bool isTerminator(const MCInst &Inst) const override { if (MCInstrAnalysis::isTerminator(Inst)) return true; 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,8 @@ BBAddrMapLabels); } + std::optional PrevInst; + uint64_t PrevIndex; 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 @@ -2024,6 +2026,9 @@ uint64_t Target; bool PrintTarget = DT->InstrAnalysis->evaluateBranch( Inst, SectionAddr + Index, Size, Target); + if (!PrintTarget && PrevInst) + PrintTarget = DT->InstrAnalysis->evaluateBranch( + *PrevInst, Inst, SectionAddr + PrevIndex, Target); if (!PrintTarget) if (std::optional MaybeTarget = DT->InstrAnalysis->evaluateMemoryOperandAddress( @@ -2128,6 +2133,9 @@ *TargetOS << "\n"; } } + + PrevInst = Inst; + PrevIndex = Index; } assert(DT->Context->getAsmInfo());