xref: /llvm-project/llvm/docs/RemoveDIsDebugInfo.md (revision 65f81df473904d2df2b9eaa91ff4fcbe69f8fb00)
1# Debug info migration: From intrinsics to records
2
3We're planning on removing debug info intrinsics from LLVM, as they're slow, unwieldy and can confuse optimisation passes if they're not expecting them. Instead of having a sequence of instructions that looks like this:
4
5```text
6    %add = add i32 %foo, %bar
7    call void @llvm.dbg.value(metadata %add, ...
8    %sub = sub i32 %add, %tosub
9    call void @llvm.dbg.value(metadata %sub, ...
10    call void @a_normal_function()
11```
12
13with `dbg.value` intrinsics representing debug info records, it would instead be printed as:
14
15```text
16    %add = add i32 %foo, %bar
17      #dbg_value(%add, ...
18    %sub = sub i32 %add, %tosub
19      #dbg_value(%sub, ...
20    call void @a_normal_function()
21```
22
23The debug records are not instructions, do not appear in the instruction list, and won't appear in your optimisation passes unless you go digging for them deliberately.
24
25# Great, what do I need to do!
26
27We've largely completed the migration. The remaining rough edge is that going forwards, instructions must be inserted into basic blocks using iterators rather than instruction pointers. In almost all circumstances you can just call `getIterator` on an instruction pointer -- however, if you call a function that returns the start of a basic block, such as:
28
291. BasicBlock::begin
302. BasicBlock::getFirstNonPHIIt
313. BasicBlock::getFirstInsertionPt
32
33Then you must past that iterator into the insertion function without modification (the iterator carries a debug-info bit). That's all! Read on for a more detailed explanation.
34
35## API Changes
36
37There are two significant changes to be aware of. Firstly, we're adding a single bit of debug relevant data to the `BasicBlock::iterator` class (it's so that we can determine whether ranges intend on including debug info at the beginning of a block or not). That means when writing passes that insert LLVM IR instructions, you need to identify positions with `BasicBlock::iterator` rather than just a bare `Instruction *`. Most of the time this means that after identifying where you intend on inserting something, you must also call `getIterator` on the instruction position -- however when inserting at the start of a block you _must_ use `getFirstInsertionPt`, `getFirstNonPHIIt` or `begin` and use that iterator to insert, rather than just fetching a pointer to the first instruction.
38
39The second matter is that if you transfer sequences of instructions from one place to another manually, i.e. repeatedly using `moveBefore` where you might have used `splice`, then you should instead use the method `moveBeforePreserving`. `moveBeforePreserving` will transfer debug info records with the instruction they're attached to. This is something that happens automatically today -- if you use `moveBefore` on every element of an instruction sequence, then debug intrinsics will be moved in the normal course of your code, but we lose this behaviour with non-instruction debug info.
40
41For a more in-depth overview of how to update existing code to support debug records, see [the guide below](#how-to-update-existing-code).
42
43## Textual IR Changes
44
45As we change from using debug intrinsics to debug records, any tools that depend on parsing IR produced by LLVM will need to handle the new format. For the most part, the difference between the printed form of a debug intrinsic call and a debug record is trivial:
46
471. An extra 2 spaces of indentation are added.
482. The text `(tail|notail|musttail)? call void @llvm.dbg.<type>` is replaced with `#dbg_<type>`.
493. The leading `metadata ` is removed from each argument to the intrinsic.
504. The DILocation changes from being an instruction attachment with the format `!dbg !<Num>`, to being an ordinary argument, i.e. `!<Num>`, that is passed as the final argument to the debug record.
51
52Following these rules, we have this example of a debug intrinsic and the equivalent debug record:
53
54```
55; Debug Intrinsic:
56  call void @llvm.dbg.value(metadata i32 %add, metadata !10, metadata !DIExpression()), !dbg !20
57; Debug Record:
58    #dbg_value(i32 %add, !10, !DIExpression(), !20)
59```
60
61### Test updates
62
63Any tests downstream of the main LLVM repo that test the IR output of LLVM may break as a result of the change to using records. Updating an individual test to expect records instead of intrinsics should be trivial, given the update rules above. Updating many tests may be burdensome however; to update the lit tests in the main repository, the following steps were used:
64
651. Collect the list of failing lit tests into a single file, `failing-tests.txt`, separated by (and ending with) newlines.
662. Use the following line to split the failing tests into tests that use update_test_checks and tests that don't:
67    ```
68    $ while IFS= read -r f; do grep -q "Assertions have been autogenerated by" "$f" && echo "$f" >> update-checks-tests.txt || echo "$f" >> manual-tests.txt; done < failing-tests.txt
69    ```
703. For the tests that use update_test_checks, run the appropriate update_test_checks script - for the main LLVM repo, this was achieved with:
71    ```
72    $ xargs ./llvm/utils/update_test_checks.py --opt-binary ./build/bin/opt < update-checks-tests.txt
73    $ xargs ./llvm/utils/update_cc_test_checks.py --llvm-bin ./build/bin/ < update-checks-tests.txt
74    ```
754. The remaining tests can be manually updated, although if there is a large number of tests then the following scripts may be useful; firstly, a script used to extract the check-line prefixes from a file:
76    ```
77    $ cat ./get-checks.sh
78    #!/bin/bash
79
80    # Always add CHECK, since it's more effort than it's worth to filter files where
81    # every RUN line uses other check prefixes.
82    # Then detect every instance of "check-prefix(es)=..." and add the
83    # comma-separated arguments as extra checks.
84    for filename in "$@"
85    do
86        echo "$filename,CHECK"
87        allchecks=$(grep -Eo 'check-prefix(es)?[ =][A-Z0-9_,-]+' $filename | sed -E 's/.+[= ]([A-Z0-9_,-]+).*/\1/g; s/,/\n/g')
88        for check in $allchecks; do
89            echo "$filename,$check"
90        done
91    done
92    ```
93    Then a second script to perform the work of actually updating the check-lines in each of the failing tests, with a series of simple substitution patterns:
94    ```
95    $ cat ./substitute-checks.sh
96    #!/bin/bash
97
98    file="$1"
99    check="$2"
100
101    # Any test that explicitly tests debug intrinsic output is not suitable to
102    # update by this script.
103    if grep -q "write-experimental-debuginfo=false" "$file"; then
104        exit 0
105    fi
106
107    sed -i -E -e "
108    /(#|;|\/\/).*$check[A-Z0-9_\-]*:/!b
109    /DIGlobalVariableExpression/b
110    /!llvm.dbg./bpostcall
111    s/((((((no|must)?tail )?call.*)?void )?@)?llvm.)?dbg\.([a-z]+)/#dbg_\7/
112    :postcall
113    /declare #dbg_/d
114    s/metadata //g
115    s/metadata\{/{/g
116    s/DIExpression\(([^)]*)\)\)(,( !dbg)?)?/DIExpression(\1),/
117    /#dbg_/!b
118    s/((\))?(,) )?!dbg (![0-9]+)/\3\4\2/
119    s/((\))?(, ))?!dbg/\3/
120    " "$file"
121    ```
122    Both of these scripts combined can be used on the list in `manual-tests.txt` as follows:
123    ```
124    $ cat manual-tests.txt | xargs ./get-checks.sh | sort | uniq | awk -F ',' '{ system("./substitute-checks.sh " $1 " " $2) }'
125    ```
126    These scripts dealt successfully with the vast majority of checks in `clang/test` and `llvm/test`.
1275. Verify the resulting tests pass, and detect any failing tests:
128    ```
129    $ xargs ./build/bin/llvm-lit -q < failing-tests.txt
130    ********************
131    Failed Tests (5):
132    LLVM :: DebugInfo/Generic/dbg-value-lower-linenos.ll
133    LLVM :: Transforms/HotColdSplit/transfer-debug-info.ll
134    LLVM :: Transforms/ObjCARC/basic.ll
135    LLVM :: Transforms/ObjCARC/ensure-that-exception-unwind-path-is-visited.ll
136    LLVM :: Transforms/SafeStack/X86/debug-loc2.ll
137
138
139    Total Discovered Tests: 295
140    Failed: 5 (1.69%)
141    ```
1426. Some tests may have failed - the update scripts are simplistic and preserve no context across lines, and so there are cases that they will not handle; the remaining cases must be manually updated (or handled by further scripts).
143
144# C-API changes
145
146Some new functions that have been added are temporary and will be deprecated in the future. The intention is that they'll help downstream projects adapt during the transition period.
147
148```
149Deleted functions
150-----------------
151LLVMDIBuilderInsertDeclareBefore   # Insert a debug record (new debug info format) instead of a debug intrinsic (old debug info format).
152LLVMDIBuilderInsertDeclareAtEnd    # Same as above.
153LLVMDIBuilderInsertDbgValueBefore  # Same as above.
154LLVMDIBuilderInsertDbgValueAtEnd   # Same as above.
155
156New functions (to be deprecated)
157--------------------------------
158LLVMIsNewDbgInfoFormat     # Returns true if the module is in the new non-instruction mode.
159LLVMSetIsNewDbgInfoFormat  # Convert to the requested debug info format.
160
161New functions (no plans to deprecate)
162-------------------------------------
163LLVMGetFirstDbgRecord                    # Obtain the first debug record attached to an instruction.
164LLVMGetLastDbgRecord                     # Obtain the last debug record attached to an instruction.
165LLVMGetNextDbgRecord                     # Get next debug record or NULL.
166LLVMGetPreviousDbgRecord                 # Get previous debug record or NULL.
167LLVMDIBuilderInsertDeclareRecordBefore   # Insert a debug record (new debug info format).
168LLVMDIBuilderInsertDeclareRecordAtEnd    # Same as above. See info below.
169LLVMDIBuilderInsertDbgValueRecordBefore  # Same as above. See info below.
170LLVMDIBuilderInsertDbgValueRecordAtEnd   # Same as above. See info below.
171
172LLVMPositionBuilderBeforeDbgRecords          # See info below.
173LLVMPositionBuilderBeforeInstrAndDbgRecords  # See info below.
174```
175
176`LLVMDIBuilderInsertDeclareRecordBefore`, `LLVMDIBuilderInsertDeclareRecordAtEnd`, `LLVMDIBuilderInsertDbgValueRecordBefore` and `LLVMDIBuilderInsertDbgValueRecordAtEnd` are replacing the deleted `LLVMDIBuilderInsertDeclareBefore-style` functions.
177
178`LLVMPositionBuilderBeforeDbgRecords` and `LLVMPositionBuilderBeforeInstrAndDbgRecords` behave the same as `LLVMPositionBuilder` and `LLVMPositionBuilderBefore` except the insertion position is set before the debug records that precede the target instruction. Note that this doesn't mean that debug intrinsics before the chosen instruction are skipped, only debug records (which unlike debug records are not themselves instructions).
179
180If you don't know which function to call then follow this rule:
181If you are trying to insert at the start of a block, or purposfully skip debug intrinsics to determine the insertion point for any other reason, then call the new functions.
182
183`LLVMPositionBuilder` and `LLVMPositionBuilderBefore` are unchanged. They insert before the indicated instruction but after any attached debug records.
184
185`LLVMGetFirstDbgRecord`, `LLVMGetLastDbgRecord`, `LLVMGetNextDbgRecord` and `LLVMGetPreviousDbgRecord` can be used for iterating over debug records attached to instructions (provided as `LLVMValueRef`).
186
187```c
188LLVMDbgRecordRef DbgRec;
189for (DbgRec = LLVMGetFirstDbgRecord(Inst); DbgRec;
190     DbgRec = LLVMGetNextDbgRecord(DbgRec)) {
191  // do something with DbgRec
192}
193```
194
195```c
196LLVMDbgRecordRef DbgRec;
197for (DbgRec = LLVMGetLastDbgRecord(Inst); DbgRec;
198     DbgRec = LLVMGetPreviousDbgRecord(DbgRec)) {
199  // do something with DbgRec
200}
201````
202
203# The new "Debug Record" model
204
205Below is a brief overview of the new representation that replaces debug intrinsics; for an instructive guide on updating old code, see [here](#how-to-update-existing-code).
206
207## What exactly have you replaced debug intrinsics with?
208
209We're using a dedicated C++ class called `DbgRecord` to store debug info, with a one-to-one relationship between each instance of a debug intrinsic and each `DbgRecord` object in any LLVM IR program; these `DbgRecord`s are represented in the IR as non-instruction debug records, as described in the [Source Level Debugging](project:SourceLevelDebugging.rst#Debug Records) document. This class has a set of subclasses that store exactly the same information as is stored in debugging intrinsics. Each one also has almost entirely the same set of methods, that behave in the same way:
210
211  https://llvm.org/docs/doxygen/classllvm_1_1DbgRecord.html
212  https://llvm.org/docs/doxygen/classllvm_1_1DbgVariableRecord.html
213  https://llvm.org/docs/doxygen/classllvm_1_1DbgLabelRecord.html
214
215This allows you to treat a `DbgVariableRecord` as if it's a `dbg.value`/`dbg.declare`/`dbg.assign` intrinsic most of the time, for example in generic (auto-param) lambdas, and the same for `DbgLabelRecord` and `dbg.label`s.
216
217## How do these `DbgRecords` fit into the instruction stream?
218
219Like so:
220
221```text
222                 +---------------+          +---------------+
223---------------->|  Instruction  +--------->|  Instruction  |
224                 +-------+-------+          +---------------+
225                         |
226                         |
227                         |
228                         |
229                         v
230                  +-------------+
231          <-------+  DbgMarker  |<-------
232         /        +-------------+        \
233        /                                 \
234       /                                   \
235      v                                     ^
236 +-------------+    +-------------+   +-------------+
237 |  DbgRecord  +--->|  DbgRecord  +-->|  DbgRecord  |
238 +-------------+    +-------------+   +-------------+
239```
240
241Each instruction has a pointer to a `DbgMarker` (which will become optional), that contains a list of `DbgRecord` objects. No debugging records appear in the instruction list at all. `DbgRecord`s have a parent pointer to their owning `DbgMarker`, and each `DbgMarker` has a pointer back to it's owning instruction.
242
243Not shown are the links from DbgRecord to other parts of the `Value`/`Metadata` hierachy: `DbgRecord` subclasses have tracking pointers to the DIMetadata that they use, and `DbgVariableRecord` has references to `Value`s that are stored in a `DebugValueUser` base class. This refers to a `ValueAsMetadata` object referring to `Value`s, via the `TrackingMetadata` facility.
244
245The various kinds of debug intrinsic (value, declare, assign, label) are all stored in `DbgRecord` subclasses, with a "RecordKind" field distinguishing `DbgLabelRecord`s from `DbgVariableRecord`s, and a `LocationType` field in the `DbgVariableRecord` class further disambiguating the various debug variable intrinsics it can represent.
246
247# How to update existing code
248
249Any existing code that interacts with debug intrinsics in some way will need to be updated to interact with debug records in the same way. A few quick rules to keep in mind when updating code:
250
251- Debug records will not be seen when iterating over instructions; to find the debug records that appear immediately before an instruction, you'll need to iterate over `Instruction::getDbgRecordRange()`.
252- Debug records have interfaces that are identical to those of debug intrinsics, meaning that any code that operates on debug intrinsics can be trivially applied to debug records as well. The exceptions for this are `Instruction` or `CallInst` methods that don't logically apply to debug records, and `isa`/`cast`/`dyn_cast` methods, are replaced by methods on the `DbgRecord` class itself.
253- Debug records cannot appear in a module that also contains debug intrinsics; the two are mutually exclusive. As debug records are the future format, handling records correctly should be prioritized in new code.
254- Until support for intrinsics is no longer present, a valid hotfix for code that only handles debug intrinsics and is non-trivial to update is to convert the module to the intrinsic format using `Module::setIsNewDbgInfoFormat`, and convert it back afterwards.
255  - This can also be performed within a lexical scope for a module or an individual function using the class `ScopedDbgInfoFormatSetter`:
256  ```
257  void handleModule(Module &M) {
258    {
259      ScopedDbgInfoFormatSetter FormatSetter(M, false);
260      handleModuleWithDebugIntrinsics(M);
261    }
262    // Module returns to previous debug info format after exiting the above block.
263  }
264  ```
265
266Below is a rough guide on how existing code that currently supports debug intrinsics can be updated to support debug records.
267
268## Creating debug records
269
270Debug records will automatically be created by the `DIBuilder` class when the new format is enabled. As with instructions, it is also possible to call `DbgRecord::clone` to create an unattached copy of an existing record.
271
272## Skipping debug records, ignoring debug-uses of `Values`, stably counting instructions, etc.
273
274This will all happen transparently without needing to think about it!
275
276```
277for (Instruction &I : BB) {
278  // Old: Skips debug intrinsics
279  if (isa<DbgInfoIntrinsic>(&I))
280    continue;
281  // New: No extra code needed, debug records are skipped by default.
282  ...
283}
284```
285
286## Finding debug records
287
288Utilities such as `findDbgUsers` and the like now have an optional argument that will return the set of `DbgVariableRecord` records that refer to a `Value`. You should be able to treat them the same as intrinsics.
289
290```
291// Old:
292  SmallVector<DbgVariableIntrinsic *> DbgUsers;
293  findDbgUsers(DbgUsers, V);
294  for (auto *DVI : DbgUsers) {
295    if (DVI->getParent() != BB)
296      DVI->replaceVariableLocationOp(V, New);
297  }
298// New:
299  SmallVector<DbgVariableIntrinsic *> DbgUsers;
300  SmallVector<DbgVariableRecord *> DVRUsers;
301  findDbgUsers(DbgUsers, V, &DVRUsers);
302  for (auto *DVI : DbgUsers)
303    if (DVI->getParent() != BB)
304      DVI->replaceVariableLocationOp(V, New);
305  for (auto *DVR : DVRUsers)
306    if (DVR->getParent() != BB)
307      DVR->replaceVariableLocationOp(V, New);
308```
309
310## Examining debug records at positions
311
312Call `Instruction::getDbgRecordRange()` to get the range of `DbgRecord` objects that are attached to an instruction.
313
314```
315for (Instruction &I : BB) {
316  // Old: Uses a data member of a debug intrinsic, and then skips to the next
317  // instruction.
318  if (DbgInfoIntrinsic *DII = dyn_cast<DbgInfoIntrinsic>(&I)) {
319    recordDebugLocation(DII->getDebugLoc());
320    continue;
321  }
322  // New: Iterates over the debug records that appear before `I`, and treats
323  // them identically to the intrinsic block above.
324  // NB: This should always appear at the top of the for-loop, so that we
325  // process the debug records preceding `I` before `I` itself.
326  for (DbgRecord &DR = I.getDbgRecordRange()) {
327    recordDebugLocation(DR.getDebugLoc());
328  }
329  processInstruction(I);
330}
331```
332
333This can also be passed through the function `filterDbgVars` to specifically
334iterate over DbgVariableRecords, which are more commonly used.
335
336```
337for (Instruction &I : BB) {
338  // Old: If `I` is a DbgVariableIntrinsic we record the variable, and apply
339  // extra logic if it is an `llvm.dbg.declare`.
340  if (DbgVariableIntrinsic *DVI = dyn_cast<DbgVariableIntrinsic>(&I)) {
341    recordVariable(DVI->getVariable());
342    if (DbgDeclareInst *DDI = dyn_cast<DbgDeclareInst>(DVI))
343      recordDeclareAddress(DDI->getAddress());
344    continue;
345  }
346  // New: `filterDbgVars` is used to iterate over only DbgVariableRecords.
347  for (DbgVariableRecord &DVR = filterDbgVars(I.getDbgRecordRange())) {
348    recordVariable(DVR.getVariable());
349    // Debug variable records are not cast to subclasses; simply call the
350    // appropriate `isDbgX()` check, and use the methods as normal.
351    if (DVR.isDbgDeclare())
352      recordDeclareAddress(DVR.getAddress());
353  }
354  // ...
355}
356```
357
358## Processing individual debug records
359
360In most cases, any code that operates on debug intrinsics can be extracted to a template function or auto lambda (if it is not already in one) that can be applied to both debug intrinsics and debug records - though keep in mind the main exception that `isa`/`cast`/`dyn_cast` do not apply to `DbgVariableRecord` types.
361
362```
363// Old: Function that operates on debug variable intrinsics in a BasicBlock, and
364// collects llvm.dbg.declares.
365void processDbgInfoInBlock(BasicBlock &BB,
366                           SmallVectorImpl<DbgDeclareInst*> &DeclareIntrinsics) {
367  for (Instruction &I : BB) {
368    if (DbgVariableIntrinsic *DVI = dyn_cast<DbgVariableIntrinsic>(&I)) {
369      processVariableValue(DebugVariable(DVI), DVI->getValue());
370      if (DbgDeclareInst *DDI = dyn_cast<DbgDeclareInst>(DVI))
371        Declares.push_back(DDI);
372      else if (!isa<Constant>(DVI->getValue()))
373        DVI->setKillLocation();
374    }
375  }
376}
377
378// New: Template function is used to deduplicate handling of intrinsics and
379// records.
380// An overloaded function is also used to handle isa/cast/dyn_cast operations
381// for intrinsics and records, since those functions cannot be directly applied
382// to DbgRecords.
383DbgDeclareInst *DynCastToDeclare(DbgVariableIntrinsic *DVI) {
384  return dyn_cast<DbgDeclareInst>(DVI);
385}
386DbgVariableRecord *DynCastToDeclare(DbgVariableRecord *DVR) {
387  return DVR->isDbgDeclare() ? DVR : nullptr;
388}
389
390template<typename DbgVarTy, DbgDeclTy>
391void processDbgVariable(DbgVarTy *DbgVar,
392                       SmallVectorImpl<DbgDeclTy*> &Declares) {
393    processVariableValue(DebugVariable(DbgVar), DbgVar->getValue());
394    if (DbgDeclTy *DbgDeclare = DynCastToDeclare(DbgVar))
395      Declares.push_back(DbgDeclare);
396    else if (!isa<Constant>(DbgVar->getValue()))
397      DbgVar->setKillLocation();
398};
399
400void processDbgInfoInBlock(BasicBlock &BB,
401                           SmallVectorImpl<DbgDeclareInst*> &DeclareIntrinsics,
402                           SmallVectorImpl<DbgVariableRecord*> &DeclareRecords) {
403  for (Instruction &I : BB) {
404    if (DbgVariableIntrinsic *DVI = dyn_cast<DbgVariableIntrinsic>(&I))
405      processDbgVariable(DVI, DeclareIntrinsics);
406    for (DbgVariableRecord *DVR : filterDbgVars(I.getDbgRecordRange()))
407      processDbgVariable(DVR, DeclareRecords);
408  }
409}
410```
411
412## Moving and deleting debug records
413
414You can use `DbgRecord::removeFromParent` to unlink a `DbgRecord` from it's marker, and then `BasicBlock::insertDbgRecordBefore` or `BasicBlock::insertDbgRecordAfter` to re-insert the `DbgRecord` somewhere else. You cannot insert a `DbgRecord` at an arbitary point in a list of `DbgRecord`s (if you're doing this with `llvm.dbg.value`s then it's unlikely to be correct).
415
416Erase `DbgRecord`s by calling `eraseFromParent`.
417
418```
419// Old: Move a debug intrinsic to the start of the block, and delete all other intrinsics for the same variable in the block.
420void moveDbgIntrinsicToStart(DbgVariableIntrinsic *DVI) {
421  BasicBlock *ParentBB = DVI->getParent();
422  DVI->removeFromParent();
423  for (Instruction &I : ParentBB) {
424    if (auto *BlockDVI = dyn_cast<DbgVariableIntrinsic>(&I))
425      if (BlockDVI->getVariable() == DVI->getVariable())
426        BlockDVI->eraseFromParent();
427  }
428  DVI->insertBefore(ParentBB->getFirstInsertionPt());
429}
430
431// New: Perform the same operation, but for a debug record.
432void moveDbgRecordToStart(DbgVariableRecord *DVR) {
433  BasicBlock *ParentBB = DVR->getParent();
434  DVR->removeFromParent();
435  for (Instruction &I : ParentBB) {
436    for (auto &BlockDVR : filterDbgVars(I.getDbgRecordRange()))
437      if (BlockDVR->getVariable() == DVR->getVariable())
438        BlockDVR->eraseFromParent();
439  }
440  DVR->insertBefore(ParentBB->getFirstInsertionPt());
441}
442```
443
444## What about dangling debug records?
445
446If you have a block like so:
447
448```text
449    foo:
450      %bar = add i32 %baz...
451      dbg.value(metadata i32 %bar,...
452      br label %xyzzy
453```
454
455your optimisation pass may wish to erase the terminator and then do something to the block. This is easy to do when debug info is kept in instructions, but with `DbgRecord`s there is no trailing instruction to attach the variable information to in the block above, once the terminator is erased. For such degenerate blocks, `DbgRecord`s are stored temporarily in a map in `LLVMContext`, and are re-inserted when a terminator is reinserted to the block or other instruction inserted at `end()`.
456
457This can technically lead to trouble in the vanishingly rare scenario where an optimisation pass erases a terminator and then decides to erase the whole block. (We recommend not doing that).
458
459## Anything else?
460
461The above guide does not comprehensively cover every pattern that could apply to debug intrinsics; as mentioned at the [start of the guide](#how-to-update-existing-code), you can temporarily convert the target module from debug records to intrinsics as a stopgap measure. Most operations that can be performed on debug intrinsics have exact equivalents for debug records, but if you encounter any exceptions, reading the class docs (linked [here](#what-exactly-have-you-replaced-debug-intrinsics-with)) may give some insight, there may be examples in the existing codebase, and you can always ask for help on the [forums](https://discourse.llvm.org/tag/debuginfo).
462