Skip to content

Commit 95f924f

Browse files
authored
[RISCV][MC] Implement evaluateBranch for auipc+jalr pairs (#65480)
This patch implements `MCInstrAnalysis` state in order to be able analyze auipc+jalr pairs inside `evaluateBranch`. This is implemented as follows: - State: array of currently known GPR values; - Whenever an auipc is detected in `updateState`, update the state value of RD with the immediate; - Whenever a jalr is detected in `evaluateBranch`, check if the state holds a value for RS1 and use that to compute its target. Note that this is similar to how binutils implements it and the output of llvm-objdump should now mostly match the one of GNU objdump. This patch also updates the relevant llvm-objdump patches and adds a new one testing the output for interleaved auipc+jalr pairs.
1 parent 195f623 commit 95f924f

File tree

4 files changed

+130
-2
lines changed

4 files changed

+130
-2
lines changed

llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCTargetDesc.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "llvm/MC/MCSubtargetInfo.h"
3232
#include "llvm/MC/TargetRegistry.h"
3333
#include "llvm/Support/ErrorHandling.h"
34+
#include <bitset>
3435

3536
#define GET_INSTRINFO_MC_DESC
3637
#define ENABLE_INSTR_PREDICATE_VERIFIER
@@ -114,10 +115,79 @@ static MCTargetStreamer *createRISCVNullTargetStreamer(MCStreamer &S) {
114115
namespace {
115116

116117
class RISCVMCInstrAnalysis : public MCInstrAnalysis {
118+
int64_t GPRState[31] = {};
119+
std::bitset<31> GPRValidMask;
120+
121+
static bool isGPR(unsigned Reg) {
122+
return Reg >= RISCV::X0 && Reg <= RISCV::X31;
123+
}
124+
125+
static unsigned getRegIndex(unsigned Reg) {
126+
assert(isGPR(Reg) && Reg != RISCV::X0 && "Invalid GPR reg");
127+
return Reg - RISCV::X1;
128+
}
129+
130+
void setGPRState(unsigned Reg, std::optional<int64_t> Value) {
131+
if (Reg == RISCV::X0)
132+
return;
133+
134+
auto Index = getRegIndex(Reg);
135+
136+
if (Value) {
137+
GPRState[Index] = *Value;
138+
GPRValidMask.set(Index);
139+
} else {
140+
GPRValidMask.reset(Index);
141+
}
142+
}
143+
144+
std::optional<int64_t> getGPRState(unsigned Reg) const {
145+
if (Reg == RISCV::X0)
146+
return 0;
147+
148+
auto Index = getRegIndex(Reg);
149+
150+
if (GPRValidMask.test(Index))
151+
return GPRState[Index];
152+
return std::nullopt;
153+
}
154+
117155
public:
118156
explicit RISCVMCInstrAnalysis(const MCInstrInfo *Info)
119157
: MCInstrAnalysis(Info) {}
120158

159+
void resetState() override { GPRValidMask.reset(); }
160+
161+
void updateState(const MCInst &Inst, uint64_t Addr) override {
162+
// Terminators mark the end of a basic block which means the sequentially
163+
// next instruction will be the first of another basic block and the current
164+
// state will typically not be valid anymore. For calls, we assume all
165+
// registers may be clobbered by the callee (TODO: should we take the
166+
// calling convention into account?).
167+
if (isTerminator(Inst) || isCall(Inst)) {
168+
resetState();
169+
return;
170+
}
171+
172+
switch (Inst.getOpcode()) {
173+
default: {
174+
// Clear the state of all defined registers for instructions that we don't
175+
// explicitly support.
176+
auto NumDefs = Info->get(Inst.getOpcode()).getNumDefs();
177+
for (unsigned I = 0; I < NumDefs; ++I) {
178+
auto DefReg = Inst.getOperand(I).getReg();
179+
if (isGPR(DefReg))
180+
setGPRState(DefReg, std::nullopt);
181+
}
182+
break;
183+
}
184+
case RISCV::AUIPC:
185+
setGPRState(Inst.getOperand(0).getReg(),
186+
Addr + (Inst.getOperand(1).getImm() << 12));
187+
break;
188+
}
189+
}
190+
121191
bool evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size,
122192
uint64_t &Target) const override {
123193
if (isConditionalBranch(Inst)) {
@@ -140,6 +210,15 @@ class RISCVMCInstrAnalysis : public MCInstrAnalysis {
140210
return true;
141211
}
142212

213+
if (Inst.getOpcode() == RISCV::JALR) {
214+
if (auto TargetRegState = getGPRState(Inst.getOperand(1).getReg())) {
215+
Target = *TargetRegState + Inst.getOperand(2).getImm();
216+
return true;
217+
}
218+
219+
return false;
220+
}
221+
143222
return false;
144223
}
145224

llvm/test/tools/llvm-objdump/ELF/RISCV/branches.s

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ c.jal bar
5757
c.j bar
5858

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

6363
# CHECK: auipc ra, 0
64-
# CHECK: jalr ra, 16(ra){{$}}
64+
# CHECK: jalr ra, 16(ra) <bar>
6565
call bar
6666

6767
.Llocal:
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+c < %s | \
2+
# RUN: llvm-objdump -d -M no-aliases --no-show-raw-insn - | \
3+
# RUN: FileCheck %s
4+
5+
## Test multiple interleaved auipc/jalr pairs
6+
# CHECK: auipc t0, 0
7+
1: auipc t0, %pcrel_hi(bar)
8+
# CHECK: auipc t1, 0
9+
2: auipc t1, %pcrel_hi(bar)
10+
# CHECK: jalr ra, {{[0-9]+}}(t0) <bar>
11+
jalr %pcrel_lo(1b)(t0)
12+
## Target should not be printed because the call above clobbers register state
13+
# CHECK: jalr ra, {{[0-9]+}}(t1){{$}}
14+
jalr %pcrel_lo(2b)(t1)
15+
16+
## Test that auipc+jalr with a write to the target register in between does not
17+
## print the target
18+
# CHECK: auipc t0, 0
19+
1: auipc t0, %pcrel_hi(bar)
20+
# CHECK: c.li t0, 0
21+
li t0, 0
22+
# CHECK: jalr ra, {{[0-9]+}}(t0){{$}}
23+
jalr %pcrel_lo(1b)(t0)
24+
25+
## Test that auipc+jalr with a write to an unrelated register in between does
26+
## print the target
27+
# CHECK: auipc t0, 0
28+
1: auipc t0, %pcrel_hi(bar)
29+
# CHECK: c.li t1, 0
30+
li t1, 0
31+
# CHECK: jalr ra, {{[0-9]+}}(t0) <bar>
32+
jalr %pcrel_lo(1b)(t0)
33+
34+
## Test that auipc+jalr with a terminator in between does not print the target
35+
# CHECK: auipc t0, 0
36+
1: auipc t0, %pcrel_hi(bar)
37+
# CHECK: c.j {{.*}} <bar>
38+
j bar
39+
# CHECK: jalr ra, {{[0-9]+}}(t0){{$}}
40+
jalr %pcrel_lo(1b)(t0)
41+
42+
# CHECK-LABEL: <bar>:
43+
bar:
44+
# CHECK: c.nop
45+
nop

llvm/tools/llvm-objdump/llvm-objdump.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,8 @@ collectLocalBranchTargets(ArrayRef<uint8_t> Bytes, MCInstrAnalysis *MIA,
13221322
!(STI->getTargetTriple().isPPC() && Target == Index))
13231323
Labels[Target] = ("L" + Twine(LabelCount++)).str();
13241324
MIA->updateState(Inst, Index);
1325+
} else if (!Disassembled && MIA) {
1326+
MIA->resetState();
13251327
}
13261328
Index += Size;
13271329
}
@@ -2194,6 +2196,8 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
21942196
}
21952197

21962198
DT->InstrAnalysis->updateState(Inst, SectionAddr + Index);
2199+
} else if (!Disassembled && DT->InstrAnalysis) {
2200+
DT->InstrAnalysis->resetState();
21972201
}
21982202
}
21992203

0 commit comments

Comments
 (0)