aboutsummaryrefslogtreecommitdiff
path: root/docs/SourceLevelDebugging.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/SourceLevelDebugging.rst')
-rw-r--r--docs/SourceLevelDebugging.rst181
1 files changed, 179 insertions, 2 deletions
diff --git a/docs/SourceLevelDebugging.rst b/docs/SourceLevelDebugging.rst
index 103c6e0365ba..3fa738c7e442 100644
--- a/docs/SourceLevelDebugging.rst
+++ b/docs/SourceLevelDebugging.rst
@@ -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.