mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-10-18 18:42:46 +02:00
[Docs] Testing Debug Info Preservation in Optimizations
Differential Revision: https://reviews.llvm.org/D49053 llvm-svn: 337457
This commit is contained in:
parent
534b6bfa64
commit
f7affed4e4
@ -77,8 +77,8 @@ source from generated code.
|
|||||||
|
|
||||||
.. _intro_debugopt:
|
.. _intro_debugopt:
|
||||||
|
|
||||||
Debugging optimized code
|
Debug information and optimizations
|
||||||
------------------------
|
-----------------------------------
|
||||||
|
|
||||||
An extremely high priority of LLVM debugging information is to make it interact
|
An extremely high priority of LLVM debugging information is to make it interact
|
||||||
well with optimizations and analysis. In particular, the LLVM debug
|
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,
|
records, constructing a C++ test case that makes MSVC emit those records,
|
||||||
dumping the records, understanding them, and then generating equivalent records
|
dumping the records, understanding them, and then generating equivalent records
|
||||||
in LLVM's backend.
|
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.
|
||||||
|
Loading…
Reference in New Issue
Block a user