Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Assertion failure: "conflicting locations for variable" #64149

Open
nikic opened this issue Jul 27, 2023 · 14 comments
Open

Assertion failure: "conflicting locations for variable" #64149

nikic opened this issue Jul 27, 2023 · 14 comments

Comments

@nikic
Copy link
Contributor

nikic commented Jul 27, 2023

From https://bugzilla.redhat.com/show_bug.cgi?id=2226564:

; RUN: llc < %s
define void @test() !dbg !4 {
  %a1 = alloca { i64, [3 x i64] }, i32 0, align 8
  %a2 = alloca [6 x i8], i32 0, align 2
  call void @llvm.dbg.declare(metadata ptr %a2, metadata !7, metadata !DIExpression(DW_OP_LLVM_fragment, 80, 48)), !dbg !16
  call void @llvm.dbg.declare(metadata ptr %a1, metadata !7, metadata !DIExpression()), !dbg !16
  call void @llvm.memcpy.p0.p0.i64(ptr null, ptr null, i64 1, i1 false), !dbg !18
  ret void
}

; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
declare void @llvm.dbg.declare(metadata, metadata, metadata) #0

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1

attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }

!llvm.module.flags = !{!0}
!llvm.dbg.cu = !{!1}

!0 = !{i32 2, !"Debug Info Version", i32 3}
!1 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !2, producer: "clang LLVM (rustc version 1.73.0-nightly (31395ec38 2023-07-24) (Fedora 1.71.0-2.fc39))", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !3, globals: !3, splitDebugInlining: false)
!2 = !DIFile(filename: "src/cargo/lib.rs/@/cargo.c1756bb5859f0146-cgu.10", directory: "/builddir/build/BUILD/rustc-nightly-src/src/tools/cargo")
!3 = !{}
!4 = distinct !DISubprogram(name: "__deserialize_content<cargo::util::config::de::Deserializer, serde::__private::de::content::ContentVisitor>", linkageName: "_RINvYNtNtNtNtCsgBM6TfnZ7YU_5cargo4util6config2de12DeserializerNtNtCs8ELY4rCNAV8_5serde2de12Deserializer21___deserialize_contentNtNtNtNtB12_9___private2de7content14ContentVisitorEBb_", scope: null, file: !5, line: 1226, type: !6, scopeLine: 1226, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !1, templateParams: !3, retainedNodes: !3)
!5 = !DIFile(filename: "/builddir/build/BUILD/rustc-nightly-src/vendor/serde/src/de/mod.rs", directory: "", checksumkind: CSK_MD5, checksum: "fe168d8d4803c3f449894ca7aef5947a")
!6 = distinct !DISubroutineType(types: !3)
!7 = !DILocalVariable(name: "res", scope: !8, file: !9, line: 52, type: !14, align: 8)
!8 = distinct !DILexicalBlock(scope: !10, file: !9, line: 52, column: 13)
!9 = !DIFile(filename: "src/cargo/util/config/de.rs", directory: "/builddir/build/BUILD/rustc-nightly-src/src/tools/cargo", checksumkind: CSK_MD5, checksum: "3faaefd60c37a4d05ff3cc3354e2863b")
!10 = distinct !DILexicalBlock(scope: !11, file: !9, line: 51, column: 30)
!11 = distinct !DILexicalBlock(scope: !12, file: !9, line: 50, column: 9)
!12 = distinct !DISubprogram(name: "deserialize_any<serde::__private::de::content::ContentVisitor>", linkageName: "_RINvXNtNtNtCsgBM6TfnZ7YU_5cargo4util6config2deNtB3_12DeserializerNtNtCs8ELY4rCNAV8_5serde2de12Deserializer15deserialize_anyNtNtNtNtB15_9___private2de7content14ContentVisitorEB9_", scope: null, file: !9, line: 46, type: !13, scopeLine: 46, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !1, templateParams: !3, retainedNodes: !3)
!13 = distinct !DISubroutineType(types: !3)
!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "(core::result::Result<serde::__private::de::content::Content, cargo::util::config::ConfigError>, cargo::util::config::value::Definition)", file: !15, size: 576, align: 64, elements: !3, templateParams: !3, identifier: "5b7515cf7e437618b1f84245f51f5b4c")
!15 = !DIFile(filename: "<unknown>", directory: "")
!16 = !DILocation(line: 52, column: 17, scope: !8, inlinedAt: !17)
!17 = distinct !DILocation(line: 1234, column: 9, scope: !4)
!18 = !DILocation(line: 62, column: 23, scope: !8, inlinedAt: !17)

Results in:

