[Docs] Testing Debug Info Preservation in Optimizations

Differential Revision: https://reviews.llvm.org/D49053

llvm-svn: 337457
This commit is contained in:
Anastasis Grammenos 2018-07-19 14:08:54 +00:00
parent 534b6bfa64
commit f7affed4e4

View File

@ -77,8 +77,8 @@ source from generated code.
.. _intro_debugopt:
Debugging optimized code
------------------------
Debug information and optimizations
-----------------------------------
An extremely high priority of LLVM debugging information is to make it interact
well with optimizations and analysis. In particular, the LLVM debug
@ -1464,3 +1464,180 @@ Improving LLVM's CodeView support is a process of finding interesting type
records, constructing a C++ test case that makes MSVC emit those records,
dumping the records, understanding them, and then generating equivalent records
in LLVM's backend.
Testing Debug Info Preservation in Optimizations
================================================
The following paragraphs are an introduction to the debugify utility
and examples of how to use it in regression tests to check debug info
preservation after optimizations.
The ``debugify`` utility
------------------------
The ``debugify`` synthetic debug info testing utility consists of two
main parts. The ``debugify`` pass and the ``check-debugify`` one. They are
meant to be used with ``opt`` for development purposes.
The first applies synthetic debug information to every instruction of the module,
while the latter 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 when possible.
For example, here is a module before:
.. code-block:: llvm
define dso_local 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`` on it we get:
.. code-block:: llvm
define dso_local 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)
The following is an example of the -check-debugify output:
.. code-block:: none
$ opt -enable-debugify -loop-vectorize llvm/test/Transforms/LoopVectorize/i8-induction.ll -disable-output
ERROR: Instruction with empty DebugLoc in function f -- %index = phi i32 [ 0, %vector.ph ], [ %index.next, %vector.body ]
Errors/warnings can range from instructions with empty debug location to an
instruction having a type that's incompatible with the source variable it describes,
all the way to missing lines and missing debug value intrinsics.
Fixing errors
^^^^^^^^^^^^^
Each of the errors above has a relevant API available to fix it.
* In the case of missing debug location, ``Instruction::setDebugLoc`` or possibly
``IRBuilder::setCurrentDebugLocation`` when using a Builder and the new location
should be reused.
* When a debug value has incompatible type ``llvm::replaceAllDbgUsesWith`` can be used.
After a RAUW call an incompatible type error can occur because RAUW does not handle
widening and narrowing of variables while ``llvm::replaceAllDbgUsesWith`` does. It is
also capable of changing the DWARF expression used by the debugger to describe the variable.
It also prevents use-before-def by salvaging or deleting invalid debug values.
* When a debug value is missing ``llvm::salvageDebugInfo`` can be used when no replacement
exists, or ``llvm::replaceAllDbgUsesWith`` when a replacement exists.
Using ``debugify``
------------------
In order for ``check-debugify`` to work, the DI must be coming from
``debugify``. Thus, modules with existing DI will be skipped.
The most straightforward way to use ``debugify`` is as follows::
$ 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.
Some other ways to run debugify are avaliable:
.. 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
``debugify`` can also be used to test a backend, e.g:
.. code-block:: bash
$ opt -debugify < sample.ll | llc -o -
``debugify`` in regression tests
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``-debugify`` pass is especially helpful when it comes to testing that
a given pass preserves DI while transforming the module. For this to work,
the ``-debugify`` output must be stable enough to use in regression tests.
Changes to this pass are not allowed to break existing tests.
It allows us to test for DI loss in the same tests we check that the
transformation is actually doing what it should.
Here is an example from ``test/Transforms/InstCombine/cast-mul-select.ll``:
.. code-block:: llvm
; RUN: opt < %s -debugify -instcombine -S | FileCheck %s --check-prefix=DEBUGINFO
define i32 @mul(i32 %x, i32 %y) {
; DBGINFO-LABEL: @mul(
; DBGINFO-NEXT: [[C:%.*]] = mul i32 {{.*}}
; DBGINFO-NEXT: call void @llvm.dbg.value(metadata i32 [[C]]
; DBGINFO-NEXT: [[D:%.*]] = and i32 {{.*}}
; DBGINFO-NEXT: call void @llvm.dbg.value(metadata i32 [[D]]
%A = trunc i32 %x to i8
%B = trunc i32 %y to i8
%C = mul i8 %A, %B
%D = zext i8 %C to i32
ret i32 %D
}
Here we test that the two ``dbg.value`` instrinsics are preserved and
are correctly pointing to the ``[[C]]`` and ``[[D]]`` variables.
.. note::
Note, that when writing this kind of regression tests, it is important
to make them as robust as possible. That's why we should try to avoid
hardcoding line/variable numbers in check lines. If for example you test
for a ``DILocation`` to have a specific line number, and someone later adds
an instruction before the one we check the test will fail. In the cases this
can't be avoided (say, if a test wouldn't be precise enough), moving the
test to it's own file is preferred.