| # How to Update Debug Info: A Guide for LLVM Pass Authors |
| |
| |
| ## Introduction |
| |
| Certain kinds of code transformations can inadvertently result in a loss of |
| debug info, or worse, make debug info misrepresent the state of a program. Debug |
| info availability is also essential for SamplePGO. |
| |
| This document specifies how to correctly update debug info in various kinds of |
| code transformations, and offers suggestions for how to create targeted debug |
| info tests for arbitrary transformations. |
| |
| For more on the philosophy behind LLVM debugging information, see |
| {doc}`SourceLevelDebugging`. |
| |
| ## Rules for updating debug locations |
| |
| (WhenToPreserveLocation)= |
| |
| ### When to preserve an instruction location |
| |
| A transformation should preserve the debug location of an instruction if the |
| instruction either remains in its basic block, or if its basic block is folded |
| into a predecessor that branches unconditionally. The APIs to use are |
| `IRBuilder`, or `Instruction::setDebugLoc`. |
| |
| The purpose of this rule is to ensure that common block-local optimizations |
| preserve the ability to set breakpoints on source locations corresponding to |
| the instructions they touch. Debugging, crash logs, and SamplePGO accuracy |
| would be severely impacted if that ability were lost. |
| |
| Examples of transformations that should follow this rule include: |
| |
| - Instruction scheduling. Block-local instruction reordering should not drop |
| source locations, even though this may lead to jumpy single-stepping |
| behavior. |
| - Simple jump threading. For example, if block `B1` unconditionally jumps to |
| `B2`, *and* is its unique predecessor, instructions from `B2` can be |
| hoisted into `B1`. Source locations from `B2` should be preserved. |
| - Peephole optimizations that replace or expand an instruction, like `(add X |
| X) => (shl X 1)`. The location of the `shl` instruction should be the same |
| as the location of the `add` instruction. |
| - Tail duplication. For example, if blocks `B1` and `B2` both |
| unconditionally branch to `B3` and `B3` can be folded into its |
| predecessors, source locations from `B3` should be preserved. |
| |
| Examples of transformations for which this rule *does not* apply include: |
| |
| - LICM. E.g., if an instruction is moved from the loop body to the preheader, |
| the rule for {ref}`dropping locations <WhenToDropLocation>` applies. |
| |
| In addition to the rule above, a transformation should also preserve the debug |
| location of an instruction that is moved between basic blocks, if the |
| destination block already contains an instruction with an identical debug |
| location. |
| |
| Examples of transformations that should follow this rule include: |
| |
| - Moving instructions between basic blocks. For example, if instruction `I1` |
| in `BB1` is moved before `I2` in `BB2`, the source location of `I1` |
| can be preserved if it has the same source location as `I2`. |
| |
| (WhenToMergeLocation)= |
| |
| ### When to merge instruction locations |
| |
| A transformation should merge instruction locations if it replaces multiple |
| instructions with one or more new instructions, *and* the new instruction(s) |
| produce the output of more than one of the original instructions. The API to use |
| is `Instruction::applyMergedLocation`. For each new instruction I, its new |
| location should be a merge of the locations of all instructions whose output is |
| produced by I. Typically, this includes any instruction being RAUWed by a new |
| instruction, and excludes any instruction that only produces an intermediate |
| value used by the RAUWed instruction. |
| |
| The purpose of this rule is to ensure that a) the single merged instruction |
| has a location with an accurate scope attached, and b) to prevent misleading |
| single-stepping (or breakpoint) behavior. Often, merged instructions are memory |
| accesses which can trap: having an accurate scope attached greatly assists in |
| crash triage by identifying the (possibly inlined) function where the bad |
| memory access occurred. |
| |
| To maintain distinct source locations for SamplePGO, it is often beneficial to |
| retain an arbitrary but deterministic location instead of discarding line and |
| column information as part of merging. In particular, loss of location |
| information for calls inhibits optimizations such as indirect call promotion. |
| This behavior can be optionally enabled until support for accurately |
| representing merged instructions in the line table is implemented. |
| |
| Examples of transformations that should follow this rule include: |
| |
| - Hoisting identical instructions from all successors of a conditional branch |
| or sinking those from all paths to a postdominating block. For example, |
| merging identical loads/stores which occur on both sides of a CFG diamond |
| (see the `MergedLoadStoreMotion` pass). For each group of identical |
| instructions being hoisted/sunk, the merge of all their locations should be |
| applied to the merged instruction. |
| - Merging identical loop-invariant stores (see the LICM utility |
| `llvm::promoteLoopAccessesToScalars`). |
| - Scalar instructions being combined into a vector instruction, like |
| `(add A1, B1), (add A2, B2) => (add (A1, A2), (B1, B2))`. As the new vector |
| `add` computes the result of both original `add` instructions |
| simultaneously, it should use a merge of the two locations. Similarly, if |
| prior optimizations have already produced vectors `(A1, A2)` and |
| `(B2, B1)`, then we might create a `(shufflevector (1, 0), (B2, B1))` |
| instruction to produce `(B1, B2)` for the vector `add`; in this case we've |
| created two instructions to replace the original `adds`, so both new |
| instructions should use the merged location. |
| |
| Examples of transformations for which this rule *does not* apply include: |
| |
| - Block-local peepholes which delete redundant instructions, like |
| `(sext (zext i8 %x to i16) to i32) => (zext i8 %x to i32)`. The inner |
| `zext` is modified but remains in its block, so the rule for |
| {ref}`preserving locations <WhenToPreserveLocation>` should apply. |
| - Peephole optimizations which combine multiple instructions together, like |
| `(add (mul A B) C) => llvm.fma.f32(A, B, C)`. Note that the result of the |
| `mul` no longer appears in the program, while the result of the `add` is |
| now produced by the `fma`, so the `add`'s location should be used. |
| - Converting an if-then-else CFG diamond into a `select`. Preserving the |
| debug locations of speculated instructions can make it seem like a condition |
| is true when it's not (or vice versa), which leads to a confusing |
| single-stepping experience. The rule for |
| {ref}`dropping locations <WhenToDropLocation>` should apply here. |
| - Hoisting/sinking that would make a location reachable when it previously |
| wasn't. Consider hoisting two identical instructions with the same location |
| from first two cases of a switch that has three cases. Merging their |
| locations would make the location from the first two cases reachable when the |
| third case is taken. The rule for |
| {ref}`dropping locations <WhenToDropLocation>` applies. |
| |
| (WhenToDropLocation)= |
| |
| ### When to drop an instruction location |
| |
| A transformation should drop debug locations if the rules for |
| {ref}`preserving <WhenToPreserveLocation>` and |
| {ref}`merging <WhenToMergeLocation>` debug locations do not apply. The API to |
| use is `Instruction::dropLocation()`. |
| |
| The purpose of this rule is to prevent erratic or misleading single-stepping |
| behavior in situations in which an instruction has no clear, unambiguous |
| relationship to a source location. |
| |
| To handle an instruction without a location, the DWARF generator |
| defaults to allowing the last-set location after a label to cascade forward, or |
| to setting a line 0 location with viable scope information if no previous |
| location is available. |
| |
| See the discussion in the section about |
| {ref}`merging locations <WhenToMergeLocation>` for examples of when the rule for |
| dropping locations applies. |
| |
| ### When to remap a debug location |
| |
| When code paths are duplicated, during passes such as loop unrolling or jump |
| threading, `DILocation` attachments need to be remapped using `mapAtomInstance` |
| and `RemapSourceAtom`. This is to support the Key Instructions debug info feature. |
| See {doc}`KeyInstructionsDebugInfo` for information. |
| |
| (NewInstLocations)= |
| |
| ### Setting locations for new instructions |
| |
| Whenever a new instruction is created and there is no suitable location for that |
| instruction, that instruction should be annotated accordingly. There are a set |
| of special `DebugLoc` values that can be set on an instruction to annotate the |
| reason that it does not have a valid location. These are as follows: |
| |
| - `DebugLoc::getCompilerGenerated()`: This indicates that the instruction is a |
| compiler-generated instruction, i.e. it is not associated with any user source |
| code. |
| - `DebugLoc::getDropped()`: This indicates that the instruction has |
| intentionally had its source location removed, according to the rules for |
| {ref}`dropping locations <WhenToDropLocation>`; this is set automatically by |
| `Instruction::dropLocation()`. |
| - `DebugLoc::getUnknown()`: This indicates that the instruction does not have |
| a known or currently knowable source location, e.g. that it is infeasible to |
| determine the correct source location, or that the source location is |
| ambiguous in a way that LLVM cannot currently represent. |
| - `DebugLoc::getTemporary()`: This is used for instructions that we don't |
| expect to be emitted (e.g. `UnreachableInst`), and so should not need a |
| valid location; if we ever try to emit a temporary location into an object/asm |
| file, this indicates that something has gone wrong. |
| |
| Where applicable, these should be used instead of leaving an instruction without |
| an assigned location or explicitly setting the location as `DebugLoc()`. |
| Ordinarily these special locations are identical to an absent location, but LLVM |
| built with coverage-tracking |
| (`-DLLVM_ENABLE_DEBUGLOC_COVERAGE_TRACKING="COVERAGE"`) will keep track of |
| these special locations in order to detect unintentionally-missing locations; |
| for this reason, the most important rule is to *not* apply any of these if it |
| isn't clear which, if any, is appropriate - an absent location can be detected |
| and fixed, while an incorrectly annotated instruction is much harder to detect. |
| On the other hand, if any of these clearly apply, then they should be used to |
| prevent false positives from being flagged up. |
| |
| ## Rules for updating debug values |
| |
| ### Deleting an IR-level Instruction |
| |
| When an `Instruction` is deleted, its debug uses change to `undef`. This is |
| a loss of debug info: the value of one or more source variables becomes |
| unavailable, starting with the `#dbg_value(undef, ...)`. When there is no |
| way to reconstitute the value of the lost instruction, this is the best |
| possible outcome. However, it's often possible to do better: |
| |
| - If the dying instruction can be RAUW'd, do so. The |
| `Value::replaceAllUsesWith` API transparently updates debug uses of the |
| dying instruction to point to the replacement value. |
| - If the dying instruction cannot be RAUW'd, call `llvm::salvageDebugInfo` on |
| it. This makes a best-effort attempt to rewrite debug uses of the dying |
| instruction by describing its effect as a `DIExpression`. |
| - If one of the **operands** of a dying instruction would become trivially |
| dead, use `llvm::replaceAllDbgUsesWith` to rewrite the debug uses of that |
| operand. Consider the following example function: |
| |
| ```llvm |
| define i16 @foo(i16 %a) { |
| %b = sext i16 %a to i32 |
| %c = and i32 %b, 15 |
| #dbg_value(i32 %c, ...) |
| %d = trunc i32 %c to i16 |
| ret i16 %d |
| } |
| ``` |
| |
| Now, here's what happens after the unnecessary truncation instruction `%d` is |
| replaced with a simplified instruction: |
| |
| ```llvm |
| define i16 @foo(i16 %a) { |
| #dbg_value(i32 undef, ...) |
| %simplified = and i16 %a, 15 |
| ret i16 %simplified |
| } |
| ``` |
| |
| Note that after deleting `%d`, all uses of its operand `%c` become |
| trivially dead. The debug use which used to point to `%c` is now `undef`, |
| and debug info is needlessly lost. |
| |
| To solve this problem, do: |
| |
| ```cpp |
| llvm::replaceAllDbgUsesWith(%c, theSimplifiedAndInstruction, ...) |
| ``` |
| |
| This results in better debug info because the debug use of `%c` is preserved: |
| |
| ```llvm |
| define i16 @foo(i16 %a) { |
| %simplified = and i16 %a, 15 |
| #dbg_value(i16 %simplified, ...) |
| ret i16 %simplified |
| } |
| ``` |
| |
| You may have noticed that `%simplified` is narrower than `%c`: this is not |
| a problem, because `llvm::replaceAllDbgUsesWith` takes care of inserting the |
| necessary conversion operations into the DIExpressions of updated debug uses. |
| |
| ### Deleting a MIR-level MachineInstr |
| |
| TODO |
| |
| ## Rules for updating `DIAssignID` Attachments |
| |
| `DIAssignID` metadata attachments are used by Assignment Tracking, which is |
| currently an experimental debug mode. |
| |
| See {doc}`AssignmentTracking` for how to update them and for more info on |
| Assignment Tracking. |
| |
| ## How to automatically convert tests into debug info tests |
| |
| (IRDebugify)= |
| |
| ### Mutation testing for IR-level transformations |
| |
| An IR test case for a transformation can, in many cases, be automatically |
| mutated to test debug info handling within that transformation. This is a |
| simple way to test for proper debug info handling. |
| |
| #### The `debugify` utility pass |
| |
| The `debugify` testing utility is just a pair of passes: `debugify` and |
| `check-debugify`. |
| |
| The first applies synthetic debug information to every instruction of the |
| module, and the second checks that this DI is still available after an |
| optimization has occurred, reporting any errors/warnings while doing so. |
| |
| The instructions are assigned sequentially increasing line locations, and are |
| immediately used by debug value records everywhere possible. |
| |
| For example, here is a module before: |
| |
| ```llvm |
| define void @f(i32* %x) { |
| entry: |
| %x.addr = alloca i32*, align 8 |
| store i32* %x, i32** %x.addr, align 8 |
| %0 = load i32*, i32** %x.addr, align 8 |
| store i32 10, i32* %0, align 4 |
| ret void |
| } |
| ``` |
| |
| and after running `opt -debugify`: |
| |
| ```llvm |
| define void @f(i32* %x) !dbg !6 { |
| entry: |
| %x.addr = alloca i32*, align 8, !dbg !12 |
| #dbg_value(i32** %x.addr, !9, !DIExpression(), !12) |
| store i32* %x, i32** %x.addr, align 8, !dbg !13 |
| %0 = load i32*, i32** %x.addr, align 8, !dbg !14 |
| #dbg_value(i32* %0, !11, !DIExpression(), !14) |
| store i32 10, i32* %0, align 4, !dbg !15 |
| ret void, !dbg !16 |
| } |
| |
| !llvm.dbg.cu = !{!0} |
| !llvm.debugify = !{!3, !4} |
| !llvm.module.flags = !{!5} |
| |
| !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) |
| !1 = !DIFile(filename: "debugify-sample.ll", directory: "/") |
| !2 = !{} |
| !3 = !{i32 5} |
| !4 = !{i32 2} |
| !5 = !{i32 2, !"Debug Info Version", i32 3} |
| !6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: true, unit: !0, retainedNodes: !8) |
| !7 = !DISubroutineType(types: !2) |
| !8 = !{!9, !11} |
| !9 = !DILocalVariable(name: "1", scope: !6, file: !1, line: 1, type: !10) |
| !10 = !DIBasicType(name: "ty64", size: 64, encoding: DW_ATE_unsigned) |
| !11 = !DILocalVariable(name: "2", scope: !6, file: !1, line: 3, type: !10) |
| !12 = !DILocation(line: 1, column: 1, scope: !6) |
| !13 = !DILocation(line: 2, column: 1, scope: !6) |
| !14 = !DILocation(line: 3, column: 1, scope: !6) |
| !15 = !DILocation(line: 4, column: 1, scope: !6) |
| !16 = !DILocation(line: 5, column: 1, scope: !6) |
| ``` |
| |
| #### Using `debugify` |
| |
| A simple way to use `debugify` is as follows: |
| |
| ```bash |
| $ opt -debugify -pass-to-test -check-debugify sample.ll |
| ``` |
| |
| This will inject synthetic DI to `sample.ll` run the `pass-to-test` and |
| then check for missing DI. The `-check-debugify` step can of course be |
| omitted in favor of more customizable FileCheck directives. |
| |
| Some other ways to run debugify are available: |
| |
| ```bash |
| # Same as the above example. |
| $ opt -enable-debugify -pass-to-test sample.ll |
| |
| # Suppresses verbose debugify output. |
| $ opt -enable-debugify -debugify-quiet -pass-to-test sample.ll |
| |
| # Prepend -debugify before and append -check-debugify -strip after |
| # each pass on the pipeline (similar to -verify-each). |
| $ opt -debugify-each -O2 sample.ll |
| ``` |
| |
| In order for `check-debugify` to work, the DI must be coming from |
| `debugify`. Thus, modules with existing DI will be skipped. |
| |
| `debugify` can be used to test a backend, e.g: |
| |
| ```bash |
| $ opt -debugify < sample.ll | llc -o - |
| ``` |
| |
| There is also a MIR-level debugify pass that can be run before each backend |
| pass, see: |
| {ref}`Mutation testing for MIR-level transformations <MIRDebugify>`. |
| |
| #### `debugify` in regression tests |
| |
| The output of the `debugify` pass must be stable enough to use in regression |
| tests. Changes to this pass are not allowed to break existing tests. |
| |
| :::{note} |
| Regression tests must be robust. Avoid hardcoding line/variable numbers in |
| check lines. In cases where this can't be avoided (say, if a test wouldn't |
| be precise enough), moving the test to its own file is preferred. |
| ::: |
| |
| #### Using Coverage Tracking to remove false positives |
| |
| As described {ref}`above <WhenToDropLocation>`, there are valid reasons for |
| instructions to not have source locations. Therefore, when detecting missing |
| source locations, it may be preferable to avoid detecting cases where the |
| missing source location is intentional. For this, you can use the "coverage |
| tracking" feature in LLVM to prevent these from appearing in the `debugify` |
| output. This is enabled in a build of LLVM by setting the CMake flag |
| `-DLLVM_ENABLE_DEBUGLOC_COVERAGE_TRACKING=COVERAGE`. When this has been set, |
| LLVM will enable runtime tracking of |
| {ref}`DebugLoc annotations <NewInstLocations>`, allowing `debugify` to ignore |
| instructions that have an explicitly recorded reason given for not having a |
| source location. |
| |
| For triaging source location bugs detected with `debugify`, you may find it |
| helpful to instead set the CMake flag to enable "origin tracking", |
| `-DLLVM_ENABLE_DEBUGLOC_COVERAGE_TRACKING=COVERAGE_AND_ORIGIN`. This flag |
| allows more detail to be added to `debugify`'s output, by including one or |
| more stacktraces with every missing source location, capturing the point at |
| which the empty source location was created, and every point at which it was |
| copied to an instruction, making it trivial in most cases to find the origin of |
| the underlying bug. When origin tracking is enabled, the |
| `--enable-origin-stacktraces` flag must be passed to actually trigger the |
| collecting of stacktraces; this flag can be passed as-is to collect stacktraces |
| all the time, or it can be passed with a comma-separated list of pass names (in |
| their internal PascalCase form) to enable collecting stacktraces during only |
| those passes. |
| |
| If using origin tracking, it is recommended to also build LLVM with debug info |
| enabled, so that the stacktrace can be accurately symbolized. |
| |
| :::{note} |
| The coverage tracking feature has been designed primarily for use with the |
| {ref}`original debug info preservation <OriginalDI>` mode of `debugify`, and |
| so may not be reliable in other settings. When using this mode, the |
| stacktraces produced by the `COVERAGE_AND_ORIGIN` setting will be printed |
| in an easy-to-read format as part of the reports generated by the |
| `llvm-original-di-preservation.py` script. |
| ::: |
| |
| (OriginalDI)= |
| |
| ### Test original debug info preservation in optimizations |
| |
| In addition to automatically generating debug info, the checks provided by |
| the `debugify` utility pass can also be used to test the preservation of |
| pre-existing debug info metadata. It could be run as follows: |
| |
| ```bash |
| # Run the pass by checking original Debug Info preservation. |
| $ opt -verify-debuginfo-preserve -pass-to-test sample.ll |
| |
| # Check the preservation of original Debug Info after each pass. |
| $ opt -verify-each-debuginfo-preserve -O2 sample.ll |
| ``` |
| |
| Limit number of observed functions to speed up the analysis: |
| |
| ```bash |
| # Test up to 100 functions (per compile unit) per pass. |
| $ opt -verify-each-debuginfo-preserve -O2 -debugify-func-limit=100 sample.ll |
| ``` |
| |
| Please do note that running `-verify-each-debuginfo-preserve` on big projects |
| could be heavily time consuming. Therefore, we suggest using |
| `-debugify-func-limit` with a suitable limit number to prevent extremely long |
| builds. |
| |
| Furthermore, there is a way to export the issues that have been found into |
| a JSON file as follows: |
| |
| ```bash |
| $ opt -verify-debuginfo-preserve -verify-di-preserve-export=sample.json -pass-to-test sample.ll |
| ``` |
| |
| and then use the `llvm/utils/llvm-original-di-preservation.py` script |
| to generate an HTML page with the issues reported in a more human-readable form |
| as follows: |
| |
| ```bash |
| $ llvm-original-di-preservation.py sample.json --report-file sample.html |
| ``` |
| |
| Testing of original debug info preservation can be invoked from front-end level |
| as follows: |
| |
| ```bash |
| # Test each pass. |
| $ clang -Xclang -fverify-debuginfo-preserve -g -O2 sample.c |
| |
| # Test each pass and export the issues report into the JSON file. |
| $ clang -Xclang -fverify-debuginfo-preserve -Xclang -fverify-debuginfo-preserve-export=sample.json -g -O2 sample.c |
| ``` |
| |
| Please do note that there are some known false positives, for source locations |
| and debug record checking, so that will be addressed as a future work. |
| |
| (MIRDebugify)= |
| |
| ### Mutation testing for MIR-level transformations |
| |
| A variant of the `debugify` utility described in |
| {ref}`Mutation testing for IR-level transformations <IRDebugify>` can be used |
| for MIR-level transformations as well: much like the IR-level pass, |
| `mir-debugify` inserts sequentially increasing line locations to each |
| `MachineInstr` in a `Module`. And the MIR-level `mir-check-debugify` is |
| similar to IR-level `check-debugify` pass. |
| |
| For example, here is a snippet before: |
| |
| ```llvm |
| name: test |
| body: | |
| bb.1 (%ir-block.0): |
| %0:_(s32) = IMPLICIT_DEF |
| %1:_(s32) = IMPLICIT_DEF |
| %2:_(s32) = G_CONSTANT i32 2 |
| %3:_(s32) = G_ADD %0, %2 |
| %4:_(s32) = G_SUB %3, %1 |
| ``` |
| |
| and after running `llc -run-pass=mir-debugify`: |
| |
| ```llvm |
| name: test |
| body: | |
| bb.0 (%ir-block.0): |
| %0:_(s32) = IMPLICIT_DEF debug-location !12 |
| DBG_VALUE %0(s32), $noreg, !9, !DIExpression(), debug-location !12 |
| %1:_(s32) = IMPLICIT_DEF debug-location !13 |
| DBG_VALUE %1(s32), $noreg, !11, !DIExpression(), debug-location !13 |
| %2:_(s32) = G_CONSTANT i32 2, debug-location !14 |
| DBG_VALUE %2(s32), $noreg, !9, !DIExpression(), debug-location !14 |
| %3:_(s32) = G_ADD %0, %2, debug-location !DILocation(line: 4, column: 1, scope: !6) |
| DBG_VALUE %3(s32), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 4, column: 1, scope: !6) |
| %4:_(s32) = G_SUB %3, %1, debug-location !DILocation(line: 5, column: 1, scope: !6) |
| DBG_VALUE %4(s32), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 5, column: 1, scope: !6) |
| ``` |
| |
| By default, `mir-debugify` inserts `DBG_VALUE` instructions **everywhere** |
| it is legal to do so. In particular, every (non-PHI) machine instruction that |
| defines a register must be followed by a `DBG_VALUE` use of that def. If |
| an instruction does not define a register, but can be followed by a debug inst, |
| MIRDebugify inserts a `DBG_VALUE` that references a constant. Insertion of |
| `DBG_VALUE`'s can be disabled by setting `-debugify-level=locations`. |
| |
| To run MIRDebugify once, simply insert `mir-debugify` into your `llc` |
| invocation, like: |
| |
| ```bash |
| # Before some other pass. |
| $ llc -run-pass=mir-debugify,other-pass ... |
| |
| # After some other pass. |
| $ llc -run-pass=other-pass,mir-debugify ... |
| ``` |
| |
| To run MIRDebugify before each pass in a pipeline, use |
| `-debugify-and-strip-all-safe`. This can be combined with `-start-before` |
| and `-start-after`. For example: |
| |
| ```bash |
| $ llc -debugify-and-strip-all-safe -run-pass=... <other llc args> |
| $ llc -debugify-and-strip-all-safe -O1 <other llc args> |
| ``` |
| |
| If you want to check it after each pass in a pipeline, use |
| `-debugify-check-and-strip-all-safe`. This can also be combined with |
| `-start-before` and `-start-after`. For example: |
| |
| ```bash |
| $ llc -debugify-check-and-strip-all-safe -run-pass=... <other llc args> |
| $ llc -debugify-check-and-strip-all-safe -O1 <other llc args> |
| ``` |
| |
| To check all debug info from a test, use `mir-check-debugify`, like: |
| |
| ```bash |
| $ llc -run-pass=mir-debugify,other-pass,mir-check-debugify |
| ``` |
| |
| To strip out all debug info from a test, use `mir-strip-debug`, like: |
| |
| ```bash |
| $ llc -run-pass=mir-debugify,other-pass,mir-strip-debug |
| ``` |
| |
| It can be useful to combine `mir-debugify`, `mir-check-debugify` and/or |
| `mir-strip-debug` to identify backend transformations which break in |
| the presence of debug info. For example, to run the AArch64 backend tests |
| with all normal passes "sandwiched" in between MIRDebugify and |
| MIRStripDebugify mutation passes, run: |
| |
| ```bash |
| $ llvm-lit test/CodeGen/AArch64 -Dllc="llc -debugify-and-strip-all-safe" |
| ``` |
| |
| ### Using LostDebugLocObserver |
| |
| TODO |