Closed alexcrichton closed 8 years ago
er I meant to indicate that this is fixed, so closing
It looks like this is now failed on LLVM trunk (yay!)
Minimally reduced: define void @main() !dbg !1 { call void @fails_if_inlined() ret void }
define void @fails_if_inlined() { unreachable, !dbg !3 }
!llvm.module.flags = !{#0}
!0 = !{i32 2, !"Debug Info Version", i32 3} !1 = distinct !DISubprogram(name: "main", file: !2, line: 1) !2 = !DIFile(filename: "foo.rs", directory: "bar") !3 = !DILocation(line: 1, scope: !4) !4 = distinct !DILexicalBlock(scope: !5, file: !2, line: 1, column: 1) !5 = distinct !DISubprogram(name: "drop", file: !2, line: 1)
I've updated the title to reflect that it doesn't only apply to MSVC any more and I've also managed to narrow down the IR a little bit further to hopefully be even more minimal! It does looks like something odd is going on with inlining yeah. If the call is manually inlined (e.g. !dbg !3 is added to the call to fails_if_inlined
) then the assert will happen as well.
declare i32 @personality(...)
declare void @foo()
declare void @bar(i64*)
define void @main() personality i32 (...)* @personality !dbg !1 {
%slot = alloca i64
invoke void @foo() to label %normal unwind label %bad
normal:
ret void
bad:
%cleanuppad = cleanuppad []
call void @fails_if_inlined(i64* %slot)
cleanupret %cleanuppad unwind to caller
}
define void @fails_if_inlined(i64) {
call void @bar(i64 %0), !dbg !3
ret void
}
!llvm.module.flags = !{#0}
!0 = !{i32 2, !"Debug Info Version", i32 3}
!1 = distinct !DISubprogram(name: "main", file: !2, line: 1)
!2 = !DIFile(filename: "foo.rs", directory: "bar")
!3 = !DILocation(line: 1, scope: !4)
!4 = distinct !DILexicalBlock(scope: !5, file: !2, line: 1, column: 1)
!5 = distinct !DISubprogram(name: "drop", file: !2, line: 1)
Cool, that repro looks like it doesn't have any funclet weirdness in it. If it fails at -O2, the inliner is probably doing something weird with your debug info here.
Hm, and through further tinkering it looks like this isn't MSVC-specific, if the target at the top is commented out it fails as well for Linux. Perhaps a red herring!
Ok, I managed to reduce this a little differently:
https://gist.github.com/alexcrichton/ce1a275616a0eefdab36
There's a comment on there as well, but opt test.ll
will pass but opt test.ll -O2
will fail (generating the same error as before). Hope that helps!
Hm although my reduction strategy involved literally deleting large swaths of basic blocks to be replaced with unreachable
instructions, so I may have actually deleted something a little too aggressively by accident. I could try a re-reduction though where I avoid deleting those instructions.
I'd be surprised if you accidentally reduced out the cleanupendpad. It's far more likely that the rust frontend never generated it, since Joseph added it to our model after the fact for C# finally blocks.
Ah unfortunately there's actually not really a great translation to C of what this is doing. I've been working to migrate the MSVC target of the Rust compiler to use the new SEH intrinsics for unwinding (in Rust we use it for panics), and this was one of the failures I saw in the test suite for our 64-bit target.
The original test case 1 is significantly more complicated and the IR is just reduced by me by hand, so I may have incorrectly deleted something or it could be legitimate that this is just bogus IR that should throw an error like this. If it's helpful I can also just upload a much larger version of the IR that files (e.g. the one that the Rust compiler actually generates). I basically used insertion of unreachable
as a way to strip out tons of unused variables and such.
Adding a cleanupendpad to the attached IR makes the verifier issue go away.
What is the attached IR trying to do? I'm assuming this is attempting to represent code like this:
try { try { bar(); } finally { bar(); } } finally { bar(); unreachable; }
The second call to 'bar' needs to unwind through a cleanupendpad before it unwinds to the second cleanuppad. This gives the backend the lexical scoping information it needs to generate tables. David Majnemer has plans to encode the scoping information in a different way, so cleanupendpad won't be around for too long.
Second, we have a limitation in our C specific handler table emission that makes it hard to lower this correctly: // Break out before we enter into a finally funclet. // FIXME: We need to emit separate EH tables for cleanups. MachineFunction::const_iterator End = MF->end(); MachineFunction::const_iterator Stop = std::next(MF->begin()); while (Stop != End && !Stop->isEHFuncletEntry()) ++Stop;
Aside from all that, I'll take a look at the debug info verifier issue here. :)
Extended Description
When compiling the attached IR with llc, I get:
!dbg attachment points at wrong subprogram for function !1 = distinct !DISubprogram(name: "main", linkageName: "_ZN19backtrace_debuginfo4mainE", scope: !3, file: !2, line: 1, type: !4, isLocal: true, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: true, templateParams: !5, variables: !5) void ()* @foo call void @bar(), !dbg !19 !19 = !DILocation(line: 1219, scope: !20, inlinedAt: !21) !25 = distinct !DILexicalBlock(scope: !26, file: !2, line: 1, column: 10) !26 = distinct !DISubprogram(name: "main", linkageName: "_ZN19backtrace_debuginfo4mainE", scope: !3, file: !2, line: 1, type: !4, isLocal: true, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: true, templateParams: !5, variables: !5) LLVM ERROR: Broken function found, compilation aborted!
But when the target is changed to x86_64-unknown-linux-gnu the file compiles successfully. I'm not 100% sure that we correctly emit debuginfo in the first place (not my area of expertise), and this may also be related to r252219 but I figured it was odd at least that it failed to compile with an MSVC target yet successfully compiled for a Linux one!