llc: /home/npopov/repos/llvm-project/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp:318: void llvm::DbgVariable::addMMIEntry(const llvm::DbgVariable&): Assertion `(FrameIndexExprs.size() == 1 || llvm::all_of(FrameIndexExprs, [](FrameIndexExpr &FIE) { return FIE.Expr && FIE.Expr->isFragment(); })) && "conflicting locations for variable"' failed.
[...]
 #7 0x00007ff4180497fc abort /usr/src/debug/glibc-2.36-9.fc37.x86_64/stdlib/abort.c:81:7
 #8 0x00007ff41804971b _nl_load_domain.cold /usr/src/debug/glibc-2.36-9.fc37.x86_64/intl/loadmsgcat.c:1177:9
 #9 0x00007ff418058656 (/lib64/libc.so.6+0x35656)
#10 0x00007ff41d4a5410 llvm::DbgVariable::addMMIEntry(llvm::DbgVariable const&) /home/npopov/repos/llvm-project/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp:308:7
#11 0x00007ff41d4ab54a llvm::DwarfDebug::collectVariableInfoFromMFTable(llvm::DwarfCompileUnit&, llvm::DenseSet<std::pair<llvm::DINode const*, llvm::DILocation const*>, llvm::DenseMapInfo<std::pair<llvm::DINode const*, llvm::DILocation const*>, void>>&) /home/npopov/repos/llvm-project/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp:1581:28
#12 0x00007ff41d4ac896 llvm::DwarfDebug::collectEntityInfo(llvm::DwarfCompileUnit&, llvm::DISubprogram const*, llvm::DenseSet<std::pair<llvm::DINode const*, llvm::DILocation const*>, llvm::DenseMapInfo<std::pair<llvm::DINode const*, llvm::DILocation const*>, void>>&) /home/npopov/repos/llvm-project/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp:1879:24
#13 0x00007ff41d4ae15e llvm::DwarfDebug::endFunctionImpl(llvm::MachineFunction const*) /home/npopov/repos/llvm-project/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp:2291:24
#14 0x00007ff41d45c662 llvm::DebugHandlerBase::endFunction(llvm::MachineFunction const*) /home/npopov/repos/llvm-project/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp:412:18
#15 0x00007ff41d3ecd45 llvm::AsmPrinter::emitFunctionBody() /home/npopov/repos/llvm-project/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp:1901:3
@llvmbot
Copy link
Member

llvmbot commented Jul 27, 2023

@llvm/issue-subscribers-debuginfo

@jmorse
Copy link
Member

jmorse commented Jul 27, 2023

The problem here is the two dbg.declares sort-of overlapping (one describes the location of a field in the source variable, the other describes the location of an entire variable). I don't believe we can have a well defined answer to what that means: variables can't have two locations for any part of them(DWARF can express it, but I don't believe LLVM is designed to handle that). Adding a fragment to the second dbg.declare that overlaps with the first causes a different assertion to fire for similar reasons.

IMO the solution is:

  • Better IR verifier rules to reject this, as we shouldn't crash on things we accept,
  • Fixing whatever's producing it. I've no familiarity with rust etc, is an LLVM optimisation producing this or are both dbg.declares produced by rust?

@nikic
Copy link
Contributor Author

nikic commented Jul 27, 2023

@cuviper Do you happen to still have the original, unreduced bitcode for this?

@cuviper
Copy link
Member

cuviper commented Jul 27, 2023

I do -- I just added it to bugzilla.

@nikic
Copy link
Contributor Author

nikic commented Jul 31, 2023

The IR that rustc produces reduces to something like this: https://gist.github.com/nikic/efc3134a0aee3cb9f71601e356c5065b This doesn't trigger an assert by itself, but does after -passes=sroa.

However that input still has two dbg.declares on different allocas with the same DILocalVariable, so I guess that's still an invalid input, just not in a way that crashes llc.

@nikic
Copy link
Contributor Author

nikic commented Aug 22, 2023

Here's a simple example where rustc assigns the same DILocalVariable to multiple allocas: https://rust.godbolt.org/z/M4cPKhWaE

The IR contains something like this:

  call void @llvm.dbg.declare(metadata ptr %s.dbg.spill, metadata !22, metadata !DIExpression()), !dbg !30
  call void @llvm.dbg.declare(metadata ptr %s.dbg.spill, metadata !23, metadata !DIExpression()), !dbg !31
  call void @llvm.dbg.declare(metadata ptr %s.dbg.spill, metadata !32, metadata !DIExpression()), !dbg !37
  call void @llvm.dbg.declare(metadata ptr %slice.dbg.spill, metadata !23, metadata !DIExpression()), !dbg !31
  call void @llvm.dbg.declare(metadata ptr %slice.dbg.spill, metadata !32, metadata !DIExpression()), !dbg !37

This looks pretty nonsensical to me.

@cuviper
Copy link
Member

cuviper commented Aug 22, 2023

On the producer side, this is now filed at rust-lang/rust#115113

But on the LLVM side, it would still be nice to catch this in the IR verifier.

@nikic
Copy link
Contributor Author

nikic commented Aug 23, 2023

@jmorse Could you please clarify what the precise requirements here are? Just having multiple dbg.declares for the same DILocalVariable seems like something that should be fine when inlining is involved, as you can legitimately have multiple copies of the same variable. That behavior exists in clang as well (https://clang.godbolt.org/z/1v1orvxzW with assignment tracking disabled for more direct comparison).

So I'm not entirely clear at which point "multiple overlapping dbg.declares" becomes invalid. Is it just the case where different fragments are involved?

(The debuginfo generated by rustc is wrong in any case, I'm just trying to understand what the verifier check would actually do.)

@OCHyams
Copy link
Contributor

OCHyams commented Aug 23, 2023

fyi @jmorse is away at the moment.

Just having multiple dbg.declares for the same DILocalVariable seems like something that should be fine when inlining is involved, as you can legitimately have multiple copies of the same variable

In that case the DILocation attached to the debugging intrinsic has an InlinedAt field which is another DILocation that describes the original call site. After inlining that debugging intrinsic a second time its InlinedAt DILocation will have an InlinedAt field too for that inlined call site.

Unlike most DILocations, InlinedAt DILocations are created as distinct (rather than uniqued) metadata.

We generally identify a variable in LLVM as a tuple of the (DILocalVariable, FragmentInfo from the DIExpression, InlinedAt from the DILocation).

@nikic
Copy link
Contributor Author

nikic commented Aug 23, 2023

We generally identify a variable in LLVM as a tuple of the (DILocalVariable, FragmentInfo from the DIExpression, InlinedAt from the DILocation).

Thanks! This is the bit I was missing.

It looks like it's not possible to verify that there are no repeated DebugVariables, because these may be introduced by transforms that perform code cloning (e.g. LoopRotate, LoopUnswitch, etc). This is discussed in #32504.

So I guess the only thing we can verify is that all fragments for a given (DILocalVariable, InlinedAt) are non-overlapping (excluding exact overlaps).

@nikic
Copy link
Contributor Author

nikic commented Aug 23, 2023

Testing this patch: https://gist.github.com/nikic/d8c328c4fe13df2d71ac468923b6cec9

Results in a few test failures:

  LLVM :: DebugInfo/AArch64/frameindices.ll
  LLVM :: DebugInfo/X86/pieces-3.ll
  LLVM :: DebugInfo/invalid-overlap.ll
  LLVM :: Transforms/Inline/alloca-dbgdeclare.ll

Annoyingly, this doesn't fail verification on the IR originally produced by rustc, because it doesn't specify fragments, so it looks like the variables are exactly the same. Possibly the way to catch that would be to check whether the size implied by the DILocalVariable is consistent with the size of the alloca.

@nikic
Copy link
Contributor Author

nikic commented Aug 24, 2023

I've put up https://reviews.llvm.org/D158743 with a sanity check that the fragment can fit into the alloca, but still needs some test fixes.

This doesn't catch the case that actually asserts, but does catch the invalid debuginfo produced by SROA in Rust.

nikic added a commit that referenced this issue Aug 28, 2023
…size

Add a check that the DILocalVariable fragment size in dbg.declare
does not exceed the size of the alloca.

This would have caught the invalid debuginfo regenerated by rustc
in #64149.

Differential Revision: https://reviews.llvm.org/D158743
nikic added a commit that referenced this issue Sep 15, 2023
…ragment size

Reapply after fixing a clang bug this exposed in D158972 and
adjusting a number of tests that failed for 32-bit targets.

-----

Add a check that the DILocalVariable fragment size in dbg.declare
does not exceed the size of the alloca.

This would have caught the invalid debuginfo regenerated by rustc
in #64149.

Differential Revision: https://reviews.llvm.org/D158743
ZijunZhaoCCK pushed a commit to ZijunZhaoCCK/llvm-project that referenced this issue Sep 19, 2023
…ragment size

Reapply after fixing a clang bug this exposed in D158972 and
adjusting a number of tests that failed for 32-bit targets.

-----

Add a check that the DILocalVariable fragment size in dbg.declare
does not exceed the size of the alloca.

This would have caught the invalid debuginfo regenerated by rustc
in llvm#64149.

Differential Revision: https://reviews.llvm.org/D158743
nikic added a commit that referenced this issue Oct 9, 2023
…ragment size

Reapply now that generation of incorrect debuginfo for FnDef
in rustc has been fixed.

-----

Add a check that the DILocalVariable fragment size in dbg.declare
does not exceed the size of the alloca.

This would have caught the invalid debuginfo regenerated by rustc
in #64149.

Differential Revision: https://reviews.llvm.org/D158743
@tstellar
Copy link
Collaborator

Has this been fixed in main?

@nikic
Copy link
Contributor Author

nikic commented Oct 13, 2023

@tstellar The invalid debug info generated by rustc has been fixed, no changes on the LLVM side were necessary. I'm keeping this open to track work on additional verifier checks, which is still ongoing (about to be reverted due to #68929).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants