61 changes: 28 additions & 33 deletions llvm/test/Transforms/Coroutines/coro-frame-reuse-alloca-02.ll
Original file line number Diff line number Diff line change
@@ -1,79 +1,74 @@
; Tests that variables of different type in a Corotuine whose lifetime range is not overlapping each other
; re-use the same slot in Coroutine frame.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split<reuse-storage>),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split<reuse-storage>),simplifycfg,early-cse' -S | FileCheck %s
%"struct.task::promise_type" = type { i8 }
%struct.awaitable = type { i8 }
%struct.big_structure = type { [500 x i8] }
%struct.big_structure.2 = type { [300 x i8] }
declare i8* @malloc(i64)
declare void @consume(%struct.big_structure*)
declare void @consume.2(%struct.big_structure.2*)
declare ptr @malloc(i64)
declare void @consume(ptr)
declare void @consume.2(ptr)
define void @a(i1 zeroext %cond) presplitcoroutine {
entry:
%__promise = alloca %"struct.task::promise_type", align 1
%a = alloca %struct.big_structure, align 1
%ref.tmp7 = alloca %struct.awaitable, align 1
%b = alloca %struct.big_structure.2, align 1
%ref.tmp18 = alloca %struct.awaitable, align 1
%0 = getelementptr inbounds %"struct.task::promise_type", %"struct.task::promise_type"* %__promise, i64 0, i32 0
%1 = call token @llvm.coro.id(i32 16, i8* nonnull %0, i8* bitcast (void (i1)* @a to i8*), i8* null)
%0 = call token @llvm.coro.id(i32 16, ptr nonnull %__promise, ptr @a, ptr null)
br label %init.ready
init.ready:
%2 = call noalias nonnull i8* @llvm.coro.begin(token %1, i8* null)
call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %0)
%1 = call noalias nonnull ptr @llvm.coro.begin(token %0, ptr null)
call void @llvm.lifetime.start.p0(i64 1, ptr nonnull %__promise)
br i1 %cond, label %if.then, label %if.else
if.then:
%3 = getelementptr inbounds %struct.big_structure, %struct.big_structure* %a, i64 0, i32 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 500, i8* nonnull %3)
call void @consume(%struct.big_structure* nonnull %a)
%save = call token @llvm.coro.save(i8* null)
call void @llvm.lifetime.start.p0(i64 500, ptr nonnull %a)
call void @consume(ptr nonnull %a)
%save = call token @llvm.coro.save(ptr null)
%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
switch i8 %suspend, label %coro.ret [
i8 0, label %await.ready
i8 1, label %cleanup1
]
await.ready:
call void @llvm.lifetime.end.p0i8(i64 500, i8* nonnull %3)
call void @llvm.lifetime.end.p0(i64 500, ptr nonnull %a)
br label %cleanup1
if.else:
%4 = getelementptr inbounds %struct.big_structure.2, %struct.big_structure.2* %b, i64 0, i32 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 300, i8* nonnull %4)
call void @consume.2(%struct.big_structure.2* nonnull %b)
%save2 = call token @llvm.coro.save(i8* null)
call void @llvm.lifetime.start.p0(i64 300, ptr nonnull %b)
call void @consume.2(ptr nonnull %b)
%save2 = call token @llvm.coro.save(ptr null)
%suspend2 = call i8 @llvm.coro.suspend(token %save2, i1 false)
switch i8 %suspend2, label %coro.ret [
i8 0, label %await2.ready
i8 1, label %cleanup2
]
await2.ready:
call void @llvm.lifetime.end.p0i8(i64 300, i8* nonnull %4)
call void @llvm.lifetime.end.p0(i64 300, ptr nonnull %b)
br label %cleanup2
cleanup1:
call void @llvm.lifetime.end.p0i8(i64 500, i8* nonnull %3)
call void @llvm.lifetime.end.p0(i64 500, ptr nonnull %a)
br label %cleanup
cleanup2:
call void @llvm.lifetime.end.p0i8(i64 300, i8* nonnull %4)
call void @llvm.lifetime.end.p0(i64 300, ptr nonnull %b)
br label %cleanup
cleanup:
call i8* @llvm.coro.free(token %1, i8* %2)
call ptr @llvm.coro.free(token %0, ptr %1)
br label %coro.ret
coro.ret:
call i1 @llvm.coro.end(i8* null, i1 false)
call i1 @llvm.coro.end(ptr null, i1 false)
ret void
}
; CHECK: %a.Frame = type { void (%a.Frame*)*, void (%a.Frame*)*, %"struct.task::promise_type", %struct.big_structure, i1 }
; CHECK: %a.Frame = type { ptr, ptr, %"struct.task::promise_type", %struct.big_structure, i1 }
; CHECK-LABEL: @a.resume(
; CHECK: %[[A:.*]] = getelementptr inbounds %a.Frame, %a.Frame* %FramePtr, i32 0, i32 3
; CHECK: %{{.*}} = bitcast %struct.big_structure* %[[A]] to %struct.big_structure.2*

declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*)
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr)
declare i1 @llvm.coro.alloc(token) #3
declare i64 @llvm.coro.size.i64() #5
declare i8* @llvm.coro.begin(token, i8* writeonly) #3
declare token @llvm.coro.save(i8*) #3
declare i8* @llvm.coro.frame() #5
declare ptr @llvm.coro.begin(token, ptr writeonly) #3
declare token @llvm.coro.save(ptr) #3
declare ptr @llvm.coro.frame() #5
declare i8 @llvm.coro.suspend(token, i1) #3
declare i8* @llvm.coro.free(token, i8* nocapture readonly) #2
declare i1 @llvm.coro.end(i8*, i1) #3
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #4
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #4
declare ptr @llvm.coro.free(token, ptr nocapture readonly) #2
declare i1 @llvm.coro.end(ptr, i1) #3
declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #4
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #4
61 changes: 28 additions & 33 deletions llvm/test/Transforms/Coroutines/coro-frame-reuse-alloca-04.ll
Original file line number Diff line number Diff line change
@@ -1,79 +1,74 @@
; Tests that variables of different type with incompatible alignment in a Corotuine whose lifetime
; range is not overlapping each other should not re-use the same slot in Coroutine frame.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split<reuse-storage>),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split<reuse-storage>),simplifycfg,early-cse' -S | FileCheck %s
%"struct.task::promise_type" = type { i8 }
%struct.awaitable = type { i8 }
%struct.big_structure = type { [500 x i8] }
%struct.big_structure.2 = type { [300 x i8] }
declare i8* @malloc(i64)
declare void @consume(%struct.big_structure*)
declare void @consume.2(%struct.big_structure.2*)
declare ptr @malloc(i64)
declare void @consume(ptr)
declare void @consume.2(ptr)
define void @a(i1 zeroext %cond) presplitcoroutine {
entry:
%__promise = alloca %"struct.task::promise_type", align 1
%a = alloca %struct.big_structure, align 1
%ref.tmp7 = alloca %struct.awaitable, align 1
%b = alloca %struct.big_structure.2, align 32
%ref.tmp18 = alloca %struct.awaitable, align 1
%0 = getelementptr inbounds %"struct.task::promise_type", %"struct.task::promise_type"* %__promise, i64 0, i32 0
%1 = call token @llvm.coro.id(i32 16, i8* nonnull %0, i8* bitcast (void (i1)* @a to i8*), i8* null)
%0 = call token @llvm.coro.id(i32 16, ptr nonnull %__promise, ptr @a, ptr null)
br label %init.ready
init.ready:
%2 = call noalias nonnull i8* @llvm.coro.begin(token %1, i8* null)
call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %0)
%1 = call noalias nonnull ptr @llvm.coro.begin(token %0, ptr null)
call void @llvm.lifetime.start.p0(i64 1, ptr nonnull %__promise)
br i1 %cond, label %if.then, label %if.else
if.then:
%3 = getelementptr inbounds %struct.big_structure, %struct.big_structure* %a, i64 0, i32 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 500, i8* nonnull %3)
call void @consume(%struct.big_structure* nonnull %a)
%save = call token @llvm.coro.save(i8* null)
call void @llvm.lifetime.start.p0(i64 500, ptr nonnull %a)
call void @consume(ptr nonnull %a)
%save = call token @llvm.coro.save(ptr null)
%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
switch i8 %suspend, label %coro.ret [
i8 0, label %await.ready
i8 1, label %cleanup1
]
await.ready:
call void @llvm.lifetime.end.p0i8(i64 500, i8* nonnull %3)
call void @llvm.lifetime.end.p0(i64 500, ptr nonnull %a)
br label %cleanup1
if.else:
%4 = getelementptr inbounds %struct.big_structure.2, %struct.big_structure.2* %b, i64 0, i32 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 300, i8* nonnull %4)
call void @consume.2(%struct.big_structure.2* nonnull %b)
%save2 = call token @llvm.coro.save(i8* null)
call void @llvm.lifetime.start.p0(i64 300, ptr nonnull %b)
call void @consume.2(ptr nonnull %b)
%save2 = call token @llvm.coro.save(ptr null)
%suspend2 = call i8 @llvm.coro.suspend(token %save2, i1 false)
switch i8 %suspend2, label %coro.ret [
i8 0, label %await2.ready
i8 1, label %cleanup2
]
await2.ready:
call void @llvm.lifetime.end.p0i8(i64 300, i8* nonnull %4)
call void @llvm.lifetime.end.p0(i64 300, ptr nonnull %b)
br label %cleanup2
cleanup1:
call void @llvm.lifetime.end.p0i8(i64 500, i8* nonnull %3)
call void @llvm.lifetime.end.p0(i64 500, ptr nonnull %a)
br label %cleanup
cleanup2:
call void @llvm.lifetime.end.p0i8(i64 300, i8* nonnull %4)
call void @llvm.lifetime.end.p0(i64 300, ptr nonnull %b)
br label %cleanup
cleanup:
call i8* @llvm.coro.free(token %1, i8* %2)
call ptr @llvm.coro.free(token %0, ptr %1)
br label %coro.ret
coro.ret:
call i1 @llvm.coro.end(i8* null, i1 false)
call i1 @llvm.coro.end(ptr null, i1 false)
ret void
}
; CHECK: %a.Frame = type { void (%a.Frame*)*, void (%a.Frame*)*, %"struct.task::promise_type", %struct.big_structure, i1, [26 x i8], %struct.big_structure.2 }
; CHECK: %a.Frame = type { ptr, ptr, %"struct.task::promise_type", %struct.big_structure, i1, [26 x i8], %struct.big_structure.2 }
; CHECK-LABEL: @a.resume(
; CHECK: %[[A:.*]] = getelementptr inbounds %a.Frame, %a.Frame* %FramePtr, i32 0, i32 3
; CHECK: %[[A:.*]] = getelementptr inbounds %a.Frame, %a.Frame* %FramePtr, i32 0, i32 6

declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*)
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr)
declare i1 @llvm.coro.alloc(token) #3
declare i64 @llvm.coro.size.i64() #5
declare i8* @llvm.coro.begin(token, i8* writeonly) #3
declare token @llvm.coro.save(i8*) #3
declare i8* @llvm.coro.frame() #5
declare ptr @llvm.coro.begin(token, ptr writeonly) #3
declare token @llvm.coro.save(ptr) #3
declare ptr @llvm.coro.frame() #5
declare i8 @llvm.coro.suspend(token, i1) #3
declare i8* @llvm.coro.free(token, i8* nocapture readonly) #2
declare i1 @llvm.coro.end(i8*, i1) #3
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #4
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #4
declare ptr @llvm.coro.free(token, ptr nocapture readonly) #2
declare i1 @llvm.coro.end(ptr, i1) #3
declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #4
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #4
61 changes: 28 additions & 33 deletions llvm/test/Transforms/Coroutines/coro-frame-reuse-alloca-05.ll
Original file line number Diff line number Diff line change
@@ -1,79 +1,74 @@
; Tests that variables of different type with incompatible alignment in a Corotuine whose
; lifetime range is not overlapping each other re-use the same slot in CorotuineFrame.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split<reuse-storage>),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split<reuse-storage>),simplifycfg,early-cse' -S | FileCheck %s
%"struct.task::promise_type" = type { i8 }
%struct.awaitable = type { i8 }
%struct.big_structure = type { [500 x i8] }
%struct.big_structure.2 = type { [400 x i8] }
declare i8* @malloc(i64)
declare void @consume(%struct.big_structure*)
declare void @consume.2(%struct.big_structure.2*)
declare ptr @malloc(i64)
declare void @consume(ptr)
declare void @consume.2(ptr)
define void @a(i1 zeroext %cond) presplitcoroutine {
entry:
%__promise = alloca %"struct.task::promise_type", align 1
%a = alloca %struct.big_structure, align 32
%ref.tmp7 = alloca %struct.awaitable, align 1
%b = alloca %struct.big_structure.2, align 16
%ref.tmp18 = alloca %struct.awaitable, align 1
%0 = getelementptr inbounds %"struct.task::promise_type", %"struct.task::promise_type"* %__promise, i64 0, i32 0
%1 = call token @llvm.coro.id(i32 16, i8* nonnull %0, i8* bitcast (void (i1)* @a to i8*), i8* null)
%0 = call token @llvm.coro.id(i32 16, ptr nonnull %__promise, ptr @a, ptr null)
br label %init.ready
init.ready:
%2 = call noalias nonnull i8* @llvm.coro.begin(token %1, i8* null)
call void @llvm.lifetime.start.p0i8(i64 1, i8* nonnull %0)
%1 = call noalias nonnull ptr @llvm.coro.begin(token %0, ptr null)
call void @llvm.lifetime.start.p0(i64 1, ptr nonnull %__promise)
br i1 %cond, label %if.then, label %if.else
if.then:
%3 = getelementptr inbounds %struct.big_structure, %struct.big_structure* %a, i64 0, i32 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 500, i8* nonnull %3)
call void @consume(%struct.big_structure* nonnull %a)
%save = call token @llvm.coro.save(i8* null)
call void @llvm.lifetime.start.p0(i64 500, ptr nonnull %a)
call void @consume(ptr nonnull %a)
%save = call token @llvm.coro.save(ptr null)
%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
switch i8 %suspend, label %coro.ret [
i8 0, label %await.ready
i8 1, label %cleanup1
]
await.ready:
call void @llvm.lifetime.end.p0i8(i64 500, i8* nonnull %3)
call void @llvm.lifetime.end.p0(i64 500, ptr nonnull %a)
br label %cleanup1
if.else:
%4 = getelementptr inbounds %struct.big_structure.2, %struct.big_structure.2* %b, i64 0, i32 0, i64 0
call void @llvm.lifetime.start.p0i8(i64 400, i8* nonnull %4)
call void @consume.2(%struct.big_structure.2* nonnull %b)
%save2 = call token @llvm.coro.save(i8* null)
call void @llvm.lifetime.start.p0(i64 400, ptr nonnull %b)
call void @consume.2(ptr nonnull %b)
%save2 = call token @llvm.coro.save(ptr null)
%suspend2 = call i8 @llvm.coro.suspend(token %save2, i1 false)
switch i8 %suspend2, label %coro.ret [
i8 0, label %await2.ready
i8 1, label %cleanup2
]
await2.ready:
call void @llvm.lifetime.end.p0i8(i64 400, i8* nonnull %4)
call void @llvm.lifetime.end.p0(i64 400, ptr nonnull %b)
br label %cleanup2
cleanup1:
call void @llvm.lifetime.end.p0i8(i64 500, i8* nonnull %3)
call void @llvm.lifetime.end.p0(i64 500, ptr nonnull %a)
br label %cleanup
cleanup2:
call void @llvm.lifetime.end.p0i8(i64 400, i8* nonnull %4)
call void @llvm.lifetime.end.p0(i64 400, ptr nonnull %b)
br label %cleanup
cleanup:
call i8* @llvm.coro.free(token %1, i8* %2)
call ptr @llvm.coro.free(token %0, ptr %1)
br label %coro.ret
coro.ret:
call i1 @llvm.coro.end(i8* null, i1 false)
call i1 @llvm.coro.end(ptr null, i1 false)
ret void
}
; CHECK: %a.Frame = type { void (%a.Frame*)*, void (%a.Frame*)*, %"struct.task::promise_type", i1, [14 x i8], %struct.big_structure }
; CHECK: %a.Frame = type { ptr, ptr, %"struct.task::promise_type", i1, [14 x i8], %struct.big_structure }
; CHECK-LABEL: @a.resume(
; CHECK: %[[A:.*]] = getelementptr inbounds %a.Frame, %a.Frame* %FramePtr, i32 0, i32 3
; CHECK: %[[A:.*]] = getelementptr inbounds %a.Frame, %a.Frame* %FramePtr, i32 0, i32 5

declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*)
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr)
declare i1 @llvm.coro.alloc(token) #3
declare i64 @llvm.coro.size.i64() #5
declare i8* @llvm.coro.begin(token, i8* writeonly) #3
declare token @llvm.coro.save(i8*) #3
declare i8* @llvm.coro.frame() #5
declare ptr @llvm.coro.begin(token, ptr writeonly) #3
declare token @llvm.coro.save(ptr) #3
declare ptr @llvm.coro.frame() #5
declare i8 @llvm.coro.suspend(token, i1) #3
declare i8* @llvm.coro.free(token, i8* nocapture readonly) #2
declare i1 @llvm.coro.end(i8*, i1) #3
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #4
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #4
declare ptr @llvm.coro.free(token, ptr nocapture readonly) #2
declare i1 @llvm.coro.end(ptr, i1) #3
declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #4
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #4
50 changes: 25 additions & 25 deletions llvm/test/Transforms/Coroutines/coro-frame.ll
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
; Check that we can handle spills of the result of the invoke instruction
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s

