[PATCH] Fix non-determinism causing assertion failure (PR22750)

Dario Domizioli dario.domizioli at gmail.com
Tue Mar 3 08:13:26 PST 2015


Hello LLVM,

I have found and filed PR22750, and I've looked into fixing it.

Basically, it's an assertion failure during debug info generation, and its
cause is the interaction of two factors:
1) When generating a DW_TAG_imported_declaration DIE which imports another
imported declaration, the code in
lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp asserts that the second
imported declaration must already have a DIE (line 661).
2) There is a non-determinism in the order in which imported declarations
within the same scope are processed.

Because of the non-determinism (2), it is possible that an imported
declaration is processed before another one it depends on, breaking the
assumption in (1).

I have tracked down the source of the non-determinism.
The imported declaration DIDescriptors are sorted by scope in
DwarfDebug::beginModule(), in lib/CodeGen/AsmPrinter/DwarfDebug.cpp:480;
however that sort is not a stable_sort, therefore the order of the
declarations within the same scope is not preserved.

The attached patch changes the std::sort to a std::stable_sort and it fixes
the problem.

I'm not 100% sure this is the right solution, even if it works. My question
is: would it be better to relax the assumption in (1), e.g. by making the
code create the dependency DIE on demand?

Cheers,
    Dario Domizioli
    SN Systems - Sony Computer Entertainment Group
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20150303/e81112aa/attachment.html>
-------------- next part --------------
Index: lib/CodeGen/AsmPrinter/DwarfDebug.cpp
===================================================================
--- lib/CodeGen/AsmPrinter/DwarfDebug.cpp	(revision 231072)
+++ lib/CodeGen/AsmPrinter/DwarfDebug.cpp	(working copy)
@@ -477,8 +477,8 @@
       ScopesWithImportedEntities.push_back(std::make_pair(
           DIImportedEntity(ImportedEntities.getElement(i)).getContext(),
           ImportedEntities.getElement(i)));
-    std::sort(ScopesWithImportedEntities.begin(),
-              ScopesWithImportedEntities.end(), less_first());
+    std::stable_sort(ScopesWithImportedEntities.begin(),
+                     ScopesWithImportedEntities.end(), less_first());
     DIArray GVs = CUNode.getGlobalVariables();
     for (unsigned i = 0, e = GVs.getNumElements(); i != e; ++i)
       CU.getOrCreateGlobalVariableDIE(DIGlobalVariable(GVs.getElement(i)));
Index: test/DebugInfo/X86/ns_stress.ll
===================================================================
--- test/DebugInfo/X86/ns_stress.ll	(revision 0)
+++ test/DebugInfo/X86/ns_stress.ll	(working copy)
@@ -0,0 +1,159 @@
+; RUN: llc -mtriple=x86_64-unknown-linux-gnu -filetype=obj < %s | llvm-dwarfdump -debug-dump=info - | FileCheck %s
+
+; This is a little stress test for DW_TAG_imported_declaration entities.
+; It was generated from the following C++ code:
+;
+;  #define ONE_BLOCK           \
+;      {                       \
+;          namespace A = base; \
+;          namespace B = A;    \
+;          namespace C = B;    \
+;          namespace D = C;    \
+;          namespace E = D;    \
+;          E::i++;             \
+;      }
+;
+;  namespace base { int i = 66; }
+;  int main() {
+;      ONE_BLOCK;
+;      ONE_BLOCK;
+;      ONE_BLOCK;
+;      ONE_BLOCK;
+;      ONE_BLOCK;
+;      return base::i;
+;  }
+;
+; > clang -g -O0 -S -emit-llvm <source file> -o <ll file>
+;
+;
+; The code handling DW_TAG_imported_declaration DIEs assumes that, upon
+; processing an imported declaration, the DIE for the object it refers to has
+; already been created. This is usually true.
+; However, there was a non-determinism in the code whereby the imported
+; declaration metadata was sorted non-deterministically, causing the order of
+; items to change: this caused the code to fail to find a DIE when it should
+; have been there, leading to an assertion failure. 25 entries appear to be
+; enough to trigger the non-determinism and the failure.
+;
+; With the fix, the order of items is preserved during processing and therefore
+; the DIEs are created sequentially and everything works as expected. 
+;
+; The CHECK directives just check that we do indeed generate twenty-five
+; DW_TAG_imported_declaration entities.
+;
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+; CHECK: DW_TAG_imported_declaration
+;
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at _ZN4base1iE = global i32 66, align 4
+
+; Function Attrs: nounwind uwtable
+define i32 @main() #0 {
+entry:
+  %retval = alloca i32, align 4
+  store i32 0, i32* %retval
+  %0 = load i32, i32* @_ZN4base1iE, align 4, !dbg !46
+  %inc = add nsw i32 %0, 1, !dbg !46
+  store i32 %inc, i32* @_ZN4base1iE, align 4, !dbg !46
+  %1 = load i32, i32* @_ZN4base1iE, align 4, !dbg !47
+  %inc1 = add nsw i32 %1, 1, !dbg !47
+  store i32 %inc1, i32* @_ZN4base1iE, align 4, !dbg !47
+  %2 = load i32, i32* @_ZN4base1iE, align 4, !dbg !48
+  %inc2 = add nsw i32 %2, 1, !dbg !48
+  store i32 %inc2, i32* @_ZN4base1iE, align 4, !dbg !48
+  %3 = load i32, i32* @_ZN4base1iE, align 4, !dbg !49
+  %inc3 = add nsw i32 %3, 1, !dbg !49
+  store i32 %inc3, i32* @_ZN4base1iE, align 4, !dbg !49
+  %4 = load i32, i32* @_ZN4base1iE, align 4, !dbg !50
+  %inc4 = add nsw i32 %4, 1, !dbg !50
+  store i32 %inc4, i32* @_ZN4base1iE, align 4, !dbg !50
+  %5 = load i32, i32* @_ZN4base1iE, align 4, !dbg !51
+  ret i32 %5, !dbg !52
+}
+
+attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!43, !44}
+!llvm.ident = !{!45}
+
+!0 = !{!"0x11\004\00clang version 3.7.0 (trunk)\000\00\000\00\001", !1, !2, !2, !3, !9, !12} ; [ DW_TAG_compile_unit ] [/home/dario/dev/upstream/ns_stress.cpp] [DW_LANG_C_plus_plus]
+!1 = !{!"ns_stress.cpp", !"/home/dario/dev/upstream"}
+!2 = !{}
+!3 = !{!4}
+!4 = !{!"0x2e\00main\00main\00\0013\000\001\000\000\00256\000\0013", !1, !5, !6, null, i32 ()* @main, null, null, !2} ; [ DW_TAG_subprogram ] [line 13] [def] [main]
+!5 = !{!"0x29", !1}                               ; [ DW_TAG_file_type ] [/home/dario/dev/upstream/ns_stress.cpp]
+!6 = !{!"0x15\00\000\000\000\000\000\000", null, null, null, !7, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ]
+!7 = !{!8}
+!8 = !{!"0x24\00int\000\0032\0032\000\000\005", null, null} ; [ DW_TAG_base_type ] [int] [line 0, size 32, align 32, offset 0, enc DW_ATE_signed]
+!9 = !{!10}
+!10 = !{!"0x34\00i\00i\00_ZN4base1iE\0012\000\001", !11, !5, !8, i32* @_ZN4base1iE, null} ; [ DW_TAG_variable ] [i] [line 12] [def]
+!11 = !{!"0x39\00base\0012", !1, null}            ; [ DW_TAG_namespace ] [base] [line 12]
+!12 = !{!13, !15, !16, !17, !18, !19, !21, !22, !23, !24, !25, !27, !28, !29, !30, !31, !33, !34, !35, !36, !37, !39, !40, !41, !42}
+!13 = !{!"0x8\0014\00A", !14, !11}                ; [ DW_TAG_imported_declaration ]
+!14 = !{!"0xb\0014\005\000", !1, !4}              ; [ DW_TAG_lexical_block ] [/home/dario/dev/upstream/ns_stress.cpp]
+!15 = !{!"0x8\0014\00B", !14, !13}                ; [ DW_TAG_imported_declaration ]
+!16 = !{!"0x8\0014\00C", !14, !15}                ; [ DW_TAG_imported_declaration ]
+!17 = !{!"0x8\0014\00D", !14, !16}                ; [ DW_TAG_imported_declaration ]
+!18 = !{!"0x8\0014\00E", !14, !17}                ; [ DW_TAG_imported_declaration ]
+!19 = !{!"0x8\0015\00A", !20, !11}                ; [ DW_TAG_imported_declaration ]
+!20 = !{!"0xb\0015\005\001", !1, !4}              ; [ DW_TAG_lexical_block ] [/home/dario/dev/upstream/ns_stress.cpp]
+!21 = !{!"0x8\0015\00B", !20, !19}                ; [ DW_TAG_imported_declaration ]
+!22 = !{!"0x8\0015\00C", !20, !21}                ; [ DW_TAG_imported_declaration ]
+!23 = !{!"0x8\0015\00D", !20, !22}                ; [ DW_TAG_imported_declaration ]
+!24 = !{!"0x8\0015\00E", !20, !23}                ; [ DW_TAG_imported_declaration ]
+!25 = !{!"0x8\0016\00A", !26, !11}                ; [ DW_TAG_imported_declaration ]
+!26 = !{!"0xb\0016\005\002", !1, !4}              ; [ DW_TAG_lexical_block ] [/home/dario/dev/upstream/ns_stress.cpp]
+!27 = !{!"0x8\0016\00B", !26, !25}                ; [ DW_TAG_imported_declaration ]
+!28 = !{!"0x8\0016\00C", !26, !27}                ; [ DW_TAG_imported_declaration ]
+!29 = !{!"0x8\0016\00D", !26, !28}                ; [ DW_TAG_imported_declaration ]
+!30 = !{!"0x8\0016\00E", !26, !29}                ; [ DW_TAG_imported_declaration ]
+!31 = !{!"0x8\0017\00A", !32, !11}                ; [ DW_TAG_imported_declaration ]
+!32 = !{!"0xb\0017\005\003", !1, !4}              ; [ DW_TAG_lexical_block ] [/home/dario/dev/upstream/ns_stress.cpp]
+!33 = !{!"0x8\0017\00B", !32, !31}                ; [ DW_TAG_imported_declaration ]
+!34 = !{!"0x8\0017\00C", !32, !33}                ; [ DW_TAG_imported_declaration ]
+!35 = !{!"0x8\0017\00D", !32, !34}                ; [ DW_TAG_imported_declaration ]
+!36 = !{!"0x8\0017\00E", !32, !35}                ; [ DW_TAG_imported_declaration ]
+!37 = !{!"0x8\0018\00A", !38, !11}                ; [ DW_TAG_imported_declaration ]
+!38 = !{!"0xb\0018\005\004", !1, !4}              ; [ DW_TAG_lexical_block ] [/home/dario/dev/upstream/ns_stress.cpp]
+!39 = !{!"0x8\0018\00B", !38, !37}                ; [ DW_TAG_imported_declaration ]
+!40 = !{!"0x8\0018\00C", !38, !39}                ; [ DW_TAG_imported_declaration ]
+!41 = !{!"0x8\0018\00D", !38, !40}                ; [ DW_TAG_imported_declaration ]
+!42 = !{!"0x8\0018\00E", !38, !41}                ; [ DW_TAG_imported_declaration ]
+!43 = !{i32 2, !"Dwarf Version", i32 4}
+!44 = !{i32 2, !"Debug Info Version", i32 2}
+!45 = !{!"clang version 3.7.0 (trunk)"}
+!46 = !MDLocation(line: 14, column: 5, scope: !14)
+!47 = !MDLocation(line: 15, column: 5, scope: !20)
+!48 = !MDLocation(line: 16, column: 5, scope: !26)
+!49 = !MDLocation(line: 17, column: 5, scope: !32)
+!50 = !MDLocation(line: 18, column: 5, scope: !38)
+!51 = !MDLocation(line: 19, column: 12, scope: !4)
+!52 = !MDLocation(line: 19, column: 5, scope: !4)


More information about the llvm-commits mailing list