LLVM 23.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
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() !=
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()) {
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.
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 {};
76 return {Reg};
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.NumImplicitDefs; I++)
139 Writes.insert(MCRI->getDwarfRegNum(
140 getSuperReg(MCRI, MCInstInfo.implicit_defs()[I]), IsEH));
141
142 for (unsigned I = 0; I < Inst.getNumOperands(); I++) {
143 auto &&Op = Inst.getOperand(I);
144 if (Op.isReg()) {
145 if (I < MCInstInfo.getNumDefs())
146 Writes.insert(
147 MCRI->getDwarfRegNum(getSuperReg(MCRI, Op.getReg()), IsEH));
148 }
149 }
150
151 auto MaybeNextRow = State.getCurrentUnwindRow();
152 assert(MaybeNextRow && "previous row existed, so should the current row");
153 auto NextRow = *MaybeNextRow;
154
155 checkCFADiff(Inst, PrevRow, NextRow, Writes);
156
157 for (auto LLVMReg : getTrackingRegs(MCRI)) {
158 DWARFRegNum Reg = MCRI->getDwarfRegNum(LLVMReg, IsEH);
159
160 checkRegDiff(Inst, Reg, PrevRow, NextRow, Writes);
161 }
162}
163
164void DWARFCFIAnalysis::checkRegDiff(const MCInst &Inst, DWARFRegNum Reg,
165 const dwarf::UnwindRow &PrevRow,
166 const dwarf::UnwindRow &NextRow,
167 const SmallSet<DWARFRegNum, 4> &Writes) {
168 auto MaybePrevLoc = PrevRow.getRegisterLocations().getRegisterLocation(Reg);
169 auto MaybeNextLoc = NextRow.getRegisterLocations().getRegisterLocation(Reg);
170
171 // All the tracked registers are added during initiation. So if a register is
172 // not added, should stay the same during execution and vice versa.
173 if (!MaybePrevLoc) {
174 assert(!MaybeNextLoc && "the register unwind info suddenly appeared here");
175 return;
176 }
177 assert(MaybeNextLoc && "the register unwind info suddenly vanished here");
178
179 auto PrevLoc = MaybePrevLoc.value();
180 auto NextLoc = MaybeNextLoc.value();
181
182 auto MaybeLLVMReg = MCRI->getLLVMRegNum(Reg, IsEH);
183 if (!MaybeLLVMReg) {
184 if (!(PrevLoc == NextLoc))
185 Context->reportWarning(
186 Inst.getLoc(),
187 formatv("the dwarf register {0} does not have a LLVM number, but its "
188 "unwind info changed. Ignoring this change",
189 Reg));
190 return;
191 }
192 const char *RegName = MCRI->getName(*MaybeLLVMReg);
193
194 // Each case is annotated with its corresponding number as described in
195 // `llvm/include/llvm/DWARFCFIChecker/DWARFCFIAnalysis.h`.
196
197 // TODO: Expressions are not supported yet, but if they were to be supported,
198 // note that structure equality for expressions is defined as follows: Two
199 // expressions are structurally equal if they become the same after you
200 // replace every operand with a placeholder.
201
202 if (PrevLoc == NextLoc) { // Case 1
203 for (DWARFRegNum UsedReg : getUnwindRuleRegSet(PrevRow, Reg))
204 if (Writes.count(UsedReg)) { // Case 1.b
205 auto MaybeLLVMUsedReg = MCRI->getLLVMRegNum(UsedReg, IsEH);
206 assert(MaybeLLVMUsedReg && "instructions will always write to a "
207 "register that has an LLVM register number");
208 Context->reportError(
209 Inst.getLoc(),
210 formatv("changed register {1}, that register {0}'s unwinding rule "
211 "uses, but there is no CFI directives about it",
212 RegName, MCRI->getName(*MaybeLLVMUsedReg)));
213 return;
214 }
215 return; // Case 1.a
216 }
217 // Case 2
218 if (PrevLoc.getLocation() != NextLoc.getLocation()) { // Case 2.a
219 Context->reportWarning(
220 Inst.getLoc(),
221 formatv("validating changes happening to register {0} unwinding "
222 "rule structure is not implemented yet",
223 RegName));
224 return;
225 }
226 auto &&PrevRegSet = getUnwindRuleRegSet(PrevRow, Reg);
227 if (PrevRegSet != getUnwindRuleRegSet(NextRow, Reg)) { // Case 2.b
228 Context->reportWarning(
229 Inst.getLoc(),
230 formatv("validating changes happening to register {0} unwinding "
231 "rule register set is not implemented yet",
232 RegName));
233 return;
234 }
235 // Case 2.c
236 for (DWARFRegNum UsedReg : PrevRegSet)
237 if (Writes.count(UsedReg)) { // Case 2.c.i
238 Context->reportWarning(
239 Inst.getLoc(),
240 formatv("register {0} unwinding rule's offset is changed, and one of "
241 "the rule's registers is modified, but validating the "
242 "modification amount is not implemented yet",
243 RegName));
244 return;
245 }
246 // Case 2.c.ii
247 Context->reportError(
248 Inst.getLoc(), formatv("register {0} unwinding rule's offset is changed, "
249 "but not any of the rule's registers are modified",
250 RegName));
251}
252
253void DWARFCFIAnalysis::checkCFADiff(const MCInst &Inst,
254 const dwarf::UnwindRow &PrevRow,
255 const dwarf::UnwindRow &NextRow,
256 const SmallSet<DWARFRegNum, 4> &Writes) {
257
258 auto MaybePrevCFA = getCFARegOffsetInfo(PrevRow);
259 auto MaybeNextCFA = getCFARegOffsetInfo(NextRow);
260
261 if (!MaybePrevCFA) {
262 if (MaybeNextCFA) {
263 Context->reportWarning(Inst.getLoc(),
264 "CFA rule changed to [reg + offset], this "
265 "transition will not be checked");
266 return;
267 }
268
269 Context->reportWarning(Inst.getLoc(),
270 "CFA rule is not [reg + offset], not checking it");
271 return;
272 }
273
274 if (!MaybeNextCFA) {
275 Context->reportWarning(Inst.getLoc(),
276 "CFA rule changed from [reg + offset], this "
277 "transition will not be checked");
278 return;
279 }
280
281 auto PrevCFA = *MaybePrevCFA;
282 auto NextCFA = *MaybeNextCFA;
283
284 auto MaybeLLVMPrevReg = MCRI->getLLVMRegNum(PrevCFA.Reg, IsEH);
285 const char *PrevCFARegName =
286 MaybeLLVMPrevReg ? MCRI->getName(*MaybeLLVMPrevReg) : "";
287 auto MaybeLLVMNextReg = MCRI->getLLVMRegNum(NextCFA.Reg, IsEH);
288 const char *NextCFARegName =
289 MaybeLLVMNextReg ? MCRI->getName(*MaybeLLVMNextReg) : "";
290
291 if (PrevCFA == NextCFA) { // Case 1
292 if (!Writes.count(PrevCFA.Reg)) // Case 1.a
293 return;
294 // Case 1.b
295 Context->reportError(
296 Inst.getLoc(),
297 formatv("modified CFA register {0} but not changed CFA rule",
298 PrevCFARegName));
299 return;
300 }
301
302 if (PrevCFA.Reg != NextCFA.Reg) { // Case 2.b
303 Context->reportWarning(
304 Inst.getLoc(),
305 formatv("CFA register changed from register {0} to register {1}, "
306 "validating this change is not implemented yet",
307 PrevCFARegName, NextCFARegName));
308 return;
309 }
310 // Case 2.c
311 if (Writes.count(PrevCFA.Reg)) { // Case 2.c.i
312 Context->reportWarning(
313 Inst.getLoc(), formatv("CFA offset is changed from {0} to {1}, and CFA "
314 "register {2} is modified, but validating the "
315 "modification amount is not implemented yet",
316 PrevCFA.Offset, NextCFA.Offset, PrevCFARegName));
317 return;
318 }
319 // Case 2.c.ii
320 Context->reportError(
321 Inst.getLoc(),
322 formatv("did not modify CFA register {0} but changed CFA rule",
323 PrevCFARegName));
324}
for(const MachineOperand &MO :llvm::drop_begin(OldMI.operands(), Desc.getNumOperands()))
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.
#define RegName(no)
#define I(x, y, z)
Definition MD5.cpp:57
Register Reg
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.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
LLVM_ABI void update(const MCInst &Inst, ArrayRef< MCCFIInstruction > Directives)
LLVM_ABI DWARFCFIAnalysis(MCContext *Context, MCInstrInfo const &MCII, bool IsEH, ArrayRef< MCCFIInstruction > Prologue)
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:664
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:618
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:671
Context object for machine code objects.
Definition MCContext.h:83
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.
unsigned char NumImplicitDefs
unsigned getNumDefs() const
Return the number of MachineOperands that are register definitions.
ArrayRef< MCPhysReg > implicit_defs() const
Return a list of registers that are potentially written by any instance of this machine instruction.
Interface to description of machine instruction set.
Definition MCInstrInfo.h:27
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:133
size_type count(const T &V) const
count - Return 1 if the element is in the set, 0 otherwise.
Definition SmallSet.h:175
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:183
std::optional< UnwindLocation > getRegisterLocation(uint32_t RegNum) const
Return the location for the register in RegNum if there is a location.
@ Undefined
Register is not available and can't be recovered.
@ Constant
Value is a constant value contained in "Offset": reg = Offset.
@ DWARFExpr
Register or CFA value is in or at a value found by evaluating a DWARF expression: reg = eval(dwarf_ex...
@ Same
Register value is in the register, nothing needs to be done to unwind it: reg = reg.
@ CFAPlusOffset
Register is in or at the CFA plus an offset: reg = CFA + offset reg = defef(CFA + offset)
@ RegPlusOffset
Register or CFA is in or at a register plus offset, optionally in an address space: reg = reg + offse...
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 Types.h:26
SmallVector< MCPhysReg > getTrackingRegs(const MCRegisterInfo *MCRI)
Definition Registers.h:44
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
uint32_t DWARFRegNum
DWARFExpression::Operation Op
MCRegister getSuperReg(const MCRegisterInfo *MCRI, MCRegister Reg)
Definition Registers.h:52
bool operator==(const CFARegOffsetInfo &RHS) const
CFARegOffsetInfo(DWARFRegNum Reg, int64_t Offset)