Andrew Kelley via llvm-dev
2019-Jul-22 03:49 UTC
[llvm-dev] is there something obviously wrong with my llvm ir code? why is it generating a trap?
Here's the frontend code: var x: i32 = 1; export fn entry() void { const p = async simpleAsyncFn(2); assert(x == 3); resume p; assert(x == 5); // this is getting replace with a trap() } fn simpleAsyncFn(delta: i32) void { x += delta; suspend; x += delta; } Here's the generated LLVM IR: ; ModuleID = 'test' source_filename = "test" target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" %"[]u8" = type { i8*, i64 } %builtin.StackTrace = type { i64, %"[]usize" } %"[]usize" = type { i64*, i64 } %"@Frame(simpleAsyncFn)" = type { i64, i32 } @x = internal unnamed_addr global i32 1, align 4 @assert = internal unnamed_addr constant void (i1)* @std.debug.assert, align 8 ; Function Attrs: nobuiltin nounwind define internal fastcc void @std.debug.assert(i1) unnamed_addr #0 { Entry: %ok = alloca i1, align 1 store i1 %0, i1* %ok, align 1 %1 = load i1, i1* %ok, align 1 %2 = icmp eq i1 %1, false br i1 %2, label %Then, label %Else Then: ; preds = %Entry unreachable Else: ; preds = %Entry br label %EndIf EndIf: ; preds = %Else ret void } ; Function Attrs: nobuiltin nounwind define void @entry() #0 { Entry: %p = alloca %"@Frame(simpleAsyncFn)", align 8 %0 = getelementptr inbounds %"@Frame(simpleAsyncFn)", %"@Frame(simpleAsyncFn)"* %p, i32 0, i32 0 store i64 0, i64* %0 %1 = getelementptr inbounds %"@Frame(simpleAsyncFn)", %"@Frame(simpleAsyncFn)"* %p, i32 0, i32 1 store i32 2, i32* %1 %2 = call fastcc i64 @simpleAsyncFn(%"@Frame(simpleAsyncFn)"* %p) %3 = load i32, i32* @x, align 4 %4 = icmp eq i32 %3, 3 call fastcc void @std.debug.assert(i1 %4) %5 = call i64 @simpleAsyncFn(%"@Frame(simpleAsyncFn)"* %p) %6 = load i32, i32* @x, align 4 %7 = icmp eq i32 %6, 5 call fastcc void @std.debug.assert(i1 %7) ret void } ; Function Attrs: nobuiltin nounwind define internal fastcc i64 @simpleAsyncFn(%"@Frame(simpleAsyncFn)"* nonnull align 8) unnamed_addr #0 { AsyncSwitch: %1 = getelementptr inbounds %"@Frame(simpleAsyncFn)", %"@Frame(simpleAsyncFn)"* %0, i32 0, i32 1 %2 = getelementptr inbounds %"@Frame(simpleAsyncFn)", %"@Frame(simpleAsyncFn)"* %0, i32 0, i32 0 %3 = load i64, i64* %2 switch i64 %3, label %BadResume [ i64 0, label %Entry i64 1, label %GetSize i64 2, label %Resume ] Entry: ; preds = %AsyncSwitch %4 = load i32, i32* @x, align 4 %5 = load i32, i32* %1, align 4 %6 = add nsw i32 %4, %5 store i32 %6, i32* @x, align 4 %7 = getelementptr inbounds %"@Frame(simpleAsyncFn)", %"@Frame(simpleAsyncFn)"* %0, i32 0, i32 0 store i64 2, i64* %7 ret i64 undef Resume: ; preds = %AsyncSwitch %8 = load i32, i32* @x, align 4 %9 = load i32, i32* %1, align 4 %10 = add nsw i32 %8, %9 store i32 %10, i32* @x, align 4 ret i64 undef BadResume: ; preds = %AsyncSwitch unreachable GetSize: ; preds = %AsyncSwitch ret i64 16 } attributes #0 = { nobuiltin nounwind } attributes #1 = { nobuiltin noreturn nounwind } !llvm.module.flags = !{!0} !llvm.dbg.cu = !{!1} !0 = !{i32 2, !"Debug Info Version", i32 3} !1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: "zig 0.4.0", isOptimized: true, runtimeVersion: 0, emissionKind: NoDebug, enums: !3) !2 = !DIFile(filename: "test", directory: ".") !3 = !{} When I turn on optimizations, I get this (note the trap() at the end): define void @entry() local_unnamed_addr #0 { Entry: %p = alloca %"@Frame(simpleAsyncFn)", align 8 %0 = getelementptr inbounds %"@Frame(simpleAsyncFn)", %"@Frame(simpleAsyncFn)"* %p, i64 0, i32 0 store i64 0, i64* %0, align 8 %1 = getelementptr inbounds %"@Frame(simpleAsyncFn)", %"@Frame(simpleAsyncFn)"* %p, i64 0, i32 1 store i32 2, i32* %1, align 8 call fastcc void @simpleAsyncFn(%"@Frame(simpleAsyncFn)"* %p) tail call void @llvm.trap() unreachable } This seems to indicate that the optimizer was able to prove that my (second) assertion was incorrect. But I don't see how this is possible. Does anyone else see it? Thanks, Andrew -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 833 bytes Desc: OpenPGP digital signature URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20190721/f5b4271f/attachment.sig>
Andrew Kelley via llvm-dev
2019-Jul-22 04:05 UTC
[llvm-dev] is there something obviously wrong with my llvm ir code? why is it generating a trap?
I figured it out. Wrong calling convention at the callsite. %2 = call fastcc i64 @simpleAsyncFn(%"@Frame(simpleAsyncFn)"* %p) vs %5 = call i64 @simpleAsyncFn(%"@Frame(simpleAsyncFn)"* %p) Missing `fastcc` on the second one. On 7/21/19 11:49 PM, Andrew Kelley via llvm-dev wrote:> Here's the frontend code: > > var x: i32 = 1; > > export fn entry() void { > const p = async simpleAsyncFn(2); > assert(x == 3); > resume p; > assert(x == 5); // this is getting replace with a trap() > } > fn simpleAsyncFn(delta: i32) void { > x += delta; > suspend; > x += delta; > } > > > Here's the generated LLVM IR: > > ; ModuleID = 'test' > source_filename = "test" > target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" > target triple = "x86_64-unknown-linux-gnu" > > %"[]u8" = type { i8*, i64 } > %builtin.StackTrace = type { i64, %"[]usize" } > %"[]usize" = type { i64*, i64 } > %"@Frame(simpleAsyncFn)" = type { i64, i32 } > > @x = internal unnamed_addr global i32 1, align 4 > @assert = internal unnamed_addr constant void (i1)* @std.debug.assert, > align 8 > > ; Function Attrs: nobuiltin nounwind > define internal fastcc void @std.debug.assert(i1) unnamed_addr #0 { > Entry: > %ok = alloca i1, align 1 > store i1 %0, i1* %ok, align 1 > %1 = load i1, i1* %ok, align 1 > %2 = icmp eq i1 %1, false > br i1 %2, label %Then, label %Else > > Then: ; preds = %Entry > unreachable > > Else: ; preds = %Entry > br label %EndIf > > EndIf: ; preds = %Else > ret void > } > > ; Function Attrs: nobuiltin nounwind > define void @entry() #0 { > Entry: > %p = alloca %"@Frame(simpleAsyncFn)", align 8 > %0 = getelementptr inbounds %"@Frame(simpleAsyncFn)", > %"@Frame(simpleAsyncFn)"* %p, i32 0, i32 0 > store i64 0, i64* %0 > %1 = getelementptr inbounds %"@Frame(simpleAsyncFn)", > %"@Frame(simpleAsyncFn)"* %p, i32 0, i32 1 > store i32 2, i32* %1 > %2 = call fastcc i64 @simpleAsyncFn(%"@Frame(simpleAsyncFn)"* %p) > %3 = load i32, i32* @x, align 4 > %4 = icmp eq i32 %3, 3 > call fastcc void @std.debug.assert(i1 %4) > %5 = call i64 @simpleAsyncFn(%"@Frame(simpleAsyncFn)"* %p) > %6 = load i32, i32* @x, align 4 > %7 = icmp eq i32 %6, 5 > call fastcc void @std.debug.assert(i1 %7) > ret void > } > > ; Function Attrs: nobuiltin nounwind > define internal fastcc i64 @simpleAsyncFn(%"@Frame(simpleAsyncFn)"* > nonnull align 8) unnamed_addr #0 { > AsyncSwitch: > %1 = getelementptr inbounds %"@Frame(simpleAsyncFn)", > %"@Frame(simpleAsyncFn)"* %0, i32 0, i32 1 > %2 = getelementptr inbounds %"@Frame(simpleAsyncFn)", > %"@Frame(simpleAsyncFn)"* %0, i32 0, i32 0 > %3 = load i64, i64* %2 > switch i64 %3, label %BadResume [ > i64 0, label %Entry > i64 1, label %GetSize > i64 2, label %Resume > ] > > Entry: ; preds = %AsyncSwitch > %4 = load i32, i32* @x, align 4 > %5 = load i32, i32* %1, align 4 > %6 = add nsw i32 %4, %5 > store i32 %6, i32* @x, align 4 > %7 = getelementptr inbounds %"@Frame(simpleAsyncFn)", > %"@Frame(simpleAsyncFn)"* %0, i32 0, i32 0 > store i64 2, i64* %7 > ret i64 undef > > Resume: ; preds = %AsyncSwitch > %8 = load i32, i32* @x, align 4 > %9 = load i32, i32* %1, align 4 > %10 = add nsw i32 %8, %9 > store i32 %10, i32* @x, align 4 > ret i64 undef > > BadResume: ; preds = %AsyncSwitch > unreachable > > GetSize: ; preds = %AsyncSwitch > ret i64 16 > } > > attributes #0 = { nobuiltin nounwind } > attributes #1 = { nobuiltin noreturn nounwind } > > !llvm.module.flags = !{!0} > !llvm.dbg.cu = !{!1} > > !0 = !{i32 2, !"Debug Info Version", i32 3} > !1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: > "zig 0.4.0", isOptimized: true, runtimeVersion: 0, emissionKind: > NoDebug, enums: !3) > !2 = !DIFile(filename: "test", directory: ".") > !3 = !{} > > > When I turn on optimizations, I get this (note the trap() at the end): > > > define void @entry() local_unnamed_addr #0 { > Entry: > %p = alloca %"@Frame(simpleAsyncFn)", align 8 > %0 = getelementptr inbounds %"@Frame(simpleAsyncFn)", > %"@Frame(simpleAsyncFn)"* %p, i64 0, i32 0 > store i64 0, i64* %0, align 8 > %1 = getelementptr inbounds %"@Frame(simpleAsyncFn)", > %"@Frame(simpleAsyncFn)"* %p, i64 0, i32 1 > store i32 2, i32* %1, align 8 > call fastcc void @simpleAsyncFn(%"@Frame(simpleAsyncFn)"* %p) > tail call void @llvm.trap() > unreachable > } > > > This seems to indicate that the optimizer was able to prove that my > (second) assertion was incorrect. But I don't see how this is possible. > Does anyone else see it? > > Thanks, > Andrew > > > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >-------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 833 bytes Desc: OpenPGP digital signature URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20190722/53caa53b/attachment.sig>