define i8* @f(i64 %this) presplitcoroutine personality i32 0 {
define ptr @f(i64 %this) presplitcoroutine personality i32 0 {
entry:
%this.addr = alloca i64
store i64 %this, i64* %this.addr
%this1 = load i64, i64* %this.addr
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
store i64 %this, ptr %this.addr
%this1 = load i64, ptr %this.addr
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%size = call i32 @llvm.coro.size.i32()
%alloc = call i8* @malloc(i32 %size)
%hdl = call i8* @llvm.coro.begin(token %id, i8* %alloc)
%alloc = call ptr @malloc(i32 %size)
%hdl = call ptr @llvm.coro.begin(token %id, ptr %alloc)
%r = invoke double @print(double 0.0) to label %cont unwind label %pad

cont:
Expand All @@ -22,45 +22,45 @@ resume:
br label %cleanup

cleanup:
%mem = call i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
%mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
call void @free(ptr %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 0)
ret i8* %hdl
call i1 @llvm.coro.end(ptr %hdl, i1 0)
ret ptr %hdl
pad:
%tok = cleanuppad within none []
cleanupret from %tok unwind to caller
}

; See if the float was added to the frame
; CHECK-LABEL: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, double, i64, i1 }
; CHECK-LABEL: %f.Frame = type { ptr, ptr, double, i64, i1 }

