Index: docs/SourceLevelDebugging.rst =================================================================== --- docs/SourceLevelDebugging.rst +++ docs/SourceLevelDebugging.rst @@ -77,8 +77,8 @@ .. _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 @@ 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.