LLVM 24.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 // Based on dwarf documentation, the default rule for all columns before
95 // interpretation of the initial instructions is the undefined rule.
96
97 // For now, this tool depends on the user to write a prologue that
98 // establishes the rules required for proper validation of the rest of the
99 // function.
100 State.update(MCCFIInstruction::createUndefined(nullptr, Reg));
101 }
102
104 nullptr, MCRI->getDwarfRegNum(MCRI->getProgramCounter(), IsEH)));
105
106 for (auto &&InitialFrameStateCFIDirective :
107 Context->getAsmInfo().getInitialFrameState())
108 State.update(InitialFrameStateCFIDirective);
109
110 // Applying the prologue after default assumptions to overwrite them.
111 for (auto &&Directive : Prologue)
112 State.update(Directive);
113}
114
116 ArrayRef<MCCFIInstruction> Directives) {
117 const MCInstrDesc &MCInstInfo = MCII.get(Inst.getOpcode());
118
119 auto MaybePrevRow = State.getCurrentUnwindRow();
120 assert(MaybePrevRow && "the analysis should have initialized the "
121 "state with at least one row by now");
122 auto PrevRow = *MaybePrevRow;
123
124 for (auto &&Directive : Directives)
125 State.update(Directive);
126
128 for (unsigned I = 0; I < MCInstInfo.NumImplicitDefs; I++)
129 Writes.insert(MCRI->getDwarfRegNum(
130 getSuperReg(MCRI, MCInstInfo.implicit_defs()[I]), IsEH));
131
132 for (unsigned I = 0; I < Inst.getNumOperands(); I++) {
133 auto &&Op = Inst.getOperand(I);
134 if (Op.isReg()) {
135 if (I < MCInstInfo.getNumDefs())
136 Writes.insert(
137 MCRI->getDwarfRegNum(getSuperReg(MCRI, Op.getReg()), IsEH));
138 }
139 }
140
141 auto MaybeNextRow = State.getCurrentUnwindRow();
142 assert(MaybeNextRow && "previous row existed, so should the current row");
143 auto NextRow = *MaybeNextRow;
144
145 checkCFADiff(Inst, PrevRow, NextRow, Writes);
146
147 for (auto LLVMReg : getTrackingRegs(MCRI)) {
148 DWARFRegNum Reg = MCRI->getDwarfRegNum(LLVMReg, IsEH);
149
150 checkRegDiff(Inst, Reg, PrevRow, NextRow, Writes);
151 }
152}
153
154void DWARFCFIAnalysis::checkRegDiff(const MCInst &Inst, DWARFRegNum Reg,
155 const dwarf::UnwindRow &PrevRow,
156 const dwarf::UnwindRow &NextRow,
157 const SmallSet<DWARFRegNum, 4> &Writes) {
158 auto MaybePrevLoc = PrevRow.getRegisterLocations().getRegisterLocation(Reg);
159 auto MaybeNextLoc = NextRow.getRegisterLocations().getRegisterLocation(Reg);
160
161 // All the tracked registers are added during initiation. So if a register is
162 // not added, should stay the same during execution and vice versa.
163 if (!MaybePrevLoc) {
164 assert(!MaybeNextLoc && "the register unwind info suddenly appeared here");
165 return;
166 }
167 assert(MaybeNextLoc && "the register unwind info suddenly vanished here");
168
169 auto PrevLoc = MaybePrevLoc.value();
170 auto NextLoc = MaybeNextLoc.value();
171
172 auto MaybeLLVMReg = MCRI->getLLVMRegNum(Reg, IsEH);
173 if (!MaybeLLVMReg) {
174 if (!(PrevLoc == NextLoc))
175 Context->reportWarning(
176 Inst.getLoc(),
177 formatv("the dwarf register {0} does not have a LLVM number, but its "
178 "unwind info changed. Ignoring this change",
179 Reg));
180 return;
181 }
182 const char *RegName = MCRI->getName(*MaybeLLVMReg);
183
184 // Each case is annotated with its corresponding number as described in
185 // `llvm/include/llvm/DWARFCFIChecker/DWARFCFIAnalysis.h`.
186
187 // TODO: Expressions are not supported yet, but if they were to be supported,
188 // note that structure equality for expressions is defined as follows: Two
189 // expressions are structurally equal if they become the same after you
190 // replace every operand with a placeholder.
191
192 if (PrevLoc == NextLoc) { // Case 1
193 for (DWARFRegNum UsedReg : getUnwindRuleRegSet(PrevRow, Reg))
194 if (Writes.count(UsedReg)) { // Case 1.b
195 auto MaybeLLVMUsedReg = MCRI->getLLVMRegNum(UsedReg, IsEH);
196 assert(MaybeLLVMUsedReg && "instructions will always write to a "
197 "register that has an LLVM register number");
198 Context->reportError(
199 Inst.getLoc(),
200 formatv("changed register {1}, that register {0}'s unwinding rule "
201 "uses, but there is no CFI directives about it",
202 RegName, MCRI->getName(*MaybeLLVMUsedReg)));
203 return;
204 }
205 return; // Case 1.a
206 }
207 // Case 2
208 if (PrevLoc.getLocation() != NextLoc.getLocation()) { // Case 2.a
209 Context->reportWarning(
210 Inst.getLoc(),
211 formatv("validating changes happening to register {0} unwinding "
212 "rule structure is not implemented yet",
213 RegName));
214 return;
215 }
216 auto &&PrevRegSet = getUnwindRuleRegSet(PrevRow, Reg);
217 if (PrevRegSet != getUnwindRuleRegSet(NextRow, Reg)) { // Case 2.b
218 Context->reportWarning(
219 Inst.getLoc(),
220 formatv("validating changes happening to register {0} unwinding "
221 "rule register set is not implemented yet",
222 RegName));
223 return;
224 }
225 // Case 2.c
226 for (DWARFRegNum UsedReg : PrevRegSet)
227 if (Writes.count(UsedReg)) { // Case 2.c.i
228 Context->reportWarning(
229 Inst.getLoc(),
230 formatv("register {0} unwinding rule's offset is changed, and one of "
231 "the rule's registers is modified, but validating the "
232 "modification amount is not implemented yet",
233 RegName));
234 return;
235 }
236 // Case 2.c.ii
237 Context->reportError(
238 Inst.getLoc(), formatv("register {0} unwinding rule's offset is changed, "
239 "but not any of the rule's registers are modified",
240 RegName));
241}
242
243void DWARFCFIAnalysis::checkCFADiff(const MCInst &Inst,
244 const dwarf::UnwindRow &PrevRow,
245 const dwarf::UnwindRow &NextRow,
246 const SmallSet<DWARFRegNum, 4> &Writes) {
247
248 auto MaybePrevCFA = getCFARegOffsetInfo(PrevRow);
249 auto MaybeNextCFA = getCFARegOffsetInfo(NextRow);
250
251 if (!MaybePrevCFA) {
252 if (MaybeNextCFA) {
253 Context->reportWarning(Inst.getLoc(),
254 "CFA rule changed to [reg + offset], this "
255 "transition will not be checked");
256 return;
257 }
258
259 Context->reportWarning(Inst.getLoc(),
260 "CFA rule is not [reg + offset], not checking it");
261 return;
262 }
263
264 if (!MaybeNextCFA) {
265 Context->reportWarning(Inst.getLoc(),
266 "CFA rule changed from [reg + offset], this "
267 "transition will not be checked");
268 return;
269 }
270
271 auto PrevCFA = *MaybePrevCFA;
272 auto NextCFA = *MaybeNextCFA;
273
274 auto MaybeLLVMPrevReg = MCRI->getLLVMRegNum(PrevCFA.Reg, IsEH);
275 const char *PrevCFARegName =
276 MaybeLLVMPrevReg ? MCRI->getName(*MaybeLLVMPrevReg) : "";
277 auto MaybeLLVMNextReg = MCRI->getLLVMRegNum(NextCFA.Reg, IsEH);
278 const char *NextCFARegName =
279 MaybeLLVMNextReg ? MCRI->getName(*MaybeLLVMNextReg) : "";
280
281 if (PrevCFA == NextCFA) { // Case 1
282 if (!Writes.count(PrevCFA.Reg)) // Case 1.a
283 return;
284 // Case 1.b
285 Context->reportError(
286 Inst.getLoc(),
287 formatv("modified CFA register {0} but not changed CFA rule",
288 PrevCFARegName));
289 return;
290 }
291
292 if (PrevCFA.Reg != NextCFA.Reg) { // Case 2.b
293 Context->reportWarning(
294 Inst.getLoc(),
295 formatv("CFA register changed from register {0} to register {1}, "
296 "validating this change is not implemented yet",
297 PrevCFARegName, NextCFARegName));
298 return;
299 }
300 // Case 2.c
301 if (Writes.count(PrevCFA.Reg)) { // Case 2.c.i
302 Context->reportWarning(
303 Inst.getLoc(), formatv("CFA offset is changed from {0} to {1}, and CFA "
304 "register {2} is modified, but validating the "
305 "modification amount is not implemented yet",
306 PrevCFA.Offset, NextCFA.Offset, PrevCFARegName));
307 return;
308 }
309 // Case 2.c.ii
310 Context->reportError(
311 Inst.getLoc(),
312 formatv("did not modify CFA register {0} but changed CFA rule",
313 PrevCFARegName));
314}
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.
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:732
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 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.
uint8_t NumImplicitDefs
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:134
size_type count(const T &V) const
count - Return 1 if the element is in the set, 0 otherwise.
Definition SmallSet.h:176
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:184
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.
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)