; See if the float was spilled into the frame
; CHECK-LABEL: @f(
; CHECK: %r = call double @print(
; CHECK: %r.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2
; CHECK: store double %r, double* %r.spill.addr
; CHECK: ret i8* %hdl
; CHECK: %r.spill.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
; CHECK: store double %r, ptr %r.spill.addr
; CHECK: ret ptr %hdl

; See if the float was loaded from the frame
; CHECK-LABEL: @f.resume(%f.Frame* noundef nonnull align 8
; CHECK: %r.reload = load double, double* %r.reload.addr
; CHECK-LABEL: @f.resume(ptr noundef nonnull align 8
; CHECK: %r.reload = load double, ptr %r.reload.addr
; CHECK: call double @print(double %r.reload)
; CHECK: ret void

declare i8* @llvm.coro.free(token, i8*)
declare ptr @llvm.coro.free(token, ptr)
declare i32 @llvm.coro.size.i32()
declare i8 @llvm.coro.suspend(token, i1)
declare void @llvm.coro.resume(i8*)
declare void @llvm.coro.destroy(i8*)
declare void @llvm.coro.resume(ptr)
declare void @llvm.coro.destroy(ptr)

declare token @llvm.coro.id(i32, i8*, i8*, i8*)
declare token @llvm.coro.id(i32, ptr, ptr, ptr)
declare i1 @llvm.coro.alloc(token)
declare i8* @llvm.coro.begin(token, i8*)
declare i1 @llvm.coro.end(i8*, i1)
declare ptr @llvm.coro.begin(token, ptr)
declare i1 @llvm.coro.end(ptr, i1)

declare noalias i8* @malloc(i32)
declare noalias ptr @malloc(i32)
declare double @print(double)
declare void @print2(i64)
declare void @free(i8*)
declare void @free(ptr)
270 changes: 126 additions & 144 deletions llvm/test/Transforms/Coroutines/coro-heap-elide.ll

Large diffs are not rendered by default.

52 changes: 26 additions & 26 deletions llvm/test/Transforms/Coroutines/coro-padding.ll
Original file line number Diff line number Diff line change
@@ -1,61 +1,61 @@
; Check that we will insert the correct padding if natural alignment of the
; spilled data does not match the alignment specified in alloca instruction.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s

%PackedStruct = type <{ i64 }>

declare void @consume(%PackedStruct*)
declare void @consume(ptr)

define i8* @f() presplitcoroutine {
define ptr @f() presplitcoroutine {
entry:
%data = alloca %PackedStruct, align 32
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%size = call i32 @llvm.coro.size.i32()
%alloc = call i8* @malloc(i32 %size)
%hdl = call i8* @llvm.coro.begin(token %id, i8* %alloc)
call void @consume(%PackedStruct* %data)
%alloc = call ptr @malloc(i32 %size)
%hdl = call ptr @llvm.coro.begin(token %id, ptr %alloc)
call void @consume(ptr %data)
%0 = call i8 @llvm.coro.suspend(token none, i1 false)
switch i8 %0, label %suspend [i8 0, label %resume
i8 1, label %cleanup]
resume:
call void @consume(%PackedStruct* %data)
call void @consume(ptr %data)
br label %cleanup

cleanup:
%mem = call i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
%mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
call void @free(ptr %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 0)
ret i8* %hdl
call i1 @llvm.coro.end(ptr %hdl, i1 0)
ret ptr %hdl
}

; See if the padding was inserted before PackedStruct
; CHECK-LABEL: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i1, [15 x i8], %PackedStruct }
; CHECK-LABEL: %f.Frame = type { ptr, ptr, i1, [15 x i8], %PackedStruct }

; See if we used correct index to access packed struct (padding is field 3)
; CHECK-LABEL: @f(
; CHECK: %[[DATA:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4
; CHECK-NEXT: call void @consume(%PackedStruct* %[[DATA]])
; CHECK: ret i8*
; CHECK: %[[DATA:.+]] = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 4
; CHECK-NEXT: call void @consume(ptr %[[DATA]])
; CHECK: ret ptr

; See if we used correct index to access packed struct (padding is field 3)
; CHECK-LABEL: @f.resume(
; CHECK: %[[DATA:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4
; CHECK-NEXT: call void @consume(%PackedStruct* %[[DATA]])
; CHECK: %[[DATA:.+]] = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 4
; CHECK-NEXT: call void @consume(ptr %[[DATA]])
; CHECK: ret void

declare i8* @llvm.coro.free(token, i8*)
declare ptr @llvm.coro.free(token, ptr)
declare i32 @llvm.coro.size.i32()
declare i8 @llvm.coro.suspend(token, i1)
declare void @llvm.coro.resume(i8*)
declare void @llvm.coro.destroy(i8*)
declare void @llvm.coro.resume(ptr)
declare void @llvm.coro.destroy(ptr)

declare token @llvm.coro.id(i32, i8*, i8*, i8*)
declare token @llvm.coro.id(i32, ptr, ptr, ptr)
declare i1 @llvm.coro.alloc(token)
declare i8* @llvm.coro.begin(token, i8*)
declare i1 @llvm.coro.end(i8*, i1)
declare ptr @llvm.coro.begin(token, ptr)
declare i1 @llvm.coro.end(ptr, i1)

declare noalias i8* @malloc(i32)
declare noalias ptr @malloc(i32)
declare double @print(double)
declare void @free(i8*)
declare void @free(ptr)
89 changes: 43 additions & 46 deletions llvm/test/Transforms/Coroutines/coro-param-copy.ll
Original file line number Diff line number Diff line change
@@ -1,96 +1,93 @@
; Check that we create copy the data from the alloca into the coroutine
; frame slot if it was written to.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s

define i8* @f() presplitcoroutine {
define ptr @f() presplitcoroutine {
entry:
%a.addr = alloca i64 ; read-only before coro.begin
%a = load i64, i64* %a.addr ; cannot modify the value, don't need to copy
%a = load i64, ptr %a.addr ; cannot modify the value, don't need to copy

%x.addr = alloca i64
call void @use(i64* %x.addr) ; uses %x.addr before coro.begin
call void @use(ptr %x.addr) ; uses %x.addr before coro.begin

%y.addr = alloca i64
%y.cast = bitcast i64* %y.addr to i8* ; alias created and used after coro.begin

%z.addr = alloca i64
%flag = call i1 @check()
br i1 %flag, label %flag_true, label %flag_merge

flag_true:
call void @use(i64* %z.addr) ; conditionally used %z.addr
call void @use(ptr %z.addr) ; conditionally used %z.addr
br label %flag_merge

flag_merge:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%size = call i32 @llvm.coro.size.i32()
%alloc = call i8* @myAlloc(i32 %size)
%hdl = call i8* @llvm.coro.begin(token %id, i8* %alloc)
call void @llvm.memset.p0i8.i32(i8* %y.cast, i8 1, i32 4, i1 false)
%alloc = call ptr @myAlloc(i32 %size)
%hdl = call ptr @llvm.coro.begin(token %id, ptr %alloc)
call void @llvm.memset.p0.i32(ptr %y.addr, i8 1, i32 4, i1 false)
%0 = call i8 @llvm.coro.suspend(token none, i1 false)
switch i8 %0, label %suspend [i8 0, label %resume
i8 1, label %cleanup]
resume:
call void @use(i64* %a.addr)
call void @use(i64* %x.addr)
call void @use(i64* %y.addr)
call void @use(i64* %z.addr)
call void @use(ptr %a.addr)
call void @use(ptr %x.addr)
call void @use(ptr %y.addr)
call void @use(ptr %z.addr)
br label %cleanup

cleanup:
%mem = call i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
%mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
call void @free(ptr %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 0)
ret i8* %hdl
call i1 @llvm.coro.end(ptr %hdl, i1 0)
ret ptr %hdl
}

; See that we added both x and y to the frame.
; CHECK: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i64, i64, i64, i64, i1 }
; CHECK: %f.Frame = type { ptr, ptr, i64, i64, i64, i64, i1 }

; See that all of the uses prior to coro-begin stays put.
; CHECK-LABEL: define i8* @f() {
; CHECK-LABEL: define ptr @f() {
; CHECK-NEXT: entry:
; CHECK-NEXT: %a.addr = alloca i64
; CHECK-NEXT: %x.addr = alloca i64
; CHECK-NEXT: call void @use(i64* %x.addr)
; CHECK-NEXT: %y.addr = alloca i64
; CHECK-NEXT: call void @use(ptr %x.addr)
; CHECK-NEXT: %z.addr = alloca i64

; See that we only copy the x as y was not modified prior to coro.begin.
; CHECK: store void (%f.Frame*)* @f.destroy, void (%f.Frame*)** %destroy.addr
; CHECK: store ptr @f.destroy, ptr %destroy.addr
; The next 3 instructions are to copy data in %x.addr from stack to frame.
; CHECK-NEXT: %0 = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 3
; CHECK-NEXT: %1 = load i64, i64* %x.addr, align 4
; CHECK-NEXT: store i64 %1, i64* %0, align 4
; The next 2 instructions are to recreate %y.cast in the original IR.
; CHECK-NEXT: %2 = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 4
; CHECK-NEXT: %3 = bitcast i64* %2 to i8*
; CHECK-NEXT: %0 = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 3
; CHECK-NEXT: %1 = load i64, ptr %x.addr, align 4
; CHECK-NEXT: store i64 %1, ptr %0, align 4
; The next 3 instructions are to copy data in %z.addr from stack to frame.
; CHECK-NEXT: %4 = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 5
; CHECK-NEXT: %5 = load i64, i64* %z.addr, align 4
; CHECK-NEXT: store i64 %5, i64* %4, align 4
; CHECK-NEXT: call void @llvm.memset.p0i8.i32(i8* %3, i8 1, i32 4, i1 false)
; CHECK-NEXT: %index.addr1 = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 6
; CHECK-NEXT: store i1 false, i1* %index.addr1, align 1
; CHECK-NEXT: ret i8* %hdl
; CHECK-NEXT: [[T2:%.+]] = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 5
; CHECK-NEXT: [[T3:%.+]] = load i64, ptr %z.addr, align 4
; CHECK-NEXT: store i64 [[T3]], ptr [[T2]], align 4
; The next instruction is to recreate %y.cast in the original IR.
; CHECK-NEXT: %y.addr.reload.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 4
; CHECK-NEXT: call void @llvm.memset.p0.i32(ptr %y.addr.reload.addr, i8 1, i32 4, i1 false)
; CHECK-NEXT: %index.addr1 = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 6
; CHECK-NEXT: store i1 false, ptr %index.addr1, align 1
; CHECK-NEXT: ret ptr %hdl


declare i8* @llvm.coro.free(token, i8*)
declare ptr @llvm.coro.free(token, ptr)
declare i32 @llvm.coro.size.i32()
declare i8 @llvm.coro.suspend(token, i1)
declare void @llvm.coro.resume(i8*)
declare void @llvm.coro.destroy(i8*)
declare void @llvm.coro.resume(ptr)
declare void @llvm.coro.destroy(ptr)

declare token @llvm.coro.id(i32, i8*, i8*, i8*)
declare token @llvm.coro.id(i32, ptr, ptr, ptr)
declare i1 @llvm.coro.alloc(token)
declare i8* @llvm.coro.begin(token, i8*)
declare i1 @llvm.coro.end(i8*, i1)
declare ptr @llvm.coro.begin(token, ptr)
declare i1 @llvm.coro.end(ptr, i1)

declare void @llvm.memset.p0i8.i32(i8*, i8, i32, i1)
declare void @llvm.memset.p0.i32(ptr, i8, i32, i1)

declare noalias i8* @myAlloc(i32)
declare void @use(i64*)
declare void @free(i8*)
declare noalias ptr @myAlloc(i32)
declare void @use(ptr)
declare void @free(ptr)
declare i1 @check()
34 changes: 17 additions & 17 deletions llvm/test/Transforms/Coroutines/coro-resume-destroy.ll
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
; Tests that CoroEarly pass correctly lowers coro.resume, coro.destroy
; RUN: opt -opaque-pointers=0 < %s -S -passes=coro-early | FileCheck %s
; RUN: opt < %s -S -passes=coro-early | FileCheck %s

; CHECK-LABEL: @callResume(
define void @callResume(i8* %hdl) {
define void @callResume(ptr %hdl) {
; CHECK-NEXT: entry
entry:
; CHECK-NEXT: %0 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
; CHECK-NEXT: %1 = bitcast i8* %0 to void (i8*)*
; CHECK-NEXT: call fastcc void %1(i8* %hdl)
call void @llvm.coro.resume(i8* %hdl)
; CHECK-NEXT: %0 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 0)
; CHECK-NEXT: %1 = bitcast ptr %0 to ptr
; CHECK-NEXT: call fastcc void %1(ptr %hdl)
call void @llvm.coro.resume(ptr %hdl)

; CHECK-NEXT: %2 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
; CHECK-NEXT: %3 = bitcast i8* %2 to void (i8*)*
; CHECK-NEXT: call fastcc void %3(i8* %hdl)
call void @llvm.coro.destroy(i8* %hdl)
; CHECK-NEXT: %2 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
; CHECK-NEXT: %3 = bitcast ptr %2 to ptr
; CHECK-NEXT: call fastcc void %3(ptr %hdl)
call void @llvm.coro.destroy(ptr %hdl)

ret void
; CHECK-NEXT: ret void
}

; CHECK-LABEL: @eh(
define void @eh(i8* %hdl) personality i8* null {
define void @eh(ptr %hdl) personality ptr null {
; CHECK-NEXT: entry
entry:
; CHECK-NEXT: %0 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
; CHECK-NEXT: %1 = bitcast i8* %0 to void (i8*)*
; CHECK-NEXT: invoke fastcc void %1(i8* %hdl)
invoke void @llvm.coro.resume(i8* %hdl)
; CHECK-NEXT: %0 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 0)
; CHECK-NEXT: %1 = bitcast ptr %0 to ptr
; CHECK-NEXT: invoke fastcc void %1(ptr %hdl)
invoke void @llvm.coro.resume(ptr %hdl)
to label %cont unwind label %ehcleanup
cont:
ret void
Expand All @@ -37,5 +37,5 @@ ehcleanup:
}


declare void @llvm.coro.resume(i8*)
declare void @llvm.coro.destroy(i8*)
declare void @llvm.coro.resume(ptr)
declare void @llvm.coro.destroy(ptr)
79 changes: 36 additions & 43 deletions llvm/test/Transforms/Coroutines/coro-retcon-frame.ll
Original file line number Diff line number Diff line change
@@ -1,63 +1,56 @@
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s

target datalayout = "p:64:64:64"

declare void @prototype_f(i8*, i1)
declare void @prototype_f(ptr, i1)

declare noalias i8* @allocate(i32 %size)
declare void @deallocate(i8* %ptr)
declare void @init(i64 *%ptr)
declare void @use(i8* %ptr)
declare void @use_addr_val(i64 %val, {i64, i64}*%addr)
declare noalias ptr @allocate(i32 %size)
declare void @deallocate(ptr %ptr)
declare void @init(ptr %ptr)
declare void @use(ptr %ptr)
declare void @use_addr_val(i64 %val, ptr %addr)

define { i8*, {i64, i64}* } @f(i8* %buffer) presplitcoroutine {
define { ptr, ptr } @f(ptr %buffer) presplitcoroutine {
entry:
%tmp = alloca { i64, i64 }, align 8
%proj.1 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %tmp, i64 0, i32 0
%proj.2 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %tmp, i64 0, i32 1
store i64 0, i64* %proj.1, align 8
store i64 0, i64* %proj.2, align 8
%cast = bitcast { i64, i64 }* %tmp to i8*
%escape_addr = ptrtoint {i64, i64}* %tmp to i64
%id = call token @llvm.coro.id.retcon.once(i32 32, i32 8, i8* %buffer, i8* bitcast (void (i8*, i1)* @prototype_f to i8*), i8* bitcast (i8* (i32)* @allocate to i8*), i8* bitcast (void (i8*)* @deallocate to i8*))
%hdl = call i8* @llvm.coro.begin(token %id, i8* null)
%proj.2.2 = getelementptr inbounds { i64, i64 }, { i64, i64 }* %tmp, i64 0, i32 1
call void @init(i64 * %proj.1)
call void @init(i64 * %proj.2.2)
call void @use_addr_val(i64 %escape_addr, {i64, i64}* %tmp)
%abort = call i1 (...) @llvm.coro.suspend.retcon.i1({i64, i64}* %tmp)
%proj.1 = getelementptr inbounds { i64, i64 }, ptr %tmp, i64 0, i32 0
%proj.2 = getelementptr inbounds { i64, i64 }, ptr %tmp, i64 0, i32 1
store i64 0, ptr %proj.1, align 8
store i64 0, ptr %proj.2, align 8
%escape_addr = ptrtoint ptr %tmp to i64
%id = call token @llvm.coro.id.retcon.once(i32 32, i32 8, ptr %buffer, ptr @prototype_f, ptr @allocate, ptr @deallocate)
%hdl = call ptr @llvm.coro.begin(token %id, ptr null)
%proj.2.2 = getelementptr inbounds { i64, i64 }, ptr %tmp, i64 0, i32 1
call void @init(ptr %proj.1)
call void @init(ptr %proj.2.2)
call void @use_addr_val(i64 %escape_addr, ptr %tmp)
%abort = call i1 (...) @llvm.coro.suspend.retcon.i1(ptr %tmp)
br i1 %abort, label %end, label %resume

resume:
call void @use(i8* %cast)
call void @use(ptr %tmp)
br label %end

end:
call i1 @llvm.coro.end(i8* %hdl, i1 0)
call i1 @llvm.coro.end(ptr %hdl, i1 0)
unreachable
}
; Make sure we don't lose writes to the frame.
; CHECK-LABEL: define { i8*, { i64, i64 }* } @f(i8* %buffer) {
; CHECK: [[FRAMEPTR:%.*]] = bitcast i8* %buffer to %f.Frame*
; CHECK: [[TMP:%.*]] = getelementptr inbounds %f.Frame, %f.Frame* [[FRAMEPTR]], i32 0, i32 0
; CHECK: [[PROJ1:%.*]] = getelementptr inbounds { i64, i64 }, { i64, i64 }* [[TMP]], i64 0, i32 0
; CHECK: [[PROJ2:%.*]] = getelementptr inbounds { i64, i64 }, { i64, i64 }* [[TMP]], i64 0, i32 1
; CHECK: store i64 0, i64* [[PROJ1]]
; CHECK: store i64 0, i64* [[PROJ2]]
; CHECK: [[ESCAPED_ADDR:%.*]] = ptrtoint { i64, i64 }* [[TMP]] to i64
; CHECK: call void @init(i64* [[PROJ1]])
; CHECK: call void @init(i64* [[PROJ2]])
; CHECK: call void @use_addr_val(i64 [[ESCAPED_ADDR]], { i64, i64 }* [[TMP]])

; CHECK-LABEL: define internal void @f.resume.0(i8* {{.*}} %0, i1 %1) {
; CHECK: [[FRAMEPTR:%.*]] = bitcast i8* %0 to %f.Frame*
; CHECK: [[TMP:%.*]] = getelementptr inbounds %f.Frame, %f.Frame* [[FRAMEPTR]], i32 0, i32 0
; CHECK-LABEL: define { ptr, ptr } @f(ptr %buffer) {
; CHECK: [[PROJ2:%.*]] = getelementptr inbounds { i64, i64 }, ptr %buffer, i64 0, i32 1
; CHECK: store i64 0, ptr %buffer
; CHECK: store i64 0, ptr [[PROJ2]]
; CHECK: [[ESCAPED_ADDR:%.*]] = ptrtoint ptr %buffer to i64
; CHECK: call void @init(ptr %buffer)
; CHECK: call void @init(ptr [[PROJ2]])
; CHECK: call void @use_addr_val(i64 [[ESCAPED_ADDR]], ptr %buffer)

; CHECK-LABEL: define internal void @f.resume.0(ptr {{.*}} %0, i1 %1) {
; CHECK: resume:
; CHECK: [[CAST:%.*]] = bitcast { i64, i64 }* [[TMP]] to i8*
; CHECK: call void @use(i8* [[CAST]])
; CHECK: call void @use(ptr %0)

declare token @llvm.coro.id.retcon.once(i32, i32, i8*, i8*, i8*, i8*)
declare i8* @llvm.coro.begin(token, i8*)
declare token @llvm.coro.id.retcon.once(i32, i32, ptr, ptr, ptr, ptr)
declare ptr @llvm.coro.begin(token, ptr)
declare i1 @llvm.coro.suspend.retcon.i1(...)
declare i1 @llvm.coro.end(i8*, i1)
declare i1 @llvm.coro.end(ptr, i1)

66 changes: 32 additions & 34 deletions llvm/test/Transforms/Coroutines/coro-spill-corobegin.ll
Original file line number Diff line number Diff line change
@@ -1,71 +1,69 @@
; Check that we can spills coro.begin from an inlined inner coroutine.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s

%g.Frame = type { void (%g.Frame*)*, void (%g.Frame*)*, i32, i1, i32 }
%g.Frame = type { ptr, ptr, i32, i1, i32 }

@g.resumers = private constant [3 x void (%g.Frame*)*] [void (%g.Frame*)* @g.dummy, void (%g.Frame*)* @g.dummy, void (%g.Frame*)* @g.dummy]
@g.resumers = private constant [3 x ptr] [ptr @g.dummy, ptr @g.dummy, ptr @g.dummy]

declare void @g.dummy(%g.Frame*)
declare void @g.dummy(ptr)

declare i8* @g()
declare ptr @g()

define i8* @f() presplitcoroutine {
define ptr @f() presplitcoroutine {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%size = call i32 @llvm.coro.size.i32()
%alloc = call i8* @malloc(i32 %size)
%hdl = call i8* @llvm.coro.begin(token %id, i8* %alloc)
%alloc = call ptr @malloc(i32 %size)
%hdl = call ptr @llvm.coro.begin(token %id, ptr %alloc)

%innerid = call token @llvm.coro.id(i32 0, i8* null, i8* bitcast (i8* ()* @g to i8*), i8* bitcast ([3 x void (%g.Frame*)*]* @g.resumers to i8*))
%innerhdl = call noalias nonnull i8* @llvm.coro.begin(token %innerid, i8* null)
%gframe = bitcast i8* %innerhdl to %g.Frame*
%innerid = call token @llvm.coro.id(i32 0, ptr null, ptr @g, ptr @g.resumers)
%innerhdl = call noalias nonnull ptr @llvm.coro.begin(token %innerid, ptr null)

%tok = call i8 @llvm.coro.suspend(token none, i1 false)
switch i8 %tok, label %suspend [i8 0, label %resume
i8 1, label %cleanup]
resume:
%gvar.addr = getelementptr inbounds %g.Frame, %g.Frame* %gframe, i32 0, i32 4
%gvar = load i32, i32* %gvar.addr
%gvar.addr = getelementptr inbounds %g.Frame, ptr %innerhdl, i32 0, i32 4
%gvar = load i32, ptr %gvar.addr
call void @print.i32(i32 %gvar)
br label %cleanup

cleanup:
%mem = call i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
%mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
call void @free(ptr %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 0)
ret i8* %hdl
call i1 @llvm.coro.end(ptr %hdl, i1 0)
ret ptr %hdl
}

; See if the i8* for coro.begin was added to f.Frame
; CHECK-LABEL: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i8*, i1 }
; CHECK-LABEL: %f.Frame = type { ptr, ptr, ptr, i1 }

; See if the g's coro.begin was spilled into the frame
; CHECK-LABEL: @f(
; CHECK: %innerid = call token @llvm.coro.id(i32 0, i8* null, i8* bitcast (i8* ()* @g to i8*), i8* bitcast ([3 x void (%g.Frame*)*]* @g.resumers to i8*))
; CHECK: %innerhdl = call noalias nonnull i8* @llvm.coro.begin(token %innerid, i8* null)
; CHECK: %[[spilladdr:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2
; CHECK: store i8* %innerhdl, i8** %[[spilladdr]]
; CHECK: %innerid = call token @llvm.coro.id(i32 0, ptr null, ptr @g, ptr @g.resumers)
; CHECK: %innerhdl = call noalias nonnull ptr @llvm.coro.begin(token %innerid, ptr null)
; CHECK: %[[spilladdr:.+]] = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
; CHECK: store ptr %innerhdl, ptr %[[spilladdr]]

; See if the coro.begin was loaded from the frame
; CHECK-LABEL: @f.resume(
; CHECK: %[[innerhdlAddr:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %{{.+}}, i32 0, i32 2
; CHECK: %[[innerhdl:.+]] = load i8*, i8** %[[innerhdlAddr]]
; CHECK: %[[gframe:.+]] = bitcast i8* %[[innerhdl]] to %g.Frame*
; CHECK: %[[gvarAddr:.+]] = getelementptr inbounds %g.Frame, %g.Frame* %[[gframe]], i32 0, i32 4
; CHECK: %[[gvar:.+]] = load i32, i32* %[[gvarAddr]]
; CHECK: %[[innerhdlAddr:.+]] = getelementptr inbounds %f.Frame, ptr %{{.+}}, i32 0, i32 2
; CHECK: %[[innerhdl:.+]] = load ptr, ptr %[[innerhdlAddr]]
; CHECK: %[[gvarAddr:.+]] = getelementptr inbounds %g.Frame, ptr %[[innerhdl]], i32 0, i32 4
; CHECK: %[[gvar:.+]] = load i32, ptr %[[gvarAddr]]
; CHECK: call void @print.i32(i32 %[[gvar]])

declare i8* @llvm.coro.free(token, i8*)
declare ptr @llvm.coro.free(token, ptr)
declare i32 @llvm.coro.size.i32()
declare i8 @llvm.coro.suspend(token, i1)

declare token @llvm.coro.id(i32, i8*, i8*, i8*)
declare token @llvm.coro.id(i32, ptr, ptr, ptr)
declare i1 @llvm.coro.alloc(token)
declare i8* @llvm.coro.begin(token, i8*)
declare i1 @llvm.coro.end(i8*, i1)
declare ptr @llvm.coro.begin(token, ptr)
declare i1 @llvm.coro.end(ptr, i1)

declare noalias i8* @malloc(i32)
declare noalias ptr @malloc(i32)
declare void @print.i32(i32)
declare void @free(i8*)
declare void @free(ptr)
60 changes: 30 additions & 30 deletions llvm/test/Transforms/Coroutines/coro-spill-defs-before-corobegin.ll
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
; Verifies that phi and invoke definitions before CoroBegin are spilled properly.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s

define i8* @f(i1 %n) presplitcoroutine personality i32 0 {
define ptr @f(i1 %n) presplitcoroutine personality i32 0 {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%size = call i32 @llvm.coro.size.i32()
%alloc = call i8* @malloc(i32 %size)
%flag = call i1 @check(i8* %alloc)
%alloc = call ptr @malloc(i32 %size)
%flag = call i1 @check(ptr %alloc)
br i1 %flag, label %flag_true, label %flag_false

flag_true:
Expand All @@ -20,7 +20,7 @@ merge:
%value_invoke = invoke i32 @calc() to label %normal unwind label %lpad

normal:
%hdl = call i8* @llvm.coro.begin(token %id, i8* %alloc)
%hdl = call ptr @llvm.coro.begin(token %id, ptr %alloc)
call i32 @print(i32 %value_phi)
call i32 @print(i32 %value_invoke)
%sp1 = call i8 @llvm.coro.suspend(token none, i1 false)
Expand All @@ -32,48 +32,48 @@ resume:
br label %cleanup

cleanup:
%mem = call i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
%mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
call void @free(ptr %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 0)
ret i8* %hdl
call i1 @llvm.coro.end(ptr %hdl, i1 0)
ret ptr %hdl

lpad:
%lpval = landingpad { i8*, i32 }
%lpval = landingpad { ptr, i32 }
cleanup

resume { i8*, i32 } %lpval
resume { ptr, i32 } %lpval
}

; Verifies that the both value_phi and value_invoke are stored correctly in the coroutine frame
; CHECK: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i32, i32, i1 }
; CHECK: %f.Frame = type { ptr, ptr, i32, i32, i1 }
; CHECK-LABEL: @f(
; CHECK: %alloc = call i8* @malloc(i32 32)
; CHECK-NEXT: %flag = call i1 @check(i8* %alloc)
; CHECK: %alloc = call ptr @malloc(i32 32)
; CHECK-NEXT: %flag = call i1 @check(ptr %alloc)
; CHECK-NEXT: %spec.select = select i1 %flag, i32 0, i32 1
; CHECK-NEXT: %value_invoke = call i32 @calc()
; CHECK-NEXT: %hdl = call noalias nonnull i8* @llvm.coro.begin(token %id, i8* %alloc)
; CHECK-NEXT: %hdl = call noalias nonnull ptr @llvm.coro.begin(token %id, ptr %alloc)

; CHECK: store void (%f.Frame*)* @f.destroy, void (%f.Frame*)** %destroy.addr
; CHECK-NEXT: %value_invoke.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 3
; CHECK-NEXT: store i32 %value_invoke, i32* %value_invoke.spill.addr
; CHECK-NEXT: %value_phi.spill.addr = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 2
; CHECK-NEXT: store i32 %spec.select, i32* %value_phi.spill.addr
; CHECK: store ptr @f.destroy, ptr %destroy.addr
; CHECK-NEXT: %value_invoke.spill.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 3
; CHECK-NEXT: store i32 %value_invoke, ptr %value_invoke.spill.addr
; CHECK-NEXT: %value_phi.spill.addr = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 2
; CHECK-NEXT: store i32 %spec.select, ptr %value_phi.spill.addr

declare i8* @llvm.coro.free(token, i8*)
declare ptr @llvm.coro.free(token, ptr)
declare i32 @llvm.coro.size.i32()
declare i8 @llvm.coro.suspend(token, i1)
declare void @llvm.coro.resume(i8*)
declare void @llvm.coro.destroy(i8*)
declare void @llvm.coro.resume(ptr)
declare void @llvm.coro.destroy(ptr)

declare token @llvm.coro.id(i32, i8*, i8*, i8*)
declare token @llvm.coro.id(i32, ptr, ptr, ptr)
declare i1 @llvm.coro.alloc(token)
declare i8* @llvm.coro.begin(token, i8*)
declare i1 @llvm.coro.end(i8*, i1)
declare ptr @llvm.coro.begin(token, ptr)
declare i1 @llvm.coro.end(ptr, i1)

declare noalias i8* @malloc(i32)
declare noalias ptr @malloc(i32)
declare i32 @print(i32)
declare i1 @check(i8*)
declare i1 @check(ptr)
declare i32 @calc()
declare void @free(i8*)
declare void @free(ptr)
51 changes: 25 additions & 26 deletions llvm/test/Transforms/Coroutines/coro-spill-promise.ll
Original file line number Diff line number Diff line change
@@ -1,56 +1,55 @@
; Check that promise object is reloaded from the correct index of the coro frame.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s

%"class.task::promise_type" = type { [64 x i8] }

declare void @consume(i32*)
declare void @consume2(%"class.task::promise_type"*)
declare void @consume(ptr)
declare void @consume2(ptr)

define i8* @f() presplitcoroutine {
define ptr @f() presplitcoroutine {
entry:
%data = alloca i32, align 4
%__promise = alloca %"class.task::promise_type", align 64
%pv = bitcast %"class.task::promise_type"* %__promise to i8*
%id = call token @llvm.coro.id(i32 0, i8* %pv, i8* null, i8* null)
%id = call token @llvm.coro.id(i32 0, ptr %__promise, ptr null, ptr null)
%size = call i32 @llvm.coro.size.i32()
%alloc = call i8* @malloc(i32 %size)
%hdl = call i8* @llvm.coro.begin(token %id, i8* %alloc)
call void @consume(i32* %data)
%alloc = call ptr @malloc(i32 %size)
%hdl = call ptr @llvm.coro.begin(token %id, ptr %alloc)
call void @consume(ptr %data)
%0 = call i8 @llvm.coro.suspend(token none, i1 false)
switch i8 %0, label %suspend [i8 0, label %resume
i8 1, label %cleanup]
resume:
call void @consume(i32* %data)
call void @consume2(%"class.task::promise_type"* %__promise)
call void @consume(ptr %data)
call void @consume2(ptr %__promise)
br label %cleanup

cleanup:
%mem = call i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
%mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
call void @free(ptr %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 0)
ret i8* %hdl
call i1 @llvm.coro.end(ptr %hdl, i1 0)
ret ptr %hdl
}

; CHECK-LABEL: %f.Frame = type { void (%f.Frame*)*, void (%f.Frame*)*, i32, i1, [43 x i8], %"class.task::promise_type" }
; CHECK-LABEL: %f.Frame = type { ptr, ptr, i32, i1, [43 x i8], %"class.task::promise_type" }

; CHECK-LABEL: @f.resume(
; CHECK: %[[DATA:.+]] = getelementptr inbounds %f.Frame, %f.Frame* %FramePtr, i32 0, i32 5
; CHECK: call void @consume2(%"class.task::promise_type"* %[[DATA]])
; CHECK: %[[DATA:.+]] = getelementptr inbounds %f.Frame, ptr %hdl, i32 0, i32 5
; CHECK: call void @consume2(ptr %[[DATA]])
; CHECK: ret void

declare i8* @llvm.coro.free(token, i8*)
declare ptr @llvm.coro.free(token, ptr)
declare i32 @llvm.coro.size.i32()
declare i8 @llvm.coro.suspend(token, i1)
declare void @llvm.coro.resume(i8*)
declare void @llvm.coro.destroy(i8*)
declare void @llvm.coro.resume(ptr)
declare void @llvm.coro.destroy(ptr)

declare token @llvm.coro.id(i32, i8*, i8*, i8*)
declare token @llvm.coro.id(i32, ptr, ptr, ptr)
declare i1 @llvm.coro.alloc(token)
declare i8* @llvm.coro.begin(token, i8*)
declare i1 @llvm.coro.end(i8*, i1)
declare ptr @llvm.coro.begin(token, ptr)
declare i1 @llvm.coro.end(ptr, i1)

declare noalias i8* @malloc(i32)
declare noalias ptr @malloc(i32)
declare double @print(double)
declare void @free(i8*)
declare void @free(ptr)
56 changes: 28 additions & 28 deletions llvm/test/Transforms/Coroutines/coro-split-00.ll
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
; Tests that coro-split pass splits the coroutine into f, f.resume and f.destroy
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s

define i8* @f() presplitcoroutine !func_sanitize !0 {
define ptr @f() presplitcoroutine !func_sanitize !0 {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%need.alloc = call i1 @llvm.coro.alloc(token %id)
br i1 %need.alloc, label %dyn.alloc, label %begin

dyn.alloc:
%size = call i32 @llvm.coro.size.i32()
%alloc = call i8* @malloc(i32 %size)
%alloc = call ptr @malloc(i32 %size)
br label %begin

begin:
%phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ]
%hdl = call i8* @llvm.coro.begin(token %id, i8* %phi)
%phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
%hdl = call ptr @llvm.coro.begin(token %id, ptr %phi)
call void @print(i32 0)
%0 = call i8 @llvm.coro.suspend(token none, i1 false)
switch i8 %0, label %suspend [i8 0, label %resume
Expand All @@ -24,58 +24,58 @@ resume:
br label %cleanup

cleanup:
%mem = call i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
%mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
call void @free(ptr %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 0)
ret i8* %hdl
call i1 @llvm.coro.end(ptr %hdl, i1 0)
ret ptr %hdl
}

; CHECK-LABEL: @f() !func_sanitize !0 {
; CHECK: call i8* @malloc
; CHECK: @llvm.coro.begin(token %id, i8* %phi)
; CHECK: store void (%f.Frame*)* @f.resume, void (%f.Frame*)** %resume.addr
; CHECK: %[[SEL:.+]] = select i1 %need.alloc, void (%f.Frame*)* @f.destroy, void (%f.Frame*)* @f.cleanup
; CHECK: store void (%f.Frame*)* %[[SEL]], void (%f.Frame*)** %destroy.addr
; CHECK: call ptr @malloc
; CHECK: @llvm.coro.begin(token %id, ptr %phi)
; CHECK: store ptr @f.resume, ptr %hdl
; CHECK: %[[SEL:.+]] = select i1 %need.alloc, ptr @f.destroy, ptr @f.cleanup
; CHECK: store ptr %[[SEL]], ptr %destroy.addr
; CHECK: call void @print(i32 0)
; CHECK-NOT: call void @print(i32 1)
; CHECK-NOT: call void @free(
; CHECK: ret i8* %hdl
; CHECK: ret ptr %hdl

; CHECK-LABEL: @f.resume({{.*}}) {
; CHECK-NOT: call i8* @malloc
; CHECK-NOT: call ptr @malloc
; CHECK-NOT: call void @print(i32 0)
; CHECK: call void @print(i32 1)
; CHECK-NOT: call void @print(i32 0)
; CHECK: call void @free(
; CHECK: ret void

; CHECK-LABEL: @f.destroy({{.*}}) {
; CHECK-NOT: call i8* @malloc
; CHECK-NOT: call ptr @malloc
; CHECK-NOT: call void @print(
; CHECK: call void @free(
; CHECK: ret void

; CHECK-LABEL: @f.cleanup({{.*}}) {
; CHECK-NOT: call i8* @malloc
; CHECK-NOT: call ptr @malloc
; CHECK-NOT: call void @print(
; CHECK-NOT: call void @free(
; CHECK: ret void

declare i8* @llvm.coro.free(token, i8*)
declare ptr @llvm.coro.free(token, ptr)
declare i32 @llvm.coro.size.i32()
declare i8 @llvm.coro.suspend(token, i1)
declare void @llvm.coro.resume(i8*)
declare void @llvm.coro.destroy(i8*)
declare void @llvm.coro.resume(ptr)
declare void @llvm.coro.destroy(ptr)

declare token @llvm.coro.id(i32, i8*, i8*, i8*)
declare token @llvm.coro.id(i32, ptr, ptr, ptr)
declare i1 @llvm.coro.alloc(token)
declare i8* @llvm.coro.begin(token, i8*)
declare i1 @llvm.coro.end(i8*, i1)
declare ptr @llvm.coro.begin(token, ptr)
declare i1 @llvm.coro.end(ptr, i1)

declare noalias i8* @malloc(i32) allockind("alloc,uninitialized") "alloc-family"="malloc"
declare noalias ptr @malloc(i32) allockind("alloc,uninitialized") "alloc-family"="malloc"
declare void @print(i32)
declare void @free(i8*) willreturn allockind("free") "alloc-family"="malloc"
declare void @free(ptr) willreturn allockind("free") "alloc-family"="malloc"

!0 = !{i32 846595819, i8** null}
!0 = !{i32 846595819, ptr null}
61 changes: 28 additions & 33 deletions llvm/test/Transforms/Coroutines/coro-split-02.ll
Original file line number Diff line number Diff line change
@@ -1,70 +1,65 @@
; Tests that coro-split can handle the case when a code after coro.suspend uses
; a value produces between coro.save and coro.suspend (%Result.i19)
; and checks whether stray coro.saves are properly removed
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s

%"struct.std::coroutine_handle" = type { i8* }
%"struct.std::coroutine_handle" = type { ptr }
%"struct.std::coroutine_handle.0" = type { %"struct.std::coroutine_handle" }
%"struct.lean_future<int>::Awaiter" = type { i32, %"struct.std::coroutine_handle.0" }

declare i8* @malloc(i64)
declare ptr @malloc(i64)
declare void @print(i32)

define void @a() presplitcoroutine {
entry:
%ref.tmp7 = alloca %"struct.lean_future<int>::Awaiter", align 8
%testval = alloca i32
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%alloc = call i8* @malloc(i64 16) #3
%vFrame = call noalias nonnull i8* @llvm.coro.begin(token %id, i8* %alloc)
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%alloc = call ptr @malloc(i64 16) #3
%vFrame = call noalias nonnull ptr @llvm.coro.begin(token %id, ptr %alloc)

%save = call token @llvm.coro.save(i8* null)
%Result.i19 = getelementptr inbounds %"struct.lean_future<int>::Awaiter", %"struct.lean_future<int>::Awaiter"* %ref.tmp7, i64 0, i32 0
%save = call token @llvm.coro.save(ptr null)
%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
switch i8 %suspend, label %exit [
i8 0, label %await.ready
i8 1, label %exit
]
await.ready:
%StrayCoroSave = call token @llvm.coro.save(i8* null)
%val = load i32, i32* %Result.i19
%cast = bitcast i32* %testval to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* %cast)
%test = load i32, i32* %testval
%StrayCoroSave = call token @llvm.coro.save(ptr null)
%val = load i32, ptr %ref.tmp7
call void @llvm.lifetime.start.p0(i64 4, ptr %testval)
%test = load i32, ptr %testval
call void @print(i32 %test)
call void @llvm.lifetime.end.p0i8(i64 4, i8* %cast)
call void @llvm.lifetime.end.p0(i64 4, ptr %testval)
call void @print(i32 %val)
br label %exit
exit:
call i1 @llvm.coro.end(i8* null, i1 false)
call i1 @llvm.coro.end(ptr null, i1 false)
ret void
}

; CHECK-LABEL: @a.resume(
; CHECK: %testval = alloca i32
; CHECK: getelementptr inbounds %a.Frame
; CHECK-NEXT: getelementptr inbounds %"struct.lean_future<int>::Awaiter"
; CHECK-NOT: call token @llvm.coro.save(i8* null)
; CHECK-NEXT: %val = load i32, i32* %Result
; CHECK-NEXT: %cast = bitcast i32* %testval to i8*
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* %cast)
; CHECK-NEXT: %test = load i32, i32* %testval
; CHECK-NOT: call token @llvm.coro.save(ptr null)
; CHECK: %val = load i32, ptr %ref.tmp7
; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr %testval)
; CHECK-NEXT: %test = load i32, ptr %testval
; CHECK-NEXT: call void @print(i32 %test)
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* %cast)
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr %testval)
; CHECK-NEXT: call void @print(i32 %val)
; CHECK-NEXT: ret void

declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*)
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr)
declare i1 @llvm.coro.alloc(token) #3
declare noalias nonnull i8* @"\01??2@YAPEAX_K@Z"(i64) local_unnamed_addr
declare noalias nonnull ptr @"\01??2@YAPEAX_K@Z"(i64) local_unnamed_addr
declare i64 @llvm.coro.size.i64() #5
declare i8* @llvm.coro.begin(token, i8* writeonly) #3
declare ptr @llvm.coro.begin(token, ptr writeonly) #3
declare void @"\01?puts@@YAXZZ"(...)
declare token @llvm.coro.save(i8*) #3
declare i8* @llvm.coro.frame() #5
declare token @llvm.coro.save(ptr) #3
declare ptr @llvm.coro.frame() #5
declare i8 @llvm.coro.suspend(token, i1) #3
declare void @"\01??3@YAXPEAX@Z"(i8*) local_unnamed_addr #10
declare i8* @llvm.coro.free(token, i8* nocapture readonly) #2
declare i1 @llvm.coro.end(i8*, i1) #3
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #4
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #4
declare void @"\01??3@YAXPEAX@Z"(ptr) local_unnamed_addr #10
declare ptr @llvm.coro.free(token, ptr nocapture readonly) #2
declare i1 @llvm.coro.end(ptr, i1) #3
declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #4
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #4
51 changes: 25 additions & 26 deletions llvm/test/Transforms/Coroutines/coro-split-eh-00.ll
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
; Tests that coro-split removes cleanup code after coro.end in resume functions
; and retains it in the start function.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s

define i8* @f(i1 %val) presplitcoroutine personality i32 3 {
define ptr @f(i1 %val) presplitcoroutine personality i32 3 {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%hdl = call i8* @llvm.coro.begin(token %id, i8* null)
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%hdl = call ptr @llvm.coro.begin(token %id, ptr null)
call void @print(i32 0)
br i1 %val, label %resume, label %susp

Expand All @@ -17,41 +17,41 @@ resume:
invoke void @print(i32 1) to label %suspend unwind label %lpad

suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 0)
call i1 @llvm.coro.end(ptr %hdl, i1 0)
call void @print(i32 0) ; should not be present in f.resume
ret i8* %hdl
ret ptr %hdl

lpad:
%lpval = landingpad { i8*, i32 }
%lpval = landingpad { ptr, i32 }
cleanup

call void @print(i32 2)
%need.resume = call i1 @llvm.coro.end(i8* null, i1 true)
%need.resume = call i1 @llvm.coro.end(ptr null, i1 true)
br i1 %need.resume, label %eh.resume, label %cleanup.cont

cleanup.cont:
call void @print(i32 3) ; should not be present in f.resume
br label %eh.resume

eh.resume:
resume { i8*, i32 } %lpval
resume { ptr, i32 } %lpval
}

; Verify that start function contains both print calls the one before and after coro.end
; CHECK-LABEL: define i8* @f(
; CHECK-LABEL: define ptr @f(
; CHECK: invoke void @print(i32 1)
; CHECK: to label %AfterCoroEnd unwind label %lpad

; CHECK: AfterCoroEnd:
; CHECK: call void @print(i32 0)
; CHECK: ret i8* %hdl
; CHECK: ret ptr %hdl

; CHECK: lpad:
; CHECK-NEXT: %lpval = landingpad { i8*, i32 }
; CHECK-NEXT: %lpval = landingpad { ptr, i32 }
; CHECK-NEXT: cleanup
; CHECK-NEXT: call void @print(i32 2)
; CHECK-NEXT: call void @print(i32 3)
; CHECK-NEXT: resume { i8*, i32 } %lpval
; CHECK-NEXT: resume { ptr, i32 } %lpval

; VERIFY Resume Parts

Expand All @@ -64,25 +64,24 @@ eh.resume:
; CHECK-NEXT: ret void

; CHECK: lpad:
; CHECK-NEXT: %lpval = landingpad { i8*, i32 }
; CHECK-NEXT: %lpval = landingpad { ptr, i32 }
; CHECK-NEXT: cleanup
; CHECK-NEXT: call void @print(i32 2)
; Checks that the coroutine would be marked as done if it exits in unwinding path.
; CHECK-NEXT: %[[RESUME_ADDR:.+]] = getelementptr inbounds %[[FRAME_TY:.+]], %[[FRAME_TY]]* %FramePtr, i32 0, i32 0
; CHECK-NEXT: store void (%[[FRAME_TY]]*)* null, void (%[[FRAME_TY]]*)** %[[RESUME_ADDR]], align 8
; CHECK-NEXT: resume { i8*, i32 } %lpval
; CHECK-NEXT: store ptr null, ptr %hdl, align 8
; CHECK-NEXT: resume { ptr, i32 } %lpval

declare i8* @llvm.coro.free(token, i8*)
declare ptr @llvm.coro.free(token, ptr)
declare i32 @llvm.coro.size.i32()
declare i8 @llvm.coro.suspend(token, i1)
declare void @llvm.coro.resume(i8*)
declare void @llvm.coro.destroy(i8*)
declare void @llvm.coro.resume(ptr)
declare void @llvm.coro.destroy(ptr)

declare token @llvm.coro.id(i32, i8*, i8*, i8*)
declare i8* @llvm.coro.alloc(token)
declare i8* @llvm.coro.begin(token, i8*)
declare i1 @llvm.coro.end(i8*, i1)
declare token @llvm.coro.id(i32, ptr, ptr, ptr)
declare ptr @llvm.coro.alloc(token)
declare ptr @llvm.coro.begin(token, ptr)
declare i1 @llvm.coro.end(ptr, i1)

declare noalias i8* @malloc(i32)
declare noalias ptr @malloc(i32)
declare void @print(i32)
declare void @free(i8*)
declare void @free(ptr)
39 changes: 19 additions & 20 deletions llvm/test/Transforms/Coroutines/coro-split-eh-01.ll
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
; Tests that coro-split removes cleanup code after coro.end in resume functions
; and retains it in the start function.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s

define i8* @f2(i1 %val) presplitcoroutine personality i32 4 {
define ptr @f2(i1 %val) presplitcoroutine personality i32 4 {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%hdl = call i8* @llvm.coro.begin(token %id, i8* null)
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%hdl = call ptr @llvm.coro.begin(token %id, ptr null)
call void @print(i32 0)
br i1 %val, label %resume, label %susp

Expand All @@ -17,14 +17,14 @@ resume:
invoke void @print(i32 1) to label %suspend unwind label %lpad

suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 0)
call i1 @llvm.coro.end(ptr %hdl, i1 0)
call void @print(i32 0) ; should not be present in f.resume
ret i8* %hdl
ret ptr %hdl

lpad:
%tok = cleanuppad within none []
call void @print(i32 2)
%unused = call i1 @llvm.coro.end(i8* null, i1 true) [ "funclet"(token %tok) ]
%unused = call i1 @llvm.coro.end(ptr null, i1 true) [ "funclet"(token %tok) ]
cleanupret from %tok unwind label %cleanup.cont

cleanup.cont:
Expand All @@ -34,13 +34,13 @@ cleanup.cont:
}

; Verify that start function contains both print calls the one before and after coro.end
; CHECK-LABEL: define i8* @f2(
; CHECK-LABEL: define ptr @f2(
; CHECK: invoke void @print(i32 1)
; CHECK: to label %AfterCoroEnd unwind label %lpad

; CHECK: AfterCoroEnd:
; CHECK: call void @print(i32 0)
; CHECK: ret i8* %hdl
; CHECK: ret ptr %hdl

; CHECK: lpad:
; CHECK-NEXT: %tok = cleanuppad within none []
Expand All @@ -62,21 +62,20 @@ cleanup.cont:
; CHECK-NEXT: %tok = cleanuppad within none []
; CHECK-NEXT: call void @print(i32 2)
; Checks that the coroutine would be marked as done if it exits in unwinding path.
; CHECK-NEXT: %[[RESUME_ADDR:.+]] = getelementptr inbounds %[[FRAME_TY:.+]], %[[FRAME_TY]]* %FramePtr, i32 0, i32 0
; CHECK-NEXT: store void (%[[FRAME_TY]]*)* null, void (%[[FRAME_TY]]*)** %[[RESUME_ADDR]], align 8
; CHECK-NEXT: store ptr null, ptr %hdl, align 8
; CHECK-NEXT: cleanupret from %tok unwind to caller

declare i8* @llvm.coro.free(token, i8*)
declare ptr @llvm.coro.free(token, ptr)
declare i32 @llvm.coro.size.i32()
declare i8 @llvm.coro.suspend(token, i1)
declare void @llvm.coro.resume(i8*)
declare void @llvm.coro.destroy(i8*)
declare void @llvm.coro.resume(ptr)
declare void @llvm.coro.destroy(ptr)

declare token @llvm.coro.id(i32, i8*, i8*, i8*)
declare i8* @llvm.coro.alloc(token)
declare i8* @llvm.coro.begin(token, i8*)
declare i1 @llvm.coro.end(i8*, i1)
declare token @llvm.coro.id(i32, ptr, ptr, ptr)
declare ptr @llvm.coro.alloc(token)
declare ptr @llvm.coro.begin(token, ptr)
declare i1 @llvm.coro.end(ptr, i1)

declare noalias i8* @malloc(i32)
declare noalias ptr @malloc(i32)
declare void @print(i32)
declare void @free(i8*)
declare void @free(ptr)
54 changes: 27 additions & 27 deletions llvm/test/Transforms/Coroutines/coro-split-hidden.ll
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
; Tests that coro-split can convert functions with hidden visibility.
; These may be generated by a frontend such as Clang, when inlining with
; '-fvisibility-inlines-hidden'.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s

define hidden i8* @f() presplitcoroutine {
define hidden ptr @f() presplitcoroutine {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%need.alloc = call i1 @llvm.coro.alloc(token %id)
br i1 %need.alloc, label %dyn.alloc, label %begin

dyn.alloc:
%size = call i32 @llvm.coro.size.i32()
%alloc = call i8* @malloc(i32 %size)
%alloc = call ptr @malloc(i32 %size)
br label %begin

begin:
%phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ]
%hdl = call i8* @llvm.coro.begin(token %id, i8* %phi)
%phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
%hdl = call ptr @llvm.coro.begin(token %id, ptr %phi)
call void @print(i32 0)
%0 = call i8 @llvm.coro.suspend(token none, i1 false)
switch i8 %0, label %suspend [i8 0, label %resume
Expand All @@ -26,56 +26,56 @@ resume:
br label %cleanup

cleanup:
%mem = call i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
%mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
call void @free(ptr %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 0)
ret i8* %hdl
call i1 @llvm.coro.end(ptr %hdl, i1 0)
ret ptr %hdl
}

; CHECK-LABEL: hidden{{.*}}@f(
; CHECK: call i8* @malloc
; CHECK: @llvm.coro.begin(token %id, i8* %phi)
; CHECK: store void (%f.Frame*)* @f.resume, void (%f.Frame*)** %resume.addr
; CHECK: %[[SEL:.+]] = select i1 %need.alloc, void (%f.Frame*)* @f.destroy, void (%f.Frame*)* @f.cleanup
; CHECK: store void (%f.Frame*)* %[[SEL]], void (%f.Frame*)** %destroy.addr
; CHECK: call ptr @malloc
; CHECK: @llvm.coro.begin(token %id, ptr %phi)
; CHECK: store ptr @f.resume, ptr %hdl
; CHECK: %[[SEL:.+]] = select i1 %need.alloc, ptr @f.destroy, ptr @f.cleanup
; CHECK: store ptr %[[SEL]], ptr %destroy.addr
; CHECK: call void @print(i32 0)
; CHECK-NOT: call void @print(i32 1)
; CHECK-NOT: call void @free(
; CHECK: ret i8* %hdl
; CHECK: ret ptr %hdl

; CHECK-LABEL: internal{{.*}}@f.resume(
; CHECK-NOT: call i8* @malloc
; CHECK-NOT: call ptr @malloc
; CHECK-NOT: call void @print(i32 0)
; CHECK: call void @print(i32 1)
; CHECK-NOT: call void @print(i32 0)
; CHECK: call void @free(
; CHECK: ret void

; CHECK-LABEL: internal{{.*}}@f.destroy(
; CHECK-NOT: call i8* @malloc
; CHECK-NOT: call ptr @malloc
; CHECK-NOT: call void @print(
; CHECK: call void @free(
; CHECK: ret void

; CHECK-LABEL: internal{{.*}}@f.cleanup(
; CHECK-NOT: call i8* @malloc
; CHECK-NOT: call ptr @malloc
; CHECK-NOT: call void @print(
; CHECK-NOT: call void @free(
; CHECK: ret void

declare i8* @llvm.coro.free(token, i8*)
declare ptr @llvm.coro.free(token, ptr)
declare i32 @llvm.coro.size.i32()
declare i8 @llvm.coro.suspend(token, i1)
declare void @llvm.coro.resume(i8*)
declare void @llvm.coro.destroy(i8*)
declare void @llvm.coro.resume(ptr)
declare void @llvm.coro.destroy(ptr)

declare token @llvm.coro.id(i32, i8*, i8*, i8*)
declare token @llvm.coro.id(i32, ptr, ptr, ptr)
declare i1 @llvm.coro.alloc(token)
declare i8* @llvm.coro.begin(token, i8*)
declare i1 @llvm.coro.end(i8*, i1)
declare ptr @llvm.coro.begin(token, ptr)
declare i1 @llvm.coro.end(ptr, i1)

declare noalias i8* @malloc(i32) allockind("alloc,uninitialized")
declare noalias ptr @malloc(i32) allockind("alloc,uninitialized")
declare void @print(i32)
declare void @free(i8*) willreturn allockind("free")
declare void @free(ptr) willreturn allockind("free")
85 changes: 39 additions & 46 deletions llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-01.ll
Original file line number Diff line number Diff line change
@@ -1,103 +1,96 @@
; Tests that coro-split will optimize the lifetime.start maker of each local variable,
; sink them to the places after the suspend block.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s

; CHECK: %a.Frame = type { void (%a.Frame*)*, void (%a.Frame*)*, %"struct.lean_future<int>::Awaiter", i1 }
; CHECK: %a_optnone.Frame = type { void (%a_optnone.Frame*)*, void (%a_optnone.Frame*)*, %"struct.lean_future<int>::Awaiter", i8*, i32*, i32, i1 }
; CHECK: %a.Frame = type { ptr, ptr, i1 }
; CHECK: %a_optnone.Frame = type { ptr, ptr, i32, i1 }

%"struct.std::coroutine_handle" = type { i8* }
%"struct.std::coroutine_handle" = type { ptr }
%"struct.std::coroutine_handle.0" = type { %"struct.std::coroutine_handle" }
%"struct.lean_future<int>::Awaiter" = type { i32, %"struct.std::coroutine_handle.0" }

declare i8* @malloc(i64)
declare ptr @malloc(i64)
declare void @print(i32)

define void @a() presplitcoroutine {
entry:
%ref.tmp7 = alloca %"struct.lean_future<int>::Awaiter", align 8
%testval = alloca i32
%cast = bitcast i32* %testval to i8*
; lifetime of %testval starts here, but not used until await.ready.
call void @llvm.lifetime.start.p0i8(i64 4, i8* %cast)
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%alloc = call i8* @malloc(i64 16) #3
%vFrame = call noalias nonnull i8* @llvm.coro.begin(token %id, i8* %alloc)
call void @llvm.lifetime.start.p0(i64 4, ptr %testval)
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%alloc = call ptr @malloc(i64 16) #3
%vFrame = call noalias nonnull ptr @llvm.coro.begin(token %id, ptr %alloc)

%save = call token @llvm.coro.save(i8* null)
%Result.i19 = getelementptr inbounds %"struct.lean_future<int>::Awaiter", %"struct.lean_future<int>::Awaiter"* %ref.tmp7, i64 0, i32 0
%save = call token @llvm.coro.save(ptr null)
%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
switch i8 %suspend, label %exit [
i8 0, label %await.ready
i8 1, label %exit
]
await.ready:
%StrayCoroSave = call token @llvm.coro.save(i8* null)
%val = load i32, i32* %Result.i19
%test = load i32, i32* %testval
%StrayCoroSave = call token @llvm.coro.save(ptr null)
%val = load i32, ptr %ref.tmp7
%test = load i32, ptr %testval
call void @print(i32 %test)
call void @llvm.lifetime.end.p0i8(i64 4, i8* %cast)
call void @llvm.lifetime.end.p0(i64 4, ptr %testval)
call void @print(i32 %val)
br label %exit
exit:
call i1 @llvm.coro.end(i8* null, i1 false)
call i1 @llvm.coro.end(ptr null, i1 false)
ret void
}

; CHECK-LABEL: @a.resume(
; CHECK: %testval = alloca i32, align 4
; CHECK-NEXT: getelementptr inbounds %a.Frame
; CHECK-NEXT: %0 = bitcast i32* %testval to i8*
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 4, i8* %0)
; CHECK-NEXT: getelementptr inbounds %"struct.lean_future<int>::Awaiter"
; CHECK-NEXT: %val = load i32, i32* %Result
; CHECK-NEXT: %test = load i32, i32* %testval
; CHECK: call void @llvm.lifetime.start.p0(i64 4, ptr %testval)
; CHECK-NEXT: %val = load i32, ptr %ref.tmp7
; CHECK-NEXT: %test = load i32, ptr %testval
; CHECK-NEXT: call void @print(i32 %test)
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 4, i8* %0)
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr %testval)
; CHECK-NEXT: call void @print(i32 %val)
; CHECK-NEXT: ret void

define void @a_optnone() presplitcoroutine optnone noinline {
entry:
%ref.tmp7 = alloca %"struct.lean_future<int>::Awaiter", align 8
%testval = alloca i32
%cast = bitcast i32* %testval to i8*
; lifetime of %testval starts here, but not used until await.ready.
call void @llvm.lifetime.start.p0i8(i64 4, i8* %cast)
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%alloc = call i8* @malloc(i64 16) #3
%vFrame = call noalias nonnull i8* @llvm.coro.begin(token %id, i8* %alloc)
call void @llvm.lifetime.start.p0(i64 4, ptr %testval)
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%alloc = call ptr @malloc(i64 16) #3
%vFrame = call noalias nonnull ptr @llvm.coro.begin(token %id, ptr %alloc)

%save = call token @llvm.coro.save(i8* null)
%Result.i19 = getelementptr inbounds %"struct.lean_future<int>::Awaiter", %"struct.lean_future<int>::Awaiter"* %ref.tmp7, i64 0, i32 0
%save = call token @llvm.coro.save(ptr null)
%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
switch i8 %suspend, label %exit [
i8 0, label %await.ready
i8 1, label %exit
]
await.ready:
%StrayCoroSave = call token @llvm.coro.save(i8* null)
%val = load i32, i32* %Result.i19
%test = load i32, i32* %testval
%StrayCoroSave = call token @llvm.coro.save(ptr null)
%val = load i32, ptr %ref.tmp7
%test = load i32, ptr %testval
call void @print(i32 %test)
call void @llvm.lifetime.end.p0i8(i64 4, i8* %cast)
call void @llvm.lifetime.end.p0(i64 4, ptr %testval)
call void @print(i32 %val)
br label %exit
exit:
call i1 @llvm.coro.end(i8* null, i1 false)
call i1 @llvm.coro.end(ptr null, i1 false)
ret void
}

declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*)
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr)
declare i1 @llvm.coro.alloc(token) #3
declare noalias nonnull i8* @"\01??2@YAPEAX_K@Z"(i64) local_unnamed_addr
declare noalias nonnull ptr @"\01??2@YAPEAX_K@Z"(i64) local_unnamed_addr
declare i64 @llvm.coro.size.i64() #5
declare i8* @llvm.coro.begin(token, i8* writeonly) #3
declare ptr @llvm.coro.begin(token, ptr writeonly) #3
declare void @"\01?puts@@YAXZZ"(...)
declare token @llvm.coro.save(i8*) #3
declare i8* @llvm.coro.frame() #5
declare token @llvm.coro.save(ptr) #3
declare ptr @llvm.coro.frame() #5
declare i8 @llvm.coro.suspend(token, i1) #3
declare void @"\01??3@YAXPEAX@Z"(i8*) local_unnamed_addr #10
declare i8* @llvm.coro.free(token, i8* nocapture readonly) #2
declare i1 @llvm.coro.end(i8*, i1) #3
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #4
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #4
declare void @"\01??3@YAXPEAX@Z"(ptr) local_unnamed_addr #10
declare ptr @llvm.coro.free(token, ptr nocapture readonly) #2
declare i1 @llvm.coro.end(ptr, i1) #3
declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #4
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #4
62 changes: 28 additions & 34 deletions llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-03.ll
Original file line number Diff line number Diff line change
@@ -1,70 +1,64 @@
; Corresponding to coro-split-sink-lifetime-01.ll. This file tests that whether the CoroFrame
; pass knows the operand of lifetime.start intrinsic may be GEP as well.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s

%"struct.std::coroutine_handle" = type { i8* }
%"struct.std::coroutine_handle" = type { ptr }
%"struct.std::coroutine_handle.0" = type { %"struct.std::coroutine_handle" }
%"struct.lean_future<int>::Awaiter" = type { i32, %"struct.std::coroutine_handle.0" }

declare i8* @malloc(i64)
declare ptr @malloc(i64)
declare void @print(i32)

%i8.array = type { [100 x i8] }
declare void @consume.i8.array(%i8.array*)
declare void @consume.i8.array(ptr)

define void @a.gep() presplitcoroutine {
entry:
%ref.tmp7 = alloca %"struct.lean_future<int>::Awaiter", align 8
%testval = alloca %i8.array
%cast = getelementptr inbounds %i8.array, %i8.array* %testval, i64 0, i32 0, i64 0
; lifetime of %testval starts here, but not used until await.ready.
call void @llvm.lifetime.start.p0i8(i64 100, i8* %cast)
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%alloc = call i8* @malloc(i64 16) #3
%vFrame = call noalias nonnull i8* @llvm.coro.begin(token %id, i8* %alloc)
call void @llvm.lifetime.start.p0(i64 100, ptr %testval)
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%alloc = call ptr @malloc(i64 16) #3
%vFrame = call noalias nonnull ptr @llvm.coro.begin(token %id, ptr %alloc)

%save = call token @llvm.coro.save(i8* null)
%Result.i19 = getelementptr inbounds %"struct.lean_future<int>::Awaiter", %"struct.lean_future<int>::Awaiter"* %ref.tmp7, i64 0, i32 0
%save = call token @llvm.coro.save(ptr null)
%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
switch i8 %suspend, label %exit [
i8 0, label %await.ready
i8 1, label %exit
]
await.ready:
%StrayCoroSave = call token @llvm.coro.save(i8* null)
%val = load i32, i32* %Result.i19
call void @consume.i8.array(%i8.array* %testval)
call void @llvm.lifetime.end.p0i8(i64 100, i8* %cast)
%StrayCoroSave = call token @llvm.coro.save(ptr null)
%val = load i32, ptr %ref.tmp7
call void @consume.i8.array(ptr %testval)
call void @llvm.lifetime.end.p0(i64 100, ptr %testval)
call void @print(i32 %val)
br label %exit
exit:
call i1 @llvm.coro.end(i8* null, i1 false)
call i1 @llvm.coro.end(ptr null, i1 false)
ret void
}
; CHECK-LABEL: @a.gep.resume(
; CHECK: %testval = alloca %i8.array
; CHECK-NEXT: getelementptr inbounds %a.gep.Frame
; CHECK-NEXT: %0 = bitcast %i8.array* %testval to i8*
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 100, i8* %0)
; CHECK-NEXT: getelementptr inbounds %"struct.lean_future<int>::Awaiter"
; CHECK-NEXT: getelementptr inbounds %i8.array, %i8.array* %testval
; CHECK-NEXT: %val = load i32, i32* %Result
; CHECK-NEXT: call void @consume.i8.array(%i8.array* %testval)
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 100, i8* %cast1)
; CHECK: call void @llvm.lifetime.start.p0(i64 100, ptr %testval)
; CHECK-NEXT: %val = load i32, ptr %ref.tmp7
; CHECK-NEXT: call void @consume.i8.array(ptr %testval)
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 100, ptr %testval)
; CHECK-NEXT: call void @print(i32 %val)
; CHECK-NEXT: ret void

declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*)
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr)
declare i1 @llvm.coro.alloc(token) #3
declare noalias nonnull i8* @"\01??2@YAPEAX_K@Z"(i64) local_unnamed_addr
declare noalias nonnull ptr @"\01??2@YAPEAX_K@Z"(i64) local_unnamed_addr
declare i64 @llvm.coro.size.i64() #5
declare i8* @llvm.coro.begin(token, i8* writeonly) #3
declare ptr @llvm.coro.begin(token, ptr writeonly) #3
declare void @"\01?puts@@YAXZZ"(...)
declare token @llvm.coro.save(i8*) #3
declare i8* @llvm.coro.frame() #5
declare token @llvm.coro.save(ptr) #3
declare ptr @llvm.coro.frame() #5
declare i8 @llvm.coro.suspend(token, i1) #3
declare void @"\01??3@YAXPEAX@Z"(i8*) local_unnamed_addr #10
declare i8* @llvm.coro.free(token, i8* nocapture readonly) #2
declare i1 @llvm.coro.end(i8*, i1) #3
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #4
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #4
declare void @"\01??3@YAXPEAX@Z"(ptr) local_unnamed_addr #10
declare ptr @llvm.coro.free(token, ptr nocapture readonly) #2
declare i1 @llvm.coro.end(ptr, i1) #3
declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #4
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #4
57 changes: 27 additions & 30 deletions llvm/test/Transforms/Coroutines/coro-split-sink-lifetime-04.ll
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
; Tests that coro-split will optimize the lifetime.start maker of each local variable,
; sink them to the places after the suspend block.
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s

%"struct.std::coroutine_handle" = type { i8* }
%"struct.std::coroutine_handle" = type { ptr }
%"struct.std::coroutine_handle.0" = type { %"struct.std::coroutine_handle" }
%"struct.lean_future<int>::Awaiter" = type { i32, %"struct.std::coroutine_handle.0" }

declare i8* @malloc(i64)
declare ptr @malloc(i64)
declare void @print(i32)
declare void @consume.i8(i8)

Expand All @@ -15,55 +15,52 @@ entry:
%ref.tmp7 = alloca %"struct.lean_future<int>::Awaiter", align 8
%testval = alloca i8
; lifetime of %testval starts here, but not used until await.ready.
call void @llvm.lifetime.start.p0i8(i64 1, i8* %testval)
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%alloc = call i8* @malloc(i64 16) #3
%vFrame = call noalias nonnull i8* @llvm.coro.begin(token %id, i8* %alloc)
call void @llvm.lifetime.start.p0(i64 1, ptr %testval)
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%alloc = call ptr @malloc(i64 16) #3
%vFrame = call noalias nonnull ptr @llvm.coro.begin(token %id, ptr %alloc)

%save = call token @llvm.coro.save(i8* null)
%Result.i19 = getelementptr inbounds %"struct.lean_future<int>::Awaiter", %"struct.lean_future<int>::Awaiter"* %ref.tmp7, i64 0, i32 0
%save = call token @llvm.coro.save(ptr null)
%suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
switch i8 %suspend, label %exit [
i8 0, label %await.ready
i8 1, label %exit
]
await.ready:
%StrayCoroSave = call token @llvm.coro.save(i8* null)
%val = load i32, i32* %Result.i19
%test = load i8, i8* %testval
%StrayCoroSave = call token @llvm.coro.save(ptr null)
%val = load i32, ptr %ref.tmp7
%test = load i8, ptr %testval
call void @consume.i8(i8 %test)
call void @llvm.lifetime.end.p0i8(i64 1, i8* %testval)
call void @llvm.lifetime.end.p0(i64 1, ptr %testval)
call void @print(i32 %val)
br label %exit
exit:
call i1 @llvm.coro.end(i8* null, i1 false)
call i1 @llvm.coro.end(ptr null, i1 false)
ret void
}

; CHECK-LABEL: @a.resume(
; CHECK: %testval = alloca i8, align 1
; CHECK-NEXT: getelementptr inbounds %a.Frame
; CHECK-NEXT: call void @llvm.lifetime.start.p0i8(i64 1, i8* %testval)
; CHECK-NEXT: getelementptr inbounds %"struct.lean_future<int>::Awaiter"
; CHECK-NEXT: %val = load i32, i32* %Result
; CHECK-NEXT: %test = load i8, i8* %testval
; CHECK: call void @llvm.lifetime.start.p0(i64 1, ptr %testval)
; CHECK-NEXT: %val = load i32, ptr %ref.tmp7
; CHECK-NEXT: %test = load i8, ptr %testval
; CHECK-NEXT: call void @consume.i8(i8 %test)
; CHECK-NEXT: call void @llvm.lifetime.end.p0i8(i64 1, i8* %testval)
; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 1, ptr %testval)
; CHECK-NEXT: call void @print(i32 %val)
; CHECK-NEXT: ret void


declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*)
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr)
declare i1 @llvm.coro.alloc(token) #3
declare noalias nonnull i8* @"\01??2@YAPEAX_K@Z"(i64) local_unnamed_addr
declare noalias nonnull ptr @"\01??2@YAPEAX_K@Z"(i64) local_unnamed_addr
declare i64 @llvm.coro.size.i64() #5
declare i8* @llvm.coro.begin(token, i8* writeonly) #3
declare ptr @llvm.coro.begin(token, ptr writeonly) #3
declare void @"\01?puts@@YAXZZ"(...)
declare token @llvm.coro.save(i8*) #3
declare i8* @llvm.coro.frame() #5
declare token @llvm.coro.save(ptr) #3
declare ptr @llvm.coro.frame() #5
declare i8 @llvm.coro.suspend(token, i1) #3
declare void @"\01??3@YAXPEAX@Z"(i8*) local_unnamed_addr #10
declare i8* @llvm.coro.free(token, i8* nocapture readonly) #2
declare i1 @llvm.coro.end(i8*, i1) #3
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #4
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #4
declare void @"\01??3@YAXPEAX@Z"(ptr) local_unnamed_addr #10
declare ptr @llvm.coro.free(token, ptr nocapture readonly) #2
declare i1 @llvm.coro.end(ptr, i1) #3
declare void @llvm.lifetime.start.p0(i64, ptr nocapture) #4
declare void @llvm.lifetime.end.p0(i64, ptr nocapture) #4
77 changes: 33 additions & 44 deletions llvm/test/Transforms/Coroutines/coro-zero-alloca.ll
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
; RUN: opt -opaque-pointers=0 < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s

declare i8* @malloc(i64)
declare void @free(i8*)
declare void @usePointer(i8*)
declare void @usePointer2([0 x i8]*)
declare ptr @malloc(i64)
declare void @free(ptr)
declare void @usePointer(ptr)
declare void @usePointer2(ptr)

declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*)
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr)
declare i64 @llvm.coro.size.i64()
declare i8* @llvm.coro.begin(token, i8* writeonly)
declare ptr @llvm.coro.begin(token, ptr writeonly)
declare i8 @llvm.coro.suspend(token, i1)
declare i1 @llvm.coro.end(i8*, i1)
declare i8* @llvm.coro.free(token, i8* nocapture readonly)
declare token @llvm.coro.save(i8*)
declare i1 @llvm.coro.end(ptr, i1)
declare ptr @llvm.coro.free(token, ptr nocapture readonly)
declare token @llvm.coro.save(ptr)

define void @foo() presplitcoroutine {
entry:
Expand All @@ -21,58 +21,47 @@ entry:
%a3 = alloca [0 x i8]
%a4 = alloca i16
%a5 = alloca [0 x i8]
%a0.cast = bitcast [0 x i8]* %a0 to i8*
%a1.cast = bitcast i32* %a1 to i8*
%a2.cast = bitcast [0 x i8]* %a2 to i8*
%a3.cast = bitcast [0 x i8]* %a3 to i8*
%a4.cast = bitcast i16* %a4 to i8*
%coro.id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
%coro.id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%coro.size = call i64 @llvm.coro.size.i64()
%coro.alloc = call i8* @malloc(i64 %coro.size)
%coro.state = call i8* @llvm.coro.begin(token %coro.id, i8* %coro.alloc)
%coro.save = call token @llvm.coro.save(i8* %coro.state)
%coro.alloc = call ptr @malloc(i64 %coro.size)
%coro.state = call ptr @llvm.coro.begin(token %coro.id, ptr %coro.alloc)
%coro.save = call token @llvm.coro.save(ptr %coro.state)
%call.suspend = call i8 @llvm.coro.suspend(token %coro.save, i1 false)
switch i8 %call.suspend, label %suspend [
i8 0, label %wakeup
i8 1, label %cleanup
]

wakeup: ; preds = %entry
call void @usePointer(i8* %a0.cast)
call void @usePointer(i8* %a1.cast)
call void @usePointer(i8* %a2.cast)
call void @usePointer(i8* %a3.cast)
call void @usePointer(i8* %a4.cast)
call void @usePointer2([0 x i8]* %a5)
call void @usePointer(ptr %a0)
call void @usePointer(ptr %a1)
call void @usePointer(ptr %a2)
call void @usePointer(ptr %a3)
call void @usePointer(ptr %a4)
call void @usePointer2(ptr %a5)
br label %cleanup

suspend: ; preds = %cleanup, %entry
%unused = call i1 @llvm.coro.end(i8* %coro.state, i1 false)
%unused = call i1 @llvm.coro.end(ptr %coro.state, i1 false)
ret void

cleanup: ; preds = %wakeup, %entry
%coro.memFree = call i8* @llvm.coro.free(token %coro.id, i8* %coro.state)
call void @free(i8* %coro.memFree)
%coro.memFree = call ptr @llvm.coro.free(token %coro.id, ptr %coro.state)
call void @free(ptr %coro.memFree)
br label %suspend
}

; CHECK: %foo.Frame = type { void (%foo.Frame*)*, void (%foo.Frame*)*, i32, i16, i1 }
; CHECK: %foo.Frame = type { ptr, ptr, i32, i16, i1 }

; CHECK-LABEL: @foo.resume(
; CHECK-NEXT: entry.resume:
; CHECK-NEXT: [[VFRAME:%.*]] = bitcast %foo.Frame* [[FRAMEPTR:%.*]] to i8*
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[FOO_FRAME:%.*]], %foo.Frame* [[FRAMEPTR]], i32 0, i32 0
; CHECK-NEXT: [[A0_RELOAD_ADDR:%.*]] = bitcast void (%foo.Frame*)** [[TMP0]] to [0 x i8]*
; CHECK-NEXT: [[A1_RELOAD_ADDR:%.*]] = getelementptr inbounds [[FOO_FRAME]], %foo.Frame* [[FRAMEPTR]], i32 0, i32 2
; CHECK-NEXT: [[A4_RELOAD_ADDR:%.*]] = getelementptr inbounds [[FOO_FRAME]], %foo.Frame* [[FRAMEPTR]], i32 0, i32 3
; CHECK-NEXT: [[A4_CAST5:%.*]] = bitcast i16* [[A4_RELOAD_ADDR]] to i8*
; CHECK-NEXT: [[A3_CAST4:%.*]] = bitcast [0 x i8]* [[A0_RELOAD_ADDR]] to i8*
; CHECK-NEXT: [[A1_CAST2:%.*]] = bitcast i32* [[A1_RELOAD_ADDR]] to i8*
; CHECK-NEXT: call void @usePointer(i8* [[A3_CAST4]])
; CHECK-NEXT: call void @usePointer(i8* [[A1_CAST2]])
; CHECK-NEXT: call void @usePointer(i8* [[A3_CAST4]])
; CHECK-NEXT: call void @usePointer(i8* [[A3_CAST4]])
; CHECK-NEXT: call void @usePointer(i8* [[A4_CAST5]])
; CHECK-NEXT: call void @usePointer2([0 x i8]* [[A0_RELOAD_ADDR]])
; CHECK-NEXT: call void @free(i8* [[VFRAME]])
; CHECK-NEXT: [[A1_RELOAD_ADDR:%.*]] = getelementptr inbounds [[FOO_FRAME:%foo.Frame]], ptr [[FRAMEPTR:%.*]], i32 0, i32 2
; CHECK-NEXT: [[A4_RELOAD_ADDR:%.*]] = getelementptr inbounds [[FOO_FRAME]], ptr [[FRAMEPTR]], i32 0, i32 3
; CHECK-NEXT: call void @usePointer(ptr [[FRAMEPTR]])
; CHECK-NEXT: call void @usePointer(ptr [[A1_RELOAD_ADDR]])
; CHECK-NEXT: call void @usePointer(ptr [[FRAMEPTR]])
; CHECK-NEXT: call void @usePointer(ptr [[FRAMEPTR]])
; CHECK-NEXT: call void @usePointer(ptr [[A4_RELOAD_ADDR]])
; CHECK-NEXT: call void @usePointer2(ptr [[FRAMEPTR]])
; CHECK-NEXT: call void @free(ptr [[FRAMEPTR]])
; CHECK-NEXT: ret void
Loading