llvm/llvm/test/Transforms/Coroutines/coro-async-unreachable.ll

; RUN: opt < %s -passes='default<O2>' -S | FileCheck --check-prefixes=CHECK %s

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

%async.task = type { i64 }
%async.actor = type { i64 }
%async.fp = type <{ i32, i32 }>

%async.ctxt = type { ptr, ptr }

; The async callee.
@my_other_async_function_fp = external global <{ i32, i32 }>
declare void @my_other_async_function(ptr %async.ctxt)

; Function that implements the dispatch to the callee function.
define swiftcc void @my_async_function.my_other_async_function_fp.apply(ptr %fnPtr, ptr %async.ctxt, ptr %task, ptr %actor) {
  tail call swiftcc void %fnPtr(ptr %async.ctxt, ptr %task, ptr %actor)
  ret void
}

declare void @some_user(i64)
declare void @some_may_write(ptr)

define ptr @resume_context_projection(ptr %ctxt) {
entry:
  %resume_ctxt = load ptr, ptr %ctxt, align 8
  ret ptr %resume_ctxt
}


@unreachable_fp = constant <{ i32, i32 }>
  <{ i32 trunc ( ; Relative pointer to async function
       i64 sub (
         i64 ptrtoint (ptr @unreachable to i64),
         i64 ptrtoint (ptr getelementptr inbounds (<{ i32, i32 }>, ptr @unreachable_fp, i32 0, i32 1) to i64)
       )
     to i32),
     i32 128    ; Initial async context size without space for frame
}>

define swiftcc void @unreachable(ptr %async.ctxt, ptr %task, ptr %actor)  {
entry:
  %tmp = alloca { i64, i64 }, align 8
  %proj.1 = getelementptr inbounds { i64, i64 }, ptr %tmp, i64 0, i32 0
  %proj.2 = getelementptr inbounds { i64, i64 }, ptr %tmp, i64 0, i32 1

  %id = call token @llvm.coro.id.async(i32 128, i32 16, i32 0,
          ptr @unreachable_fp)
  %hdl = call ptr @llvm.coro.begin(token %id, ptr null)
  store i64 0, ptr %proj.1, align 8
  store i64 1, ptr %proj.2, align 8
  call void @some_may_write(ptr %proj.1)

	; Begin lowering: apply %my_other_async_function(%args...)
  ; setup callee context
  %arg1 = bitcast ptr @my_other_async_function_fp to ptr
  %callee_context = call ptr @llvm.coro.async.context.alloc(ptr %task, ptr %arg1)
  ; store the return continuation
  %callee_context.return_to_caller.addr = getelementptr inbounds %async.ctxt, ptr %callee_context, i32 0, i32 1
  %resume.func_ptr = call ptr @llvm.coro.async.resume()
  store ptr %resume.func_ptr, ptr %callee_context.return_to_caller.addr

  ; store caller context into callee context
  store ptr %async.ctxt, ptr %callee_context
  %res = call {ptr, ptr, ptr} (i32, ptr, ptr, ...) @llvm.coro.suspend.async(i32 0,
                                                  ptr %resume.func_ptr,
                                                  ptr @resume_context_projection,
                                                  ptr @my_async_function.my_other_async_function_fp.apply,
                                                  ptr @asyncSuspend, ptr %callee_context, ptr %task, ptr %actor)

  call void @llvm.coro.async.context.dealloc(ptr %callee_context)
  %continuation_task_arg = extractvalue {ptr, ptr, ptr} %res, 1
  %val = load i64, ptr %proj.1
  call void @some_user(i64 %val)
  %val.2 = load i64, ptr %proj.2
  call void @some_user(i64 %val.2)
  unreachable
}

; CHECK: define swiftcc void @unreachable
; CHECK-NOT: @llvm.coro.suspend.async
; CHECK: return

; CHECK: define internal swiftcc void @unreachable.resume.0
; CHECK: unreachable

declare ptr @llvm.coro.prepare.async(ptr)
declare token @llvm.coro.id.async(i32, i32, i32, ptr)
declare ptr @llvm.coro.begin(token, ptr)
declare {ptr, ptr, ptr} @llvm.coro.suspend.async(i32, ptr, ptr, ...)
declare ptr @llvm.coro.async.context.alloc(ptr, ptr)
declare void @llvm.coro.async.context.dealloc(ptr)
declare swiftcc void @asyncReturn(ptr, ptr, ptr)
declare swiftcc void @asyncSuspend(ptr, ptr, ptr)
declare ptr @llvm.coro.async.resume()