-
-
Notifications
You must be signed in to change notification settings - Fork 267
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
Comments
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. |
I'd check the IR for call instructions without location info, see #999. |
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 |
Seems to be injected here by the front-end: https://github.com/ldc-developers/ldc/blob/merge-2.067/dmd2/func.c#L4251 |
Well that may not be the real issue. Not
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. 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 Relevant metadata:
Inlined |
Yes, unless they also have an "inlined at" scope set. (Edit: Which doesn't look to be the case from your snippet). |
Tried to reproduce it manually without that magic /**************************************/
// 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 |
Hmm, these may be 2 separate issues. When defining a custom Here's the .ll for both 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) |
Any ideas guys? We should fix this before releasing 0.16... |
Did you check the IR for __xopCmp before inlining? |
Nope. :) |
Pretty sure you'll find the reason there. ;) |
Will check it after work. |
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. |
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 |
Oh man, thx, that led to 0705717. ;) |
runnable/opover2.d
fails if and only if using both-inline
and-g
(-O
doesn't matter), on both Win64 and Linux x64:That's the only dmd-testsuite issue with LLVM 3.7 on Linux x64 btw.
The text was updated successfully, but these errors were encountered: