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

LLVM 3.7: dmd-testsuite-debug ICE wrt. debug infos and inlined functions #1032

Closed
kinke opened this issue Aug 20, 2015 · 17 comments
Closed

LLVM 3.7: dmd-testsuite-debug ICE wrt. debug infos and inlined functions #1032

kinke opened this issue Aug 20, 2015 · 17 comments

Comments

@kinke
Copy link
Member

kinke commented Aug 20, 2015

runnable/opover2.d fails if and only if using both -inline and -g (-O doesn't matter), on both Win64 and Linux x64:

ldc2: /home/martin/llvm/lib/CodeGen/LexicalScopes.cpp:160: llvm::LexicalScope* llvm::LexicalScopes::getOrCreateRegularScope(const llvm::DILocalScope*): Assertion `cast<DISubprogram>(Scope)->describes(MF->getFunction())' failed.
#0 0x204293a llvm::sys::PrintStackTrace(llvm::raw_ostream&) /home/martin/llvm/lib/Support/Unix/Signals.inc:437:0
#1 0x2042c4f PrintStackTraceSignalHandler(void*) /home/martin/llvm/lib/Support/Unix/Signals.inc:495:0
#2 0x204181a SignalHandler(int) /home/martin/llvm/lib/Support/Unix/Signals.inc:210:0
#3 0x7fdad5a00340 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x10340)
#4 0x7fdad4a24cc9 gsignal /build/buildd/eglibc-2.19/signal/../nptl/sysdeps/unix/sysv/linux/raise.c:56:0
#5 0x7fdad4a280d8 abort /build/buildd/eglibc-2.19/stdlib/abort.c:91:0
#6 0x7fdad4a1db86 __assert_fail_base /build/buildd/eglibc-2.19/assert/assert.c:92:0
#7 0x7fdad4a1dc32 (/lib/x86_64-linux-gnu/libc.so.6+0x2fc32)
#8 0x1549cd3 llvm::LexicalScopes::getOrCreateRegularScope(llvm::DILocalScope const*) /home/martin/llvm/lib/CodeGen/LexicalScopes.cpp:161:0
#9 0x1549afc llvm::LexicalScopes::getOrCreateLexicalScope(llvm::DILocalScope const*, llvm::DILocation const*) /home/martin/llvm/lib/CodeGen/LexicalScopes.cpp:138:0
#10 0x154acc2 llvm::LexicalScopes::getOrCreateLexicalScope(llvm::DILocation const*) /home/martin/llvm/include/llvm/CodeGen/LexicalScopes.h:209:0
#11 0x154994a llvm::LexicalScopes::extractLexicalScopes(llvm::SmallVectorImpl<std::pair<llvm::MachineInstr const*, llvm::MachineInstr const*> >&, llvm::DenseMap<llvm::MachineInstr const*, llvm::LexicalScope*, llvm::DenseMapInfo<llvm::MachineInstr const*>, llvm::detail::DenseMapPair<llvm::MachineInstr const*, llvm::LexicalScope*> >&) /home/martin/llvm/lib/CodeGen/LexicalScopes.cpp:102:0
#12 0x1549699 llvm::LexicalScopes::initialize(llvm::MachineFunction const&) /home/martin/llvm/lib/CodeGen/LexicalScopes.cpp:46:0
#13 0x148854a llvm::DwarfDebug::beginFunction(llvm::MachineFunction const*) /home/martin/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp:1089:0
#14 0x146779b llvm::AsmPrinter::EmitFunctionHeader() /home/martin/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp:579:0
#15 0x14688ed llvm::AsmPrinter::EmitFunctionBody() /home/martin/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp:788:0
#16 0x10b372e llvm::X86AsmPrinter::runOnMachineFunction(llvm::MachineFunction&) /home/martin/llvm/lib/Target/X86/X86AsmPrinter.cpp:70:0
#17 0x15815fb llvm::MachineFunctionPass::runOnFunction(llvm::Function&) /home/martin/llvm/lib/CodeGen/MachineFunctionPass.cpp:41:0
#18 0x1f47ad2 llvm::FPPassManager::runOnFunction(llvm::Function&) /home/martin/llvm/lib/IR/LegacyPassManager.cpp:1520:0
#19 0x1f47c4f llvm::FPPassManager::runOnModule(llvm::Module&) /home/martin/llvm/lib/IR/LegacyPassManager.cpp:1540:0
#20 0x1f47fa5 (anonymous namespace)::MPPassManager::runOnModule(llvm::Module&) /home/martin/llvm/lib/IR/LegacyPassManager.cpp:1596:0
#21 0x1f48669 llvm::legacy::PassManagerImpl::run(llvm::Module&) /home/martin/llvm/lib/IR/LegacyPassManager.cpp:1698:0
#22 0x1f488a3 llvm::legacy::PassManager::run(llvm::Module&) /home/martin/llvm/lib/IR/LegacyPassManager.cpp:1730:0
#23 0xc617b6 codegenModule(llvm::TargetMachine&, llvm::Module&, llvm::raw_fd_ostream&, llvm::TargetMachine::CodeGenFileType) /home/martin/ldc/driver/toobj.cpp:130:0
#24 0xc6333e writeModule(llvm::Module*, std::string) /home/martin/ldc/driver/toobj.cpp:568:0
#25 0xc5a19c ldc::CodeGenerator::writeAndFreeLLModule(char const*) /home/martin/ldc/driver/codegenerator.cpp:171:0
#26 0xc59fbb ldc::CodeGenerator::finishLLModule(Module*) /home/martin/ldc/driver/codegenerator.cpp:144:0
#27 0xc5a455 ldc::CodeGenerator::emit(Module*) /home/martin/ldc/driver/codegenerator.cpp:217:0
#28 0xc6e87f main /home/martin/ldc/driver/main.cpp:1298:0
#29 0x7fdad4a0fec5 __libc_start_main /build/buildd/eglibc-2.19/csu/libc-start.c:321:0
#30 0xc43df9 _start (/home/martin/build-ldc/bin/ldc2+0xc43df9)

That's the only dmd-testsuite issue with LLVM 3.7 on Linux x64 btw.

@dnadlinger
Copy link
Member

This might be a bug in the inliner, similar to the one I recently worked around on our side. The assert is triggered because something has a debug location attached that does not point into the currently emitted function.

@dnadlinger
Copy link
Member

#998

@dnadlinger
Copy link
Member

I'd check the IR for call instructions without location info, see #999.

@kinke
Copy link
Member Author

kinke commented Aug 20, 2015

Thanks for the hint! The reduced test:

/**************************************/
// 10567

// requires thunk
struct S10567y1n { int value; int opCmp(const S10567y1n rhs) const { return 0; } }
//struct S10567y1t { int value; int opCmp(S)(const S rhs) const { return 0; } }

int main()
{
    return 0;
}

yields:

define i32 @_D7opover29S10567y1n5opCmpMxFxS7opover29S10567y1nZi(%opover2.S10567y1n* %.this_arg, %opover2.S10567y1n %rhs_arg) #0 {
  %rhs = alloca %opover2.S10567y1n, align 4       ; [#uses = 1 type = %opover2.S10567y1n*]
  call void @llvm.dbg.declare(metadata %opover2.S10567y1n* %.this_arg, metadata !12, metadata !32), !dbg !33 ; [debug line = 13:35] [debug variable = this]
  store %opover2.S10567y1n %rhs_arg, %opover2.S10567y1n* %rhs, !dbg !33 ; [debug line = 13:35]
  call void @llvm.dbg.declare(metadata %opover2.S10567y1n* %rhs, metadata !16, metadata !32), !dbg !33 ; [debug line = 13:35] [debug variable = rhs]
  %1 = icmp ne %opover2.S10567y1n* %.this_arg, null, !dbg !34 ; [#uses = 1 type = i1] [debug line = 0:0]
  br i1 %1, label %assertPassed, label %assertFailed

assertPassed:                                     ; preds = %0
  ret i32 0, !dbg !35                             ; [debug line = 13:70]

assertFailed:                                     ; preds = %0
  call void @_d_assert_msg({ i64, i8* } { i64 9, i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str, i32 0, i32 0) }, { i64, i8* } { i64 48, i8* getelementptr inbounds ([49 x i8], [49 x i8]* @.str.1, i32 0, i32 0) }, i32 0), !dbg !34 ; [debug line = 0:0]
  unreachable, !dbg !34                           ; [debug line = 0:0]
}

So the icmp instruction, the call to _d_assert_msg and the unreachable all share !dbg !34 and debug line = 0:0. That seems to be caused by an implicit assert(&this !is null) when entering the function, apparently without loc info.

@kinke
Copy link
Member Author

kinke commented Aug 20, 2015

Seems to be injected here by the front-end: https://github.com/ldc-developers/ldc/blob/merge-2.067/dmd2/func.c#L4251

@kinke
Copy link
Member Author

kinke commented Aug 20, 2015

Well that may not be the real issue. Not opCmp() is the interesting one, it's __xopCmp(), which calls opCmp (after dereferencing the rhs pointer), and that call gets inlined:

-inline -g:

define i32 @_D7opover29S10567y1n8__xopCmpFKxS7opover29S10567y1nKxS7opover29S10567y1nZi(%opover2.S10567y1n* %q_arg, %opover2.S10567y1n* %p_arg) #0 {
  %rhs.i = alloca %opover2.S10567y1n, align 4     ; [#uses = 3 type = %opover2.S10567y1n*]
  %1 = load %opover2.S10567y1n, %opover2.S10567y1n* %p_arg ; [#uses = 1 type = %opover2.S10567y1n = type { i32 }]
  %2 = bitcast %opover2.S10567y1n* %rhs.i to i8*, !dbg !32 ; [#uses = 1 type = i8*] [debug line = 5:35]
  call void @llvm.lifetime.start(i64 4, i8* %2), !dbg !32 ; [debug line = 5:35]
  call void @llvm.dbg.declare(metadata %opover2.S10567y1n* %q_arg, metadata !11, metadata !31), !dbg !32 ; [debug line = 5:35] [debug variable = this]
  store %opover2.S10567y1n %1, %opover2.S10567y1n* %rhs.i, !dbg !32 ; [debug line = 5:35]
  %3 = icmp ne %opover2.S10567y1n* %q_arg, null, !dbg !33 ; [#uses = 1 type = i1] [debug line = 0:0]
  br i1 %3, label %_D7opover29S10567y1n5opCmpMxFxS7opover29S10567y1nZi.exit, label %assertFailed.i

assertFailed.i:                                   ; preds = %0
  call void @_d_assert_msg({ i64, i8* } { i64 9, i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str, i32 0, i32 0) }, { i64, i8* } { i64 48, i8* getelementptr inbounds ([49 x i8], [49 x i8]* @.str.1, i32 0, i32 0) }, i32 0), !dbg !33 ; [debug line = 0:0]
  unreachable, !dbg !33                           ; [debug line = 0:0]

_D7opover29S10567y1n5opCmpMxFxS7opover29S10567y1nZi.exit: ; preds = %0
  %4 = bitcast %opover2.S10567y1n* %rhs.i to i8*, !dbg !34 ; [#uses = 1 type = i8*] [debug line = 5:70]
  call void @llvm.lifetime.end(i64 4, i8* %4), !dbg !34 ; [debug line = 5:70]
  call void @llvm.dbg.declare(metadata %opover2.S10567y1n* %rhs.i, metadata !15, metadata !31), !dbg !32 ; [debug line = 5:35] [debug variable = rhs]
  ret i32 0
}

vs. -g:

define i32 @_D7opover29S10567y1n8__xopCmpFKxS7opover29S10567y1nKxS7opover29S10567y1nZi(%opover2.S10567y1n* %q_arg, %opover2.S10567y1n* %p_arg) #0 {
  %1 = load %opover2.S10567y1n, %opover2.S10567y1n* %p_arg ; [#uses = 1 type = %opover2.S10567y1n = type { i32 }]
  %2 = call i32 @_D7opover29S10567y1n5opCmpMxFxS7opover29S10567y1nZi(%opover2.S10567y1n* %q_arg, %opover2.S10567y1n %1) ; [#uses = 1 type = i32] [display name = opover2.S10567y1n.opCmp]
  ret i32 %2
}

So llvm.dbg.declare for rhs.i is called after its llvm.lifetime.start and llvm.lifetime.end calls! Doesn't seem exactly right.

Relevant metadata:

!2 = !{}
!6 = !DISubprogram(name: "opover2.S10567y1n.opCmp", linkageName: "_D7opover29S10567y1n5opCmpMxFxS7opover29S10567y1nZi", scope: null, file: !4, line: 5, type: !7, isLocal: false, isDefinition: true, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: false, function: i32 (%opover2.S10567y1n*, %opover2.S10567y1n)* @_D7opover29S10567y1n5opCmpMxFxS7opover29S10567y1nZi, variables: !10)
!10 = !{!11, !15}
!11 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "this", arg: 0, scope: !6, file: !4, line: 5, type: !12, flags: DIFlagArtificial | DIFlagObjectPointer)
!15 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "rhs", arg: 0, scope: !6, file: !4, line: 5, type: !12)
!16 = !DISubprogram(name: "opover2.S10567y1n.__xopCmp", linkageName: "_D7opover29S10567y1n8__xopCmpFKxS7opover29S10567y1nKxS7opover29S10567y1nZi", scope: null, file: !17, type: !7, isLocal: false, isDefinition: true, flags: DIFlagPrototyped, isOptimized: false, function: i32 (%opover2.S10567y1n*, %opover2.S10567y1n*)* @_D7opover29S10567y1n8__xopCmpFKxS7opover29S10567y1nKxS7opover29S10567y1nZi, variables: !2)
!31 = !DIExpression()

Inlined !11 and !15 still have the original scope (opCmp), not sure if that's not a problem.

@dnadlinger
Copy link
Member

Inlined !11 and !15 still have the original scope (opCmp), not sure if that's not a problem.

Yes, unless they also have an "inlined at" scope set. (Edit: Which doesn't look to be the case from your snippet).

@kinke
Copy link
Member Author

kinke commented Aug 20, 2015

Tried to reproduce it manually without that magic __xopCmp, but failed (i.e., this works):

/**************************************/
// 10567

// requires thunk
struct S10567y1n { int value; int cmp(const S10567y1n rhs) const { return 0; } int __cmp(ref const S10567y1n rhs) const { return cmp(rhs); } }
//struct S10567y1t { int value; int opCmp(S)(const S rhs) const { return 0; } }

int main()
{
    return 0;
}
define i32 @_D7opover29S10567y1n5__cmpMxFKxS7opover29S10567y1nZi(%opover2.S10567y1n* %.this_arg, %opover2.S10567y1n* %rhs_arg) #0 {
  %rhs.i = alloca %opover2.S10567y1n, align 4     ; [#uses = 3 type = %opover2.S10567y1n*]
  call void @llvm.dbg.declare(metadata %opover2.S10567y1n* %.this_arg, metadata !18, metadata !33), !dbg !37 ; [debug line = 5:84] [debug variable = this]
  %1 = icmp ne %opover2.S10567y1n* %.this_arg, null, !dbg !38 ; [#uses = 1 type = i1] [debug line = 0:0]
  call void @llvm.dbg.declare(metadata %opover2.S10567y1n* %rhs.i, metadata !15, metadata !33), !dbg !39 ; [debug line = 5:35@5:123] [debug variable = rhs]
  br i1 %1, label %assertPassed, label %assertFailed

assertPassed:                                     ; preds = %0
  %2 = load %opover2.S10567y1n, %opover2.S10567y1n* %rhs_arg, !dbg !41 ; [#uses = 1 type = %opover2.S10567y1n = type { i32 }] [debug line = 5:123]
  %3 = bitcast %opover2.S10567y1n* %rhs.i to i8*, !dbg !39 ; [#uses = 1 type = i8*] [debug line = 5:35@5:123]
  call void @llvm.lifetime.start(i64 4, i8* %3), !dbg !39 ; [debug line = 5:35@5:123]
  call void @llvm.dbg.declare(metadata %opover2.S10567y1n* %.this_arg, metadata !11, metadata !33), !dbg !39 ; [debug line = 5:35@5:123] [debug variable = this]
  store %opover2.S10567y1n %2, %opover2.S10567y1n* %rhs.i, !dbg !39 ; [debug line = 5:35@5:123]
  %4 = icmp ne %opover2.S10567y1n* %.this_arg, null, !dbg !42 ; [#uses = 1 type = i1] [debug line = 0:0@5:123]
  br i1 %4, label %_D7opover29S10567y1n3cmpMxFxS7opover29S10567y1nZi.exit, label %assertFailed.i, !dbg !41 ; [debug line = 5:123]

assertFailed.i:                                   ; preds = %assertPassed
  call void @_d_assert_msg({ i64, i8* } { i64 9, i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str, i32 0, i32 0) }, { i64, i8* } { i64 48, i8* getelementptr inbounds ([49 x i8], [49 x i8]* @.str.1, i32 0, i32 0) }, i32 0), !dbg !42 ; [debug line = 0:0@5:123]
  unreachable, !dbg !42                           ; [debug line = 0:0@5:123]

_D7opover29S10567y1n3cmpMxFxS7opover29S10567y1nZi.exit: ; preds = %assertPassed
  %5 = bitcast %opover2.S10567y1n* %rhs.i to i8*, !dbg !43 ; [#uses = 1 type = i8*] [debug line = 5:68@5:123]
  call void @llvm.lifetime.end(i64 4, i8* %5), !dbg !43 ; [debug line = 5:68@5:123]
  ret i32 0, !dbg !41                             ; [debug line = 5:123]

assertFailed:                                     ; preds = %0
  call void @_d_assert_msg({ i64, i8* } { i64 9, i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str, i32 0, i32 0) }, { i64, i8* } { i64 48, i8* getelementptr inbounds ([49 x i8], [49 x i8]* @.str.1, i32 0, i32 0) }, i32 0), !dbg !38 ; [debug line = 0:0]
  unreachable, !dbg !38                           ; [debug line = 0:0]
}

!6 = !DISubprogram(name: "opover2.S10567y1n.cmp", linkageName: "_D7opover29S10567y1n3cmpMxFxS7opover29S10567y1nZi", scope: null, file: !4, line: 5, type: !7, isLocal: false, isDefinition: true, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: false, function: i32 (%opover2.S10567y1n*, %opover2.S10567y1n)* @_D7opover29S10567y1n3cmpMxFxS7opover29S10567y1nZi, variables: !10)
!11 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "this", arg: 0, scope: !6, file: !4, line: 5, type: !12, flags: DIFlagArtificial | DIFlagObjectPointer)
!15 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "rhs", arg: 0, scope: !6, file: !4, line: 5, type: !12)
!16 = !DISubprogram(name: "opover2.S10567y1n.__cmp", linkageName: "_D7opover29S10567y1n5__cmpMxFKxS7opover29S10567y1nZi", scope: null, file: !4, line: 5, type: !7, isLocal: false, isDefinition: true, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: false, function: i32 (%opover2.S10567y1n*, %opover2.S10567y1n*)* @_D7opover29S10567y1n5__cmpMxFKxS7opover29S10567y1nZi, variables: !17)
!18 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "this", arg: 0, scope: !16, file: !4, line: 5, type: !12, flags: DIFlagArtificial | DIFlagObjectPointer)

Apart from an additional outer assert on this (!18 from __cmp, i.e., harmless), it llvm.dbg.declares the rhs variable right at the beginning, even before llvm.lifetime.start, and not right after llvm.lifetime.end as __xopCmp. That seems to be the only real difference. The scope of both !11 (this) and !15 (rhs) is cmp, no issue there, so that inlined-at seems to be correct. Yeah well by looking more closely, the [debug line = ...@...] hints at proper inlining for __cmp(), so that the debug declaration most likely relates to this. What makes that __xopCmp() function that special?!

@kinke
Copy link
Member Author

kinke commented Aug 21, 2015

Hmm, these may be 2 separate issues. When defining a custom __cmp() function analog to the special __xopCmp() generated by the front-end in buildXopCmp() in dmd2/clone.c, i.e., as static class function and not as method as in my previous post, the debug declaration also directly follows the llvm.lifetime.end instruction, analog to __xopCmp(). But the opCmp() call is still inlined correctly and so compiles fine.

Here's the .ll for both __cmp() and __xopCmp() for:

struct Struct
{
    int value;
    //int cmp(const Struct rhs) const { return 0; }
    int opCmp(const Struct rhs) const { return 0; }
    static int __cmp(ref const Struct p, ref const Struct q) { return q.opCmp(p); }
}

int main() { return 0; }
define i32 @_D7opover26Struct5__cmpFKxS7opover26StructKxS7opover26StructZi(%opover2.Struct* %q_arg, %opover2.Struct* %p_arg) #0 {
  %rhs.i = alloca %opover2.Struct, align 4        ; [#uses = 3 type = %opover2.Struct*]
  %1 = load %opover2.Struct, %opover2.Struct* %p_arg, !dbg !36 ; [#uses = 1 type = %opover2.Struct = type { i32 }] [debug line = 6:64]
  %2 = bitcast %opover2.Struct* %rhs.i to i8*, !dbg !37 ; [#uses = 1 type = i8*] [debug line = 5:9@6:64]
  call void @llvm.lifetime.start(i64 4, i8* %2), !dbg !37 ; [debug line = 5:9@6:64]
  call void @llvm.dbg.declare(metadata %opover2.Struct* %q_arg, metadata !11, metadata !32), !dbg !37 ; [debug line = 5:9@6:64] [debug variable = this]
  store %opover2.Struct %1, %opover2.Struct* %rhs.i, !dbg !37 ; [debug line = 5:9@6:64]
  %3 = icmp ne %opover2.Struct* %q_arg, null, !dbg !39 ; [#uses = 1 type = i1] [debug line = 0:0@6:64]
  br i1 %3, label %_D7opover26Struct5opCmpMxFxS7opover26StructZi.exit, label %assertFailed.i, !dbg !36 ; [debug line = 6:64]

assertFailed.i:                                   ; preds = %0
  call void @_d_assert_msg({ i64, i8* } { i64 9, i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str, i32 0, i32 0) }, { i64, i8* } { i64 48, i8* getelementptr inbounds ([49 x i8], [49 x i8]* @.str.1, i32 0, i32 0) }, i32 0), !dbg !39 ; [debug line = 0:0@6:64]
  unreachable, !dbg !39                           ; [debug line = 0:0@6:64]

_D7opover26Struct5opCmpMxFxS7opover26StructZi.exit: ; preds = %0
  %4 = bitcast %opover2.Struct* %rhs.i to i8*, !dbg !40 ; [#uses = 1 type = i8*] [debug line = 5:41@6:64]
  call void @llvm.lifetime.end(i64 4, i8* %4), !dbg !40 ; [debug line = 5:41@6:64]
  call void @llvm.dbg.declare(metadata %opover2.Struct* %rhs.i, metadata !15, metadata !32), !dbg !37 ; [debug line = 5:9@6:64] [debug variable = rhs]
  ret i32 0, !dbg !36                             ; [debug line = 6:64]
}

!36 = !DILocation(line: 6, column: 64, scope: !16)
!37 = !DILocation(line: 5, column: 9, scope: !6, inlinedAt: !38)
!38 = distinct !DILocation(line: 6, column: 64, scope: !16)
!39 = !DILocation(line: 0, scope: !6, inlinedAt: !38)
!40 = !DILocation(line: 5, column: 41, scope: !6, inlinedAt: !38)

vs.

define i32 @_D7opover26Struct8__xopCmpFKxS7opover26StructKxS7opover26StructZi(%opover2.Struct* %q_arg, %opover2.Struct* %p_arg) #0 {
  %rhs.i = alloca %opover2.Struct, align 4        ; [#uses = 3 type = %opover2.Struct*]
  %1 = load %opover2.Struct, %opover2.Struct* %p_arg ; [#uses = 1 type = %opover2.Struct = type { i32 }]
  %2 = bitcast %opover2.Struct* %rhs.i to i8*, !dbg !33 ; [#uses = 1 type = i8*] [debug line = 5:9]
  call void @llvm.lifetime.start(i64 4, i8* %2), !dbg !33 ; [debug line = 5:9]
  call void @llvm.dbg.declare(metadata %opover2.Struct* %q_arg, metadata !11, metadata !32), !dbg !33 ; [debug line = 5:9] [debug variable = this]
  store %opover2.Struct %1, %opover2.Struct* %rhs.i, !dbg !33 ; [debug line = 5:9]
  %3 = icmp ne %opover2.Struct* %q_arg, null, !dbg !34 ; [#uses = 1 type = i1] [debug line = 0:0]
  br i1 %3, label %_D7opover26Struct5opCmpMxFxS7opover26StructZi.exit, label %assertFailed.i

assertFailed.i:                                   ; preds = %0
  call void @_d_assert_msg({ i64, i8* } { i64 9, i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str, i32 0, i32 0) }, { i64, i8* } { i64 48, i8* getelementptr inbounds ([49 x i8], [49 x i8]* @.str.1, i32 0, i32 0) }, i32 0), !dbg !34 ; [debug line = 0:0]
  unreachable, !dbg !34                           ; [debug line = 0:0]

_D7opover26Struct5opCmpMxFxS7opover26StructZi.exit: ; preds = %0
  %4 = bitcast %opover2.Struct* %rhs.i to i8*, !dbg !35 ; [#uses = 1 type = i8*] [debug line = 5:41]
  call void @llvm.lifetime.end(i64 4, i8* %4), !dbg !35 ; [debug line = 5:41]
  call void @llvm.dbg.declare(metadata %opover2.Struct* %rhs.i, metadata !15, metadata !32), !dbg !33 ; [debug line = 5:9] [debug variable = rhs]
  ret i32 0
}

!33 = !DILocation(line: 5, column: 9, scope: !6)
!34 = !DILocation(line: 0, scope: !6)
!35 = !DILocation(line: 5, column: 41, scope: !6)

@kinke
Copy link
Member Author

kinke commented Sep 24, 2015

Any ideas guys? We should fix this before releasing 0.16...

@dnadlinger
Copy link
Member

Did you check the IR for __xopCmp before inlining?

@kinke
Copy link
Member Author

kinke commented Sep 24, 2015

Nope. :)

@dnadlinger
Copy link
Member

Pretty sure you'll find the reason there. ;)

@kinke
Copy link
Member Author

kinke commented Sep 24, 2015

Will check it after work.
Any ideas why Travis doesn't fail? See #1103 (comment).

@kinke
Copy link
Member Author

kinke commented Sep 25, 2015

Okay, so that's the diff when not inlining:

define i32 @_D6opover6Struct5__cmpFKxS6opover6StructKxS6opover6StructZi(%opover.Struct* %q_arg, %opover.Struct* %p_arg) #0 {
  %1 = load %opover.Struct, %opover.Struct* %p_arg, !dbg !40 ; [#uses = 1] [debug line = 6:64]
  %2 = call i32 @_D6opover6Struct5opCmpMxFxS6opover6StructZi(%opover.Struct* %q_arg, %opover.Struct %1), !dbg !40 ; [#uses = 1] [debug line = 6:64] [display name = opover.Struct.opCmp]
  ret i32 %2, !dbg !40                            ; [debug line = 6:64]
}

define i32 @_D6opover6Struct8__xopCmpFKxS6opover6StructKxS6opover6StructZi(%opover.Struct* %q_arg, %opover.Struct* %p_arg) #0 {
  %1 = load %opover.Struct, %opover.Struct* %p_arg ; [#uses = 1]
  %2 = call i32 @_D6opover6Struct5opCmpMxFxS6opover6StructZi(%opover.Struct* %q_arg, %opover.Struct %1) ; [#uses = 1] [display name = opover.Struct.opCmp]
  ret i32 %2
}

So no debug infos at all for the special __xopCmp function generated by the front-end. So it seems as if that special function takes a different route through the glue layer. Afaik, the only reference to it is in the struct's TypeInfo.

@dnadlinger
Copy link
Member

So here is the point where I realize that we must have been talking past each other, because I mentioned that as a possible source of the problem back in #1032 (comment). ;)

The functions do not take a special route through the backend (well, only in the sense that visit(StructDeclaration*) explicitly recurses into them), but DMD does not emit any location info for the statements. We might need to detect this sort of missing location info in EmitDwarfStopPoint and e.g. fall back to the function start.

@kinke
Copy link
Member Author

kinke commented Sep 25, 2015

Oh man, thx, that led to 0705717. ;)

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

No branches or pull requests

2 participants