forked from OSchip/llvm-project
425 lines
16 KiB
ReStructuredText
425 lines
16 KiB
ReStructuredText
=======================================================
|
|
How to Update Debug Info: A Guide for LLVM Pass Authors
|
|
=======================================================
|
|
|
|
.. contents::
|
|
:local:
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
.. _WhenToMergeLocation:
|
|
|
|
When to merge instruction locations
|
|
-----------------------------------
|
|
|
|
A transformation should merge instruction locations if it replaces multiple
|
|
instructions with a single merged instruction, *and* that merged instruction
|
|
does not correspond to any of the original instructions' locations. The API to
|
|
use is ``Instruction::applyMergedLocation``.
|
|
|
|
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. This rule is also meant to assist SamplePGO by banning
|
|
scenarios in which a sample of a block containing a merged instruction is
|
|
misattributed to a block containing one of the instructions-to-be-merged.
|
|
|
|
Examples of transformations that should follow this rule include:
|
|
|
|
* Merging identical loads/stores which occur on both sides of a CFG diamond
|
|
(see the ``MergedLoadStoreMotion`` pass).
|
|
|
|
* Merging identical loop-invariant stores (see the LICM utility
|
|
``llvm::promoteLoopAccessesToScalars``).
|
|
|
|
* Peephole optimizations which combine multiple instructions together, like
|
|
``(add (mul A B) C) => llvm.fma.f32(A, B, C)``. Note that the location of
|
|
the ``fma`` does not exactly correspond to the locations of either the
|
|
``mul`` or the ``add`` instructions.
|
|
|
|
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.
|
|
|
|
* 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 identical instructions which appear in several successor blocks into
|
|
a predecessor block (see ``BranchFolder::HoistCommonCodeInSuccs``). In this
|
|
case there is no single merged instruction. 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::setDebugLoc()``.
|
|
|
|
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.
|
|
|
|
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 ``llvm.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:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i16 @foo(i16 %a) {
|
|
%b = sext i16 %a to i32
|
|
%c = and i32 %b, 15
|
|
call void @llvm.dbg.value(metadata 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:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i16 @foo(i16 %a) {
|
|
call void @llvm.dbg.value(metadata 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:
|
|
|
|
.. code-block:: cpp
|
|
|
|
llvm::replaceAllDbgUsesWith(%c, theSimplifiedAndInstruction, ...)
|
|
|
|
This results in better debug info because the debug use of ``%c`` is preserved:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define i16 @foo(i16 %a) {
|
|
%simplified = and i16 %a, 15
|
|
call void @llvm.dbg.value(metadata 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
|
|
|
|
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
|
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
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 intrinsics everywhere possible.
|
|
|
|
For example, here is a module before:
|
|
|
|
.. code-block:: 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``:
|
|
|
|
.. code-block:: llvm
|
|
|
|
define void @f(i32* %x) !dbg !6 {
|
|
entry:
|
|
%x.addr = alloca i32*, align 8, !dbg !12
|
|
call void @llvm.dbg.value(metadata i32** %x.addr, metadata !9, metadata !DIExpression()), !dbg !12
|
|
store i32* %x, i32** %x.addr, align 8, !dbg !13
|
|
%0 = load i32*, i32** %x.addr, align 8, !dbg !14
|
|
call void @llvm.dbg.value(metadata i32* %0, metadata !11, metadata !DIExpression()), !dbg !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:
|
|
|
|
.. code-block:: 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:
|
|
|
|
.. code-block:: 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:
|
|
|
|
.. code-block:: 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.
|
|
|
|
.. _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`` (although there is no equivalent MIR-level
|
|
``check-debugify`` pass).
|
|
|
|
For example, here is a snippet before:
|
|
|
|
.. code-block:: 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``:
|
|
|
|
.. code-block:: 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:
|
|
|
|
.. code-block:: 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:
|
|
|
|
.. code-block:: bash
|
|
|
|
$ llc -debugify-and-strip-all-safe -run-pass=... <other llc args>
|
|
$ llc -debugify-and-strip-all-safe -O1 <other llc args>
|
|
|
|
To strip out all debug info from a test, use ``mir-strip-debug``, like:
|
|
|
|
.. code-block:: bash
|
|
|
|
$ llc -run-pass=mir-debugify,other-pass,mir-strip-debug
|
|
|
|
It can be useful to combine ``mir-debugify`` and ``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:
|
|
|
|
.. code-block:: bash
|
|
|
|
$ llvm-lit test/CodeGen/AArch64 -Dllc="llc -debugify-and-strip-all-safe"
|
|
|
|
Using LostDebugLocObserver
|
|
--------------------------
|
|
|
|
TODO
|