Skip to content

Commit a266d9e

Browse files
zhaoqi5github-actions[bot]
authored andcommitted
Automerge: [sancov][LoongArch] Resolve pcaddu18i+jirl in evaluateBranch and teach sancov (#155371)
This commit overrides `updateState` and `resetState` hooks in `MCInstrAnalysis` in order to be able to analyze pcaddu18i+jirl pairs inside `evaluateBranch`. After this commit, `llvm-objdump` is able to correctly analyze and print detailed information. `lld/test/ELF/loongarch-call36.s` shows the changes. Besides, this commit also teaches sancov to resolve such call sequences. Without this commit, some tests in compiler-rt failed: ``` Failed Tests : SanitizerCommon-asan-loongarch64-Linux :: sanitizer_coverage_trace_pc_guard-dso.cpp SanitizerCommon-asan-loongarch64-Linux :: sanitizer_coverage_trace_pc_guard.cpp SanitizerCommon-lsan-loongarch64-Linux :: sanitizer_coverage_trace_pc_guard-dso.cpp SanitizerCommon-lsan-loongarch64-Linux :: sanitizer_coverage_trace_pc_guard.cpp SanitizerCommon-msan-loongarch64-Linux :: sanitizer_coverage_trace_pc_guard-dso.cpp SanitizerCommon-msan-loongarch64-Linux :: sanitizer_coverage_trace_pc_guard.cpp ``` The reason is that sancov could not resolve pcaddu18i+jirl call sequence correctly and caused mismatches between coverage points in the binary and the .sancov file: ``` ERROR: Coverage points in binary and .sancov file do not match. ``` NOTE: A similar issue might also occur on RISC-V when relaxation is disabled (not verified). This commit can also fix for it.
2 parents 9d97c62 + 9c994f5 commit a266d9e

File tree

3 files changed

+87
-4
lines changed

3 files changed

+87
-4
lines changed

lld/test/ELF/loongarch-call36.s

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@
88
## hi20 = target - pc + (1 << 17) >> 18 = 0x60020 - 0x20010 + 0x20000 >> 18 = 1
99
## lo18 = target - pc & (1 << 18) - 1 = 0x60020 - 0x20010 & 0x3ffff = 16
1010
# EXE1: 20010: pcaddu18i $t0, 1
11-
# EXE1-NEXT: 20014: jirl $zero, $t0, 16
11+
# EXE1-NEXT: 20014: jirl $zero, $t0, 16 <foo>
1212

1313
# RUN: ld.lld %t/a.o --section-start=.text=0x20010 --section-start=.sec.foo=0x40020 -o %t/exe2
1414
# RUN: llvm-objdump --no-show-raw-insn -d %t/exe2 | FileCheck --match-full-lines %s --check-prefix=EXE2
1515
## hi20 = target - pc + (1 << 17) >> 18 = 0x40020 - 0x20010 + 0x20000 >> 18 = 1
1616
## lo18 = target - pc & (1 << 18) - 1 = 0x40020 - 0x20010 & 0x3ffff = -131056
1717
# EXE2: 20010: pcaddu18i $t0, 1
18-
# EXE2-NEXT: 20014: jirl $zero, $t0, -131056
18+
# EXE2-NEXT: 20014: jirl $zero, $t0, -131056 <foo>
1919

2020
# RUN: ld.lld %t/a.o -shared -T %t/a.t -o %t/a.so
2121
# RUN: llvm-readelf -x .got.plt %t/a.so | FileCheck --check-prefix=GOTPLT %s
@@ -34,7 +34,7 @@
3434
## hi20 = foo@plt - pc + (1 << 17) >> 18 = 0x1234520 - 0x1274670 + 0x20000 >> 18 = -1
3535
## lo18 = foo@plt - pc & (1 << 18) - 1 = 0x1234520 - 0x1274670 & 0x3ffff = -336
3636
# SO-NEXT: pcaddu18i $t0, -1{{$}}
37-
# SO-NEXT: jirl $zero, $t0, -336{{$}}
37+
# SO-NEXT: jirl $zero, $t0, -336 <.plt+0x20>{{$}}
3838

3939
# GOTPLT: section '.got.plt':
4040
# GOTPLT-NEXT: 0x01274730 00000000 00000000 00000000 00000000

llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCTargetDesc.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/MC/MCSubtargetInfo.h"
2727
#include "llvm/MC/TargetRegistry.h"
2828
#include "llvm/Support/Compiler.h"
29+
#include <bitset>
2930

3031
#define GET_INSTRINFO_MC_DESC
3132
#define ENABLE_INSTR_PREDICATE_VERIFIER
@@ -95,10 +96,79 @@ createLoongArchAsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS,
9596
namespace {
9697

9798
class LoongArchMCInstrAnalysis : public MCInstrAnalysis {
99+
int64_t GPRState[31] = {};
100+
std::bitset<31> GPRValidMask;
101+
102+
static bool isGPR(MCRegister Reg) {
103+
return Reg >= LoongArch::R0 && Reg <= LoongArch::R31;
104+
}
105+
106+
static unsigned getRegIndex(MCRegister Reg) {
107+
assert(isGPR(Reg) && Reg != LoongArch::R0 && "Invalid GPR reg");
108+
return Reg - LoongArch::R1;
109+
}
110+
111+
void setGPRState(MCRegister Reg, std::optional<int64_t> Value) {
112+
if (Reg == LoongArch::R0)
113+
return;
114+
115+
auto Index = getRegIndex(Reg);
116+
117+
if (Value) {
118+
GPRState[Index] = *Value;
119+
GPRValidMask.set(Index);
120+
} else {
121+
GPRValidMask.reset(Index);
122+
}
123+
}
124+
125+
std::optional<int64_t> getGPRState(MCRegister Reg) const {
126+
if (Reg == LoongArch::R0)
127+
return 0;
128+
129+
auto Index = getRegIndex(Reg);
130+
131+
if (GPRValidMask.test(Index))
132+
return GPRState[Index];
133+
return std::nullopt;
134+
}
135+
98136
public:
99137
explicit LoongArchMCInstrAnalysis(const MCInstrInfo *Info)
100138
: MCInstrAnalysis(Info) {}
101139

140+
void resetState() override { GPRValidMask.reset(); }
141+
142+
void updateState(const MCInst &Inst, uint64_t Addr) override {
143+
// Terminators mark the end of a basic block which means the sequentially
144+
// next instruction will be the first of another basic block and the current
145+
// state will typically not be valid anymore. For calls, we assume all
146+
// registers may be clobbered by the callee (TODO: should we take the
147+
// calling convention into account?).
148+
if (isTerminator(Inst) || isCall(Inst)) {
149+
resetState();
150+
return;
151+
}
152+
153+
switch (Inst.getOpcode()) {
154+
default: {
155+
// Clear the state of all defined registers for instructions that we don't
156+
// explicitly support.
157+
auto NumDefs = Info->get(Inst.getOpcode()).getNumDefs();
158+
for (unsigned I = 0; I < NumDefs; ++I) {
159+
auto DefReg = Inst.getOperand(I).getReg();
160+
if (isGPR(DefReg))
161+
setGPRState(DefReg, std::nullopt);
162+
}
163+
break;
164+
}
165+
case LoongArch::PCADDU18I:
166+
setGPRState(Inst.getOperand(0).getReg(),
167+
Addr + SignExtend64<32>(Inst.getOperand(1).getImm() << 18));
168+
break;
169+
}
170+
}
171+
102172
bool evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size,
103173
uint64_t &Target) const override {
104174
unsigned NumOps = Inst.getNumOperands();
@@ -108,6 +178,14 @@ class LoongArchMCInstrAnalysis : public MCInstrAnalysis {
108178
return true;
109179
}
110180

181+
if (Inst.getOpcode() == LoongArch::JIRL) {
182+
if (auto TargetRegState = getGPRState(Inst.getOperand(1).getReg())) {
183+
Target = *TargetRegState + Inst.getOperand(2).getImm();
184+
return true;
185+
}
186+
return false;
187+
}
188+
111189
return false;
112190
}
113191

llvm/tools/sancov/sancov.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ static void getObjectCoveragePoints(const object::ObjectFile &O,
730730
std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo());
731731
failIfEmpty(MII, "no instruction info for target " + TripleName);
732732

733-
std::unique_ptr<const MCInstrAnalysis> MIA(
733+
std::unique_ptr<MCInstrAnalysis> MIA(
734734
TheTarget->createMCInstrAnalysis(MII.get()));
735735
failIfEmpty(MIA, "no instruction analysis info for target " + TripleName);
736736

@@ -750,6 +750,9 @@ static void getObjectCoveragePoints(const object::ObjectFile &O,
750750
failIfError(BytesStr);
751751
ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(*BytesStr);
752752

753+
if (MIA)
754+
MIA->resetState();
755+
753756
for (uint64_t Index = 0, Size = 0; Index < Section.getSize();
754757
Index += Size) {
755758
MCInst Inst;
@@ -760,6 +763,7 @@ static void getObjectCoveragePoints(const object::ObjectFile &O,
760763
Size = std::min<uint64_t>(
761764
ThisBytes.size(),
762765
DisAsm->suggestBytesToSkip(ThisBytes, ThisAddr));
766+
MIA->resetState();
763767
continue;
764768
}
765769
uint64_t Addr = Index + SectionAddr;
@@ -770,6 +774,7 @@ static void getObjectCoveragePoints(const object::ObjectFile &O,
770774
MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target) &&
771775
SanCovAddrs.find(Target) != SanCovAddrs.end())
772776
Addrs->insert(CovPoint);
777+
MIA->updateState(Inst, Addr);
773778
}
774779
}
775780
}

0 commit comments

Comments
 (0)