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>