LLVM 22.0.0git
DWARFCFIAnalysis.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "Registers.h"
11#include "llvm/ADT/ArrayRef.h"
12#include "llvm/ADT/SmallSet.h"
14#include "llvm/ADT/Twine.h"
17#include "llvm/MC/MCAsmInfo.h"
18#include "llvm/MC/MCContext.h"
19#include "llvm/MC/MCDwarf.h"
20#include "llvm/MC/MCExpr.h"
21#include "llvm/MC/MCInst.h"
22#include "llvm/MC/MCInstrInfo.h"
23#include "llvm/MC/MCRegister.h"
25#include "llvm/MC/MCStreamer.h"
30#include <optional>
31
32using namespace llvm;
33
36 int64_t Offset;
37
39 : Reg(Reg), Offset(Offset) {}
40
41 bool operator==(const CFARegOffsetInfo &RHS) const {
42 return Reg == RHS.Reg && Offset == RHS.Offset;
43 }
44};
45
46static std::optional<CFARegOffsetInfo>
48 auto CFALocation = UnwindRow.getCFAValue();
49 if (CFALocation.getLocation() !=
50 dwarf::UnwindLocation::Location::RegPlusOffset)
51 return std::nullopt;
52
53 return CFARegOffsetInfo(CFALocation.getRegister(), CFALocation.getOffset());
54}
55
58 auto MaybeLoc = UnwindRow.getRegisterLocations().getRegisterLocation(Reg);
59 assert(MaybeLoc && "the register should be included in the unwinding row");
60 auto Loc = *MaybeLoc;
61
62 switch (Loc.getLocation()) {
63 case dwarf::UnwindLocation::Location::Unspecified:
64 case dwarf::UnwindLocation::Location::Undefined:
65 case dwarf::UnwindLocation::Location::Constant:
66 case dwarf::UnwindLocation::Location::CFAPlusOffset:
67 // [CFA + offset] does not depend on any register because the CFA value is
68 // constant throughout the entire frame; only the way to calculate it might
69 // change.
70 case dwarf::UnwindLocation::Location::DWARFExpr:
71 // TODO: Expressions are not supported yet, but if they were to be
72 // supported, all the registers used in an expression should extracted and
73 // returned here.
74 return {};
75 case dwarf::UnwindLocation::Location::Same:
76 return {Reg};
77 case dwarf::UnwindLocation::Location::RegPlusOffset:
78 return {Loc.getRegister()};
79 }
80 llvm_unreachable("Unknown dwarf::UnwindLocation::Location enum");
81}
82
84 bool IsEH,
86 : State(Context), Context(Context), MCII(MCII),
87 MCRI(Context->getRegisterInfo()), IsEH(IsEH) {
88
89 for (auto LLVMReg : getTrackingRegs(MCRI)) {
90 if (MCRI->get(LLVMReg).IsArtificial || MCRI->get(LLVMReg).IsConstant)
91 continue;
92
93 DWARFRegNum Reg = MCRI->getDwarfRegNum(LLVMReg, IsEH);
94 // TODO: this should be `undefined` instead of `same_value`, but because
95 // initial frame state doesn't have any directives about callee saved
96 // registers, every register is tracked. After initial frame state is
97 // corrected, this should be changed.
98 State.update(MCCFIInstruction::createSameValue(nullptr, Reg));
99 }
100
101 // TODO: Ignoring PC should be in the initial frame state.
103 nullptr, MCRI->getDwarfRegNum(MCRI->getProgramCounter(), IsEH)));
104
105 for (auto &&InitialFrameStateCFIDirective :
106 Context->getAsmInfo()->getInitialFrameState())
107 State.update(InitialFrameStateCFIDirective);
108
109 auto MaybeCurrentRow = State.getCurrentUnwindRow();
110 assert(MaybeCurrentRow && "there should be at least one row");
111 auto MaybeCFA = getCFARegOffsetInfo(*MaybeCurrentRow);
112 assert(MaybeCFA &&
113 "the CFA information should be describable in [reg + offset] in here");
114 auto CFA = *MaybeCFA;
115
116 // TODO: CFA register callee value is CFA's value, this should be in initial
117 // frame state.
118 State.update(MCCFIInstruction::createOffset(nullptr, CFA.Reg, 0));
119
120 // Applying the prologue after default assumptions to overwrite them.
121 for (auto &&Directive : Prologue)
122 State.update(Directive);
123}
124
126 ArrayRef<MCCFIInstruction> Directives) {
127 const MCInstrDesc &MCInstInfo = MCII.get(Inst.getOpcode());
128
129 auto MaybePrevRow = State.getCurrentUnwindRow();
130 assert(MaybePrevRow && "the analysis should have initialized the "
131 "state with at least one row by now");
132 auto PrevRow = *MaybePrevRow;
133
134 for (auto &&Directive : Directives)
135 State.update(Directive);
136
138 for (unsigned I = 0; I < MCInstInfo.NumImplicitUses; I++)
139 Reads.insert(MCRI->getDwarfRegNum(
140 getSuperReg(MCRI, MCInstInfo.implicit_uses()[I]), IsEH));
141 for (unsigned I = 0; I < MCInstInfo.NumImplicitDefs; I++)
143 getSuperReg(MCRI, MCInstInfo.implicit_defs()[I]), IsEH));
144
145 for (unsigned I = 0; I < Inst.getNumOperands(); I++) {
146 auto &&Op = Inst.getOperand(I);
147 if (Op.isReg()) {
148 if (I < MCInstInfo.getNumDefs())
150 MCRI->getDwarfRegNum(getSuperReg(MCRI, Op.getReg()), IsEH));
151 else if (Op.getReg())
152 Reads.insert(
153 MCRI->getDwarfRegNum(getSuperReg(MCRI, Op.getReg()), IsEH));
154 }
155 }
156
157 auto MaybeNextRow = State.getCurrentUnwindRow();
158 assert(MaybeNextRow && "previous row existed, so should the current row");
159 auto NextRow = *MaybeNextRow;
160
161 checkCFADiff(Inst, PrevRow, NextRow, Reads, Writes);
162
163 for (auto LLVMReg : getTrackingRegs(MCRI)) {
164 DWARFRegNum Reg = MCRI->getDwarfRegNum(LLVMReg, IsEH);
165
166 checkRegDiff(Inst, Reg, PrevRow, NextRow, Reads, Writes);
167 }
168}
169
170void DWARFCFIAnalysis::checkRegDiff(const MCInst &Inst, DWARFRegNum Reg,
171 const dwarf::UnwindRow &PrevRow,
172 const dwarf::UnwindRow &NextRow,
173 const SmallSet<DWARFRegNum, 4> &Reads,
175 auto MaybePrevLoc = PrevRow.getRegisterLocations().getRegisterLocation(Reg);
176 auto MaybeNextLoc = NextRow.getRegisterLocations().getRegisterLocation(Reg);
177
178 // All the tracked registers are added during initiation. So if a register is
179 // not added, should stay the same during execution and vice versa.
180 if (!MaybePrevLoc) {
181 assert(!MaybeNextLoc && "the register unwind info suddenly appeared here");
182 return;
183 }
184 assert(MaybeNextLoc && "the register unwind info suddenly vanished here");
185
186 auto PrevLoc = MaybePrevLoc.value();
187 auto NextLoc = MaybeNextLoc.value();
188
189 auto MaybeLLVMReg = MCRI->getLLVMRegNum(Reg, IsEH);
190 if (!MaybeLLVMReg) {
191 if (!(PrevLoc == NextLoc))
192 Context->reportWarning(
193 Inst.getLoc(),
194 formatv("the dwarf register {0} does not have a LLVM number, but its "
195 "unwind info changed. Ignoring this change",
196 Reg));
197 return;
198 }
199 const char *RegName = MCRI->getName(*MaybeLLVMReg);
200
201 // Each case is annotated with its corresponding number as described in
202 // `llvm/include/llvm/DWARFCFIChecker/DWARFCFIAnalysis.h`.
203
204 // TODO: Expressions are not supported yet, but if they were to be supported,
205 // note that structure equality for expressions is defined as follows: Two
206 // expressions are structurally equal if they become the same after you
207 // replace every operand with a placeholder.
208
209 if (PrevLoc == NextLoc) { // Case 1
210 for (DWARFRegNum UsedReg : getUnwindRuleRegSet(PrevRow, Reg))
211 if (Writes.count(UsedReg)) { // Case 1.b
212 auto MaybeLLVMUsedReg = MCRI->getLLVMRegNum(UsedReg, IsEH);
213 assert(MaybeLLVMUsedReg && "instructions will always write to a "
214 "register that has an LLVM register number");
215 Context->reportError(
216 Inst.getLoc(),
217 formatv("changed register {1}, that register {0}'s unwinding rule "
218 "uses, but there is no CFI directives about it",
219 RegName, MCRI->getName(*MaybeLLVMUsedReg)));
220 return;
221 }
222 return; // Case 1.a
223 }
224 // Case 2
225 if (PrevLoc.getLocation() != NextLoc.getLocation()) { // Case 2.a
226 Context->reportWarning(
227 Inst.getLoc(),
228 formatv("validating changes happening to register {0} unwinding "
229 "rule structure is not implemented yet",
230 RegName));
231 return;
232 }
233 auto &&PrevRegSet = getUnwindRuleRegSet(PrevRow, Reg);
234 if (PrevRegSet != getUnwindRuleRegSet(NextRow, Reg)) { // Case 2.b
235 Context->reportWarning(
236 Inst.getLoc(),
237 formatv("validating changes happening to register {0} unwinding "
238 "rule register set is not implemented yet",
239 RegName));
240 return;
241 }
242 // Case 2.c
243 for (DWARFRegNum UsedReg : PrevRegSet)
244 if (Writes.count(UsedReg)) { // Case 2.c.i
245 Context->reportWarning(
246 Inst.getLoc(),
247 formatv("register {0} unwinding rule's offset is changed, and one of "
248 "the rule's registers is modified, but validating the "
249 "modification amount is not implemented yet",
250 RegName));
251 return;
252 }
253 // Case 2.c.ii
254 Context->reportError(
255 Inst.getLoc(), formatv("register {0} unwinding rule's offset is changed, "
256 "but not any of the rule's registers are modified",
257 RegName));
258}
259
260void DWARFCFIAnalysis::checkCFADiff(const MCInst &Inst,
261 const dwarf::UnwindRow &PrevRow,
262 const dwarf::UnwindRow &NextRow,
263 const SmallSet<DWARFRegNum, 4> &Reads,
265
266 auto MaybePrevCFA = getCFARegOffsetInfo(PrevRow);
267 auto MaybeNextCFA = getCFARegOffsetInfo(NextRow);
268
269 if (!MaybePrevCFA) {
270 if (MaybeNextCFA) {
271 Context->reportWarning(Inst.getLoc(),
272 "CFA rule changed to [reg + offset], this "
273 "transition will not be checked");
274 return;
275 }
276
277 Context->reportWarning(Inst.getLoc(),
278 "CFA rule is not [reg + offset], not checking it");
279 return;
280 }
281
282 if (!MaybeNextCFA) {
283 Context->reportWarning(Inst.getLoc(),
284 "CFA rule changed from [reg + offset], this "
285 "transition will not be checked");
286 return;
287 }
288
289 auto PrevCFA = *MaybePrevCFA;
290 auto NextCFA = *MaybeNextCFA;
291
292 auto MaybeLLVMPrevReg = MCRI->getLLVMRegNum(PrevCFA.Reg, IsEH);
293 const char *PrevCFARegName =
294 MaybeLLVMPrevReg ? MCRI->getName(*MaybeLLVMPrevReg) : "";
295 auto MaybeLLVMNextReg = MCRI->getLLVMRegNum(NextCFA.Reg, IsEH);
296 const char *NextCFARegName =
297 MaybeLLVMNextReg ? MCRI->getName(*MaybeLLVMNextReg) : "";
298
299 if (PrevCFA == NextCFA) { // Case 1
300 if (!Writes.count(PrevCFA.Reg)) // Case 1.a
301 return;
302 // Case 1.b
303 Context->reportError(
304 Inst.getLoc(),
305 formatv("modified CFA register {0} but not changed CFA rule",
306 PrevCFARegName));
307 return;
308 }
309
310 if (PrevCFA.Reg != NextCFA.Reg) { // Case 2.b
311 Context->reportWarning(
312 Inst.getLoc(),
313 formatv("CFA register changed from register {0} to register {1}, "
314 "validating this change is not implemented yet",
315 PrevCFARegName, NextCFARegName));
316 return;
317 }
318 // Case 2.c
319 if (Writes.count(PrevCFA.Reg)) { // Case 2.c.i
320 Context->reportWarning(
321 Inst.getLoc(), formatv("CFA offset is changed from {0} to {1}, and CFA "
322 "register {2} is modified, but validating the "
323 "modification amount is not implemented yet",
324 PrevCFA.Offset, NextCFA.Offset, PrevCFARegName));
325 return;
326 }
327 // Case 2.c.ii
328 Context->reportError(
329 Inst.getLoc(),
330 formatv("did not modify CFA register {0} but changed CFA rule",
331 PrevCFARegName));
332}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static std::optional< CFARegOffsetInfo > getCFARegOffsetInfo(const dwarf::UnwindRow &UnwindRow)
static SmallSet< DWARFRegNum, 4 > getUnwindRuleRegSet(const dwarf::UnwindRow &UnwindRow, DWARFRegNum Reg)
This file declares DWARFCFIAnalysis class.
This file declares DWARFCFIState class.
SmallVector< uint32_t, 0 > Writes
Definition: ELF_riscv.cpp:497
#define RegName(no)
#define I(x, y, z)
Definition: MD5.cpp:58
This file contains helper functions to find and list registers that are tracked by the unwinding info...
This file defines the SmallSet class.
This file defines the SmallVector class.
Value * RHS
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
LLVM_ABI void update(const MCInst &Inst, ArrayRef< MCCFIInstruction > Directives)
LLVM_ABI DWARFCFIAnalysis(MCContext *Context, MCInstrInfo const &MCII, bool IsEH, ArrayRef< MCCFIInstruction > Prologue)
LLVM_ABI std::optional< dwarf::UnwindRow > getCurrentUnwindRow() const
LLVM_ABI void update(const MCCFIInstruction &Directive)
This method updates the state by applying Directive to the current state.
This class represents an Operation in the Expression.
static MCCFIInstruction createUndefined(MCSymbol *L, unsigned Register, SMLoc Loc={})
.cfi_undefined From now on the previous value of Register can't be restored anymore.
Definition: MCDwarf.h:673
static MCCFIInstruction createOffset(MCSymbol *L, unsigned Register, int64_t Offset, SMLoc Loc={})
.cfi_offset Previous value of Register is saved at offset Offset from CFA.
Definition: MCDwarf.h:627
static MCCFIInstruction createSameValue(MCSymbol *L, unsigned Register, SMLoc Loc={})
.cfi_same_value Current value of Register is the same as in the previous frame.
Definition: MCDwarf.h:680
Context object for machine code objects.
Definition: MCContext.h:83
LLVM_ABI void reportWarning(SMLoc L, const Twine &Msg)
Definition: MCContext.cpp:1122
LLVM_ABI void reportError(SMLoc L, const Twine &Msg)
Definition: MCContext.cpp:1115
Instances of this class represent a single low-level machine instruction.
Definition: MCInst.h:188
unsigned getNumOperands() const
Definition: MCInst.h:212
SMLoc getLoc() const
Definition: MCInst.h:208
unsigned getOpcode() const
Definition: MCInst.h:202
const MCOperand & getOperand(unsigned i) const
Definition: MCInst.h:210
Describe properties that are true of each instruction in the target description file.
Definition: MCInstrDesc.h:199
unsigned char NumImplicitUses
Definition: MCInstrDesc.h:211
unsigned char NumImplicitDefs
Definition: MCInstrDesc.h:212
unsigned getNumDefs() const
Return the number of MachineOperands that are register definitions.
Definition: MCInstrDesc.h:249
ArrayRef< MCPhysReg > implicit_defs() const
Return a list of registers that are potentially written by any instance of this machine instruction.
Definition: MCInstrDesc.h:581
ArrayRef< MCPhysReg > implicit_uses() const
Return a list of registers that are potentially read by any instance of this machine instruction.
Definition: MCInstrDesc.h:567
Interface to description of machine instruction set.
Definition: MCInstrInfo.h:27
const MCInstrDesc & get(unsigned Opcode) const
Return the machine instruction descriptor that corresponds to the specified instruction opcode.
Definition: MCInstrInfo.h:64
MCRegister getProgramCounter() const
Return the register which is the program counter.
const MCRegisterDesc & get(MCRegister Reg) const
Provide a get method, equivalent to [], but more useful with a pointer to this object.
const char * getName(MCRegister RegNo) const
Return the human-readable symbolic target-specific name for the specified physical register.
virtual int64_t getDwarfRegNum(MCRegister RegNum, bool isEH) const
Map a target register to an equivalent dwarf register number.
std::optional< MCRegister > getLLVMRegNum(uint64_t RegNum, bool isEH) const
Map a dwarf register back to a target register.
SmallSet - This maintains a set of unique values, optimizing for the case when the set is small (less...
Definition: SmallSet.h:134
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
Definition: SmallSet.h:182
iterator insert(iterator I, T &&Elt)
Definition: SmallVector.h:806
std::optional< UnwindLocation > getRegisterLocation(uint32_t RegNum) const
Return the location for the register in RegNum if there is a location.
A class that represents a single row in the unwind table that is decoded by parsing the DWARF Call Fr...
UnwindLocation & getCFAValue()
RegisterLocations & getRegisterLocations()
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
SmallVector< MCPhysReg > getTrackingRegs(const MCRegisterInfo *MCRI)
Definition: Registers.h:45
MCPhysReg getSuperReg(const MCRegisterInfo *MCRI, MCPhysReg Reg)
Definition: Registers.h:53
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
bool operator==(const CFARegOffsetInfo &RHS) const
CFARegOffsetInfo(DWARFRegNum Reg, int64_t Offset)