llvm/test/Transforms/Coroutines/coro-split-eh.ll
Gor Nishanov 2d33745172 [coroutines] Add handling for unwind coro.ends
Summary:
The purpose of coro.end intrinsic is to allow frontends to mark the cleanup and
other code that is only relevant during the initial invocation of the coroutine
and should not be present in resume and destroy parts.

In landing pads coro.end is replaced with an appropriate instruction to unwind to
caller. The handling of coro.end differs depending on whether the target is
using landingpad or WinEH exception model.

For landingpad based exception model, it is expected that frontend uses the
`coro.end`_ intrinsic as follows:

```
    ehcleanup:
      %InResumePart = call i1 @llvm.coro.end(i8* null, i1 true)
      br i1 %InResumePart, label %eh.resume, label %cleanup.cont

    cleanup.cont:
      ; rest of the cleanup

    eh.resume:
      %exn = load i8*, i8** %exn.slot, align 8
      %sel = load i32, i32* %ehselector.slot, align 4
      %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn, 0
      %lpad.val29 = insertvalue { i8*, i32 } %lpad.val, i32 %sel, 1
      resume { i8*, i32 } %lpad.val29

```
The `CoroSpit` pass replaces `coro.end` with ``True`` in the resume functions,
thus leading to immediate unwind to the caller, whereas in start function it
is replaced with ``False``, thus allowing to proceed to the rest of the cleanup
code that is only needed during initial invocation of the coroutine.

For Windows Exception handling model, a frontend should attach a funclet bundle
referring to an enclosing cleanuppad as follows:

```
    ehcleanup:
      %tok = cleanuppad within none []
      %unused = call i1 @llvm.coro.end(i8* null, i1 true) [ "funclet"(token %tok) ]
      cleanupret from %tok unwind label %RestOfTheCleanup
```

The `CoroSplit` pass, if the funclet bundle is present, will insert
``cleanupret from %tok unwind to caller`` before
the `coro.end`_ intrinsic and will remove the rest of the block.

Reviewers: majnemer

Reviewed By: majnemer

Subscribers: llvm-commits, mehdi_amini

Differential Revision: https://reviews.llvm.org/D25543

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@297223 91177308-0d34-0410-b5e6-96231b3b80d8
2017-03-07 21:00:54 +00:00

146 lines
4.4 KiB
LLVM

; Tests that coro-split removes cleanup code after coro.end in resume functions
; and retains it in the start function.
; RUN: opt < %s -coro-split -S | FileCheck %s
define i8* @f(i1 %val) "coroutine.presplit"="1" 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)
call void @print(i32 0)
br i1 %val, label %resume, label %susp
susp:
%0 = call i8 @llvm.coro.suspend(token none, i1 false)
switch i8 %0, label %suspend [i8 0, label %resume
i8 1, label %suspend]
resume:
invoke void @print(i32 1) to label %suspend unwind label %lpad
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 0)
call void @print(i32 0) ; should not be present in f.resume
ret i8* %hdl
lpad:
%lpval = landingpad { i8*, i32 }
cleanup
call void @print(i32 2)
%need.resume = call i1 @llvm.coro.end(i8* 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
}
; Verify that start function contains both print calls the one before and after coro.end
; CHECK-LABEL: define i8* @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: lpad:
; CHECK-NEXT: %lpval = landingpad { i8*, i32 }
; CHECK-NEXT: cleanup
; CHECK-NEXT: call void @print(i32 2)
; CHECK-NEXT: call void @print(i32 3)
; CHECK-NEXT: resume { i8*, i32 } %lpval
define i8* @f2(i1 %val) "coroutine.presplit"="1" 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)
call void @print(i32 0)
br i1 %val, label %resume, label %susp
susp:
%0 = call i8 @llvm.coro.suspend(token none, i1 false)
switch i8 %0, label %suspend [i8 0, label %resume
i8 1, label %suspend]
resume:
invoke void @print(i32 1) to label %suspend unwind label %lpad
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 0)
call void @print(i32 0) ; should not be present in f.resume
ret i8* %hdl
lpad:
%tok = cleanuppad within none []
call void @print(i32 2)
%unused = call i1 @llvm.coro.end(i8* null, i1 true) [ "funclet"(token %tok) ]
cleanupret from %tok unwind label %cleanup.cont
cleanup.cont:
%tok2 = cleanuppad within none []
call void @print(i32 3) ; should not be present in f.resume
cleanupret from %tok2 unwind to caller
}
; Verify that start function contains both print calls the one before and after coro.end
; CHECK-LABEL: define i8* @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: lpad:
; CHECK-NEXT: %tok = cleanuppad within none []
; CHECK-NEXT: call void @print(i32 2)
; CHECK-NEXT: call void @print(i32 3)
; CHECK-NEXT: cleanupret from %tok unwind to caller
; VERIFY Resume Parts
; Verify that resume function does not contains both print calls appearing after coro.end
; CHECK-LABEL: define internal fastcc void @f.resume
; CHECK: invoke void @print(i32 1)
; CHECK: to label %CoroEnd unwind label %lpad
; CHECK: CoroEnd:
; CHECK-NEXT: ret void
; CHECK: lpad:
; CHECK-NEXT: %lpval = landingpad { i8*, i32 }
; CHECK-NEXT: cleanup
; CHECK-NEXT: call void @print(i32 2)
; CHECK-NEXT: resume { i8*, i32 } %lpval
; Verify that resume function does not contains both print calls appearing after coro.end
; CHECK-LABEL: define internal fastcc void @f2.resume
; CHECK: invoke void @print(i32 1)
; CHECK: to label %CoroEnd unwind label %lpad
; CHECK: CoroEnd:
; CHECK-NEXT: ret void
; CHECK: lpad:
; CHECK-NEXT: %tok = cleanuppad within none []
; CHECK-NEXT: call void @print(i32 2)
; CHECK-NEXT: cleanupret from %tok unwind to caller
declare i8* @llvm.coro.free(token, i8*)
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 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 noalias i8* @malloc(i32)
declare void @print(i32)
declare void @free(i8*)