Skip to content

[RISCV][MC] Implement evaluateBranch for auipc+jalr pairs #65480

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/ErrorHandling.h"
#include <bitset>

#define GET_INSTRINFO_MC_DESC
#define ENABLE_INSTR_PREDICATE_VERIFIER
Expand Down Expand Up @@ -114,10 +115,79 @@ static MCTargetStreamer *createRISCVNullTargetStreamer(MCStreamer &S) {
namespace {

class RISCVMCInstrAnalysis : public MCInstrAnalysis {
int64_t GPRState[31] = {};
std::bitset<31> GPRValidMask;

static bool isGPR(unsigned Reg) {
return Reg >= RISCV::X0 && Reg <= RISCV::X31;
}

static unsigned getRegIndex(unsigned Reg) {
assert(isGPR(Reg) && Reg != RISCV::X0 && "Invalid GPR reg");
return Reg - RISCV::X1;
}

void setGPRState(unsigned Reg, std::optional<int64_t> Value) {
if (Reg == RISCV::X0)
return;

auto Index = getRegIndex(Reg);

if (Value) {
GPRState[Index] = *Value;
GPRValidMask.set(Index);
} else {
GPRValidMask.reset(Index);
}
}

std::optional<int64_t> getGPRState(unsigned Reg) const {
if (Reg == RISCV::X0)
return 0;

auto Index = getRegIndex(Reg);

if (GPRValidMask.test(Index))
return GPRState[Index];
return std::nullopt;
}

public:
explicit RISCVMCInstrAnalysis(const MCInstrInfo *Info)
: MCInstrAnalysis(Info) {}

void resetState() override { GPRValidMask.reset(); }

void updateState(const MCInst &Inst, uint64_t Addr) override {
// Terminators mark the end of a basic block which means the sequentially
// next instruction will be the first of another basic block and the current
// state will typically not be valid anymore. For calls, we assume all
// registers may be clobbered by the callee (TODO: should we take the
// calling convention into account?).
if (isTerminator(Inst) || isCall(Inst)) {
resetState();
return;
}

switch (Inst.getOpcode()) {
default: {
// Clear the state of all defined registers for instructions that we don't
// explicitly support.
auto NumDefs = Info->get(Inst.getOpcode()).getNumDefs();
for (unsigned I = 0; I < NumDefs; ++I) {
auto DefReg = Inst.getOperand(I).getReg();
if (isGPR(DefReg))
setGPRState(DefReg, std::nullopt);
}
break;
}
case RISCV::AUIPC:
setGPRState(Inst.getOperand(0).getReg(),
Addr + (Inst.getOperand(1).getImm() << 12));
break;
}
}

bool evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size,
uint64_t &Target) const override {
if (isConditionalBranch(Inst)) {
Expand All @@ -140,6 +210,15 @@ class RISCVMCInstrAnalysis : public MCInstrAnalysis {
return true;
}

if (Inst.getOpcode() == RISCV::JALR) {
if (auto TargetRegState = getGPRState(Inst.getOperand(1).getReg())) {
Target = *TargetRegState + Inst.getOperand(2).getImm();
return true;
}

return false;
}

return false;
}

Expand Down
4 changes: 2 additions & 2 deletions llvm/test/tools/llvm-objdump/ELF/RISCV/branches.s
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ c.jal bar
c.j bar

# CHECK: auipc ra, 0
# CHECK: jalr ra, 16(ra){{$}}
# CHECK: jalr ra, 16(ra) <foo+0x58>
call .Llocal

# CHECK: auipc ra, 0
# CHECK: jalr ra, 16(ra){{$}}
# CHECK: jalr ra, 16(ra) <bar>
call bar

.Llocal:
Expand Down
45 changes: 45 additions & 0 deletions llvm/test/tools/llvm-objdump/ELF/RISCV/multi-instr-target.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c < %s | \
# RUN: llvm-objdump -d -M no-aliases --no-show-raw-insn - | \
# RUN: FileCheck %s

## Test multiple interleaved auipc/jalr pairs
# CHECK: auipc t0, 0
1: auipc t0, %pcrel_hi(bar)
# CHECK: auipc t1, 0
2: auipc t1, %pcrel_hi(bar)
# CHECK: jalr ra, {{[0-9]+}}(t0) <bar>
jalr %pcrel_lo(1b)(t0)
## Target should not be printed because the call above clobbers register state
# CHECK: jalr ra, {{[0-9]+}}(t1){{$}}
jalr %pcrel_lo(2b)(t1)

## Test that auipc+jalr with a write to the target register in between does not
## print the target
# CHECK: auipc t0, 0
1: auipc t0, %pcrel_hi(bar)
# CHECK: c.li t0, 0
li t0, 0
# CHECK: jalr ra, {{[0-9]+}}(t0){{$}}
jalr %pcrel_lo(1b)(t0)

## Test that auipc+jalr with a write to an unrelated register in between does
## print the target
# CHECK: auipc t0, 0
1: auipc t0, %pcrel_hi(bar)
# CHECK: c.li t1, 0
li t1, 0
# CHECK: jalr ra, {{[0-9]+}}(t0) <bar>
jalr %pcrel_lo(1b)(t0)

## Test that auipc+jalr with a terminator in between does not print the target
# CHECK: auipc t0, 0
1: auipc t0, %pcrel_hi(bar)
# CHECK: c.j {{.*}} <bar>
j bar
# CHECK: jalr ra, {{[0-9]+}}(t0){{$}}
jalr %pcrel_lo(1b)(t0)

# CHECK-LABEL: <bar>:
bar:
# CHECK: c.nop
nop
4 changes: 4 additions & 0 deletions llvm/tools/llvm-objdump/llvm-objdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,8 @@ collectLocalBranchTargets(ArrayRef<uint8_t> Bytes, MCInstrAnalysis *MIA,
!(STI->getTargetTriple().isPPC() && Target == Index))
Labels[Target] = ("L" + Twine(LabelCount++)).str();
MIA->updateState(Inst, Index);
} else if (!Disassembled && MIA) {
MIA->resetState();
}
Index += Size;
}
Expand Down Expand Up @@ -2194,6 +2196,8 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
}

DT->InstrAnalysis->updateState(Inst, SectionAddr + Index);
} else if (!Disassembled && DT->InstrAnalysis) {
DT->InstrAnalysis->resetState();
}
}

Expand Down