mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-29 22:30:33 +00: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:
|
||||
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user