mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-07 12:33:42 +00:00
internal/task: remove coroutines
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,18 +0,0 @@
|
||||
package transform_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/tinygo-org/tinygo/transform"
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
func TestGoroutineLowering(t *testing.T) {
|
||||
t.Parallel()
|
||||
testTransform(t, "testdata/coroutines", func(mod llvm.Module) {
|
||||
err := transform.LowerCoroutines(mod, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
+3
-48
@@ -29,10 +29,9 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
|
||||
if inlinerThreshold != 0 {
|
||||
builder.UseInlinerWithThreshold(inlinerThreshold)
|
||||
}
|
||||
builder.AddCoroutinePassesToExtensionPoints()
|
||||
|
||||
// Make sure these functions are kept in tact during TinyGo transformation passes.
|
||||
for _, name := range getFunctionsUsedInTransforms(config) {
|
||||
for _, name := range functionsUsedInTransforms {
|
||||
fn := mod.NamedFunction(name)
|
||||
if fn.IsNil() {
|
||||
panic(fmt.Errorf("missing core function %q", name))
|
||||
@@ -118,17 +117,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
|
||||
goPasses.Run(mod)
|
||||
}
|
||||
|
||||
// Lower async implementations.
|
||||
switch config.Scheduler() {
|
||||
case "coroutines":
|
||||
// Lower async as coroutines.
|
||||
err := LowerCoroutines(mod, config.NeedsStackObjects())
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
case "tasks", "asyncify":
|
||||
// No transformations necessary.
|
||||
case "none":
|
||||
if config.Scheduler() == "none" {
|
||||
// Check for any goroutine starts.
|
||||
if start := mod.NamedFunction("internal/task.start"); !start.IsNil() && len(getUses(start)) > 0 {
|
||||
errs := []error{}
|
||||
@@ -137,8 +126,6 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
|
||||
}
|
||||
return errs
|
||||
}
|
||||
default:
|
||||
return []error{errors.New("invalid scheduler")}
|
||||
}
|
||||
|
||||
if config.VerifyIR() {
|
||||
@@ -151,7 +138,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
|
||||
}
|
||||
|
||||
// After TinyGo-specific transforms have finished, undo exporting these functions.
|
||||
for _, name := range getFunctionsUsedInTransforms(config) {
|
||||
for _, name := range functionsUsedInTransforms {
|
||||
fn := mod.NamedFunction(name)
|
||||
if fn.IsNil() || fn.IsDeclaration() {
|
||||
continue
|
||||
@@ -195,35 +182,3 @@ var functionsUsedInTransforms = []string{
|
||||
"runtime.free",
|
||||
"runtime.nilPanic",
|
||||
}
|
||||
|
||||
var taskFunctionsUsedInTransforms = []string{}
|
||||
|
||||
// These functions need to be preserved in the IR until after the coroutines
|
||||
// pass has run.
|
||||
var coroFunctionsUsedInTransforms = []string{
|
||||
"internal/task.start",
|
||||
"internal/task.Pause",
|
||||
"internal/task.fake",
|
||||
"internal/task.Current",
|
||||
"internal/task.createTask",
|
||||
"(*internal/task.Task).setState",
|
||||
"(*internal/task.Task).returnTo",
|
||||
"(*internal/task.Task).returnCurrent",
|
||||
"(*internal/task.Task).setReturnPtr",
|
||||
"(*internal/task.Task).getReturnPtr",
|
||||
}
|
||||
|
||||
// getFunctionsUsedInTransforms gets a list of all special functions that should be preserved during transforms and optimization.
|
||||
func getFunctionsUsedInTransforms(config *compileopts.Config) []string {
|
||||
fnused := functionsUsedInTransforms
|
||||
switch config.Scheduler() {
|
||||
case "none":
|
||||
case "coroutines":
|
||||
fnused = append(append([]string{}, fnused...), coroFunctionsUsedInTransforms...)
|
||||
case "tasks", "asyncify":
|
||||
fnused = append(append([]string{}, fnused...), taskFunctionsUsedInTransforms...)
|
||||
default:
|
||||
panic(fmt.Errorf("invalid scheduler %q", config.Scheduler()))
|
||||
}
|
||||
return fnused
|
||||
}
|
||||
|
||||
Vendored
-152
@@ -1,152 +0,0 @@
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv7m-none-eabi"
|
||||
|
||||
%"internal/task.state" = type { i8* }
|
||||
%"internal/task.Task" = type { %"internal/task.Task", i8*, i32, %"internal/task.state" }
|
||||
|
||||
declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
|
||||
declare void @"internal/task.Pause"(i8*, i8*)
|
||||
|
||||
declare void @runtime.scheduler(i8*, i8*)
|
||||
|
||||
declare i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||
declare void @runtime.free(i8*, i8*, i8*)
|
||||
|
||||
declare %"internal/task.Task"* @"internal/task.Current"(i8*, i8*)
|
||||
|
||||
declare i8* @"(*internal/task.Task).setState"(%"internal/task.Task"*, i8*, i8*, i8*)
|
||||
declare void @"(*internal/task.Task).setReturnPtr"(%"internal/task.Task"*, i8*, i8*, i8*)
|
||||
declare i8* @"(*internal/task.Task).getReturnPtr"(%"internal/task.Task"*, i8*, i8*)
|
||||
declare void @"(*internal/task.Task).returnTo"(%"internal/task.Task"*, i8*, i8*, i8*)
|
||||
declare void @"(*internal/task.Task).returnCurrent"(%"internal/task.Task"*, i8*, i8*)
|
||||
declare %"internal/task.Task"* @"internal/task.createTask"(i8*, i8*)
|
||||
|
||||
declare void @callMain(i8*, i8*)
|
||||
|
||||
; Test a simple sleep-like scenario.
|
||||
declare void @enqueueTimer(%"internal/task.Task"*, i64, i8*, i8*)
|
||||
|
||||
define void @sleep(i64, i8*, i8* %parentHandle) {
|
||||
entry:
|
||||
%2 = call %"internal/task.Task"* @"internal/task.Current"(i8* undef, i8* null)
|
||||
call void @enqueueTimer(%"internal/task.Task"* %2, i64 %0, i8* undef, i8* null)
|
||||
call void @"internal/task.Pause"(i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Test a delayed value return.
|
||||
define i32 @delayedValue(i32, i64, i8*, i8* %parentHandle) {
|
||||
entry:
|
||||
call void @sleep(i64 %1, i8* undef, i8* null)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Test a deadlocking async func.
|
||||
define void @deadlock(i8*, i8* %parentHandle) {
|
||||
entry:
|
||||
call void @"internal/task.Pause"(i8* undef, i8* null)
|
||||
unreachable
|
||||
}
|
||||
|
||||
; Test a regular tail call.
|
||||
define i32 @tail(i32, i64, i8*, i8* %parentHandle) {
|
||||
entry:
|
||||
%3 = call i32 @delayedValue(i32 %0, i64 %1, i8* undef, i8* null)
|
||||
ret i32 %3
|
||||
}
|
||||
|
||||
; Test a ditching tail call.
|
||||
define void @ditchTail(i32, i64, i8*, i8* %parentHandle) {
|
||||
entry:
|
||||
%3 = call i32 @delayedValue(i32 %0, i64 %1, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Test a void tail call.
|
||||
define void @voidTail(i32, i64, i8*, i8* %parentHandle) {
|
||||
entry:
|
||||
call void @ditchTail(i32 %0, i64 %1, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Test a tail call returning an alternate value.
|
||||
define i32 @alternateTail(i32, i32, i64, i8*, i8* %parentHandle) {
|
||||
entry:
|
||||
%4 = call i32 @delayedValue(i32 %1, i64 %2, i8* undef, i8* null)
|
||||
ret i32 %0
|
||||
}
|
||||
|
||||
; Test a normal return from a coroutine.
|
||||
; This must be turned into a coroutine.
|
||||
define i1 @coroutine(i32, i64, i8*, i8* %parentHandle) {
|
||||
entry:
|
||||
%3 = call i32 @delayedValue(i32 %0, i64 %1, i8* undef, i8* null)
|
||||
%4 = icmp eq i32 %3, 0
|
||||
ret i1 %4
|
||||
}
|
||||
|
||||
; Normal function which should not be transformed.
|
||||
define void @doNothing(i8*, i8* %parentHandle) {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Regression test: ensure that a tail call does not destroy the frame while it is still in use.
|
||||
; Previously, the tail-call lowering transform would branch to the cleanup block after usePtr.
|
||||
; This caused the lifetime of %a to be incorrectly reduced, and allowed the coroutine lowering transform to keep %a on the stack.
|
||||
; After a suspend %a would be used, resulting in memory corruption.
|
||||
define i8 @coroutineTailRegression(i8*, i8* %parentHandle) {
|
||||
entry:
|
||||
%a = alloca i8
|
||||
store i8 5, i8* %a
|
||||
%val = call i8 @usePtr(i8* %a, i8* undef, i8* null)
|
||||
ret i8 %val
|
||||
}
|
||||
|
||||
; Regression test: ensure that stack allocations alive during a suspend end up on the heap.
|
||||
; This used to not be transformed to a coroutine, keeping %a on the stack.
|
||||
; After a suspend %a would be used, resulting in memory corruption.
|
||||
define i8 @allocaTailRegression(i8*, i8* %parentHandle) {
|
||||
entry:
|
||||
%a = alloca i8
|
||||
call void @sleep(i64 1000000, i8* undef, i8* null)
|
||||
store i8 5, i8* %a
|
||||
%val = call i8 @usePtr(i8* %a, i8* undef, i8* null)
|
||||
ret i8 %val
|
||||
}
|
||||
|
||||
; usePtr uses a pointer after a suspend.
|
||||
define i8 @usePtr(i8*, i8*, i8* %parentHandle) {
|
||||
entry:
|
||||
call void @sleep(i64 1000000, i8* undef, i8* null)
|
||||
%val = load i8, i8* %0
|
||||
ret i8 %val
|
||||
}
|
||||
|
||||
; Goroutine that sleeps and does nothing.
|
||||
; Should be a void tail call.
|
||||
define void @sleepGoroutine(i8*, i8* %parentHandle) {
|
||||
call void @sleep(i64 1000000, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Program main function.
|
||||
define void @progMain(i8*, i8* %parentHandle) {
|
||||
entry:
|
||||
; Call a sync func in a goroutine.
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*, i8*)* @doNothing to i32), i8* undef, i32 undef, i8* undef, i8* null)
|
||||
; Call an async func in a goroutine.
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*, i8*)* @sleepGoroutine to i32), i8* undef, i32 undef, i8* undef, i8* null)
|
||||
; Sleep a bit.
|
||||
call void @sleep(i64 2000000, i8* undef, i8* null)
|
||||
; Done.
|
||||
ret void
|
||||
}
|
||||
|
||||
; Entrypoint of runtime.
|
||||
define void @main() {
|
||||
entry:
|
||||
call void @"internal/task.start"(i32 ptrtoint (void (i8*, i8*)* @progMain to i32), i8* undef, i32 undef, i8* undef, i8* null)
|
||||
call void @runtime.scheduler(i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
Vendored
-313
@@ -1,313 +0,0 @@
|
||||
target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||
target triple = "armv7m-none-eabi"
|
||||
|
||||
%"internal/task.Task" = type { %"internal/task.Task", i8*, i32, %"internal/task.state" }
|
||||
%"internal/task.state" = type { i8* }
|
||||
|
||||
declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*)
|
||||
|
||||
declare void @"internal/task.Pause"(i8*, i8*)
|
||||
|
||||
declare void @runtime.scheduler(i8*, i8*)
|
||||
|
||||
declare i8* @runtime.alloc(i32, i8*, i8*, i8*)
|
||||
|
||||
declare void @runtime.free(i8*, i8*, i8*)
|
||||
|
||||
declare %"internal/task.Task"* @"internal/task.Current"(i8*, i8*)
|
||||
|
||||
declare i8* @"(*internal/task.Task).setState"(%"internal/task.Task"*, i8*, i8*, i8*)
|
||||
|
||||
declare void @"(*internal/task.Task).setReturnPtr"(%"internal/task.Task"*, i8*, i8*, i8*)
|
||||
|
||||
declare i8* @"(*internal/task.Task).getReturnPtr"(%"internal/task.Task"*, i8*, i8*)
|
||||
|
||||
declare void @"(*internal/task.Task).returnTo"(%"internal/task.Task"*, i8*, i8*, i8*)
|
||||
|
||||
declare void @"(*internal/task.Task).returnCurrent"(%"internal/task.Task"*, i8*, i8*)
|
||||
|
||||
declare %"internal/task.Task"* @"internal/task.createTask"(i8*, i8*)
|
||||
|
||||
declare void @callMain(i8*, i8*)
|
||||
|
||||
declare void @enqueueTimer(%"internal/task.Task"*, i64, i8*, i8*)
|
||||
|
||||
define void @sleep(i64 %0, i8* %1, i8* %parentHandle) {
|
||||
entry:
|
||||
%task.current = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
%task.current1 = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
call void @enqueueTimer(%"internal/task.Task"* %task.current1, i64 %0, i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @delayedValue(i32 %0, i64 %1, i8* %2, i8* %parentHandle) {
|
||||
entry:
|
||||
%task.current = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
%ret.ptr = call i8* @"(*internal/task.Task).getReturnPtr"(%"internal/task.Task"* %task.current, i8* undef, i8* undef)
|
||||
%ret.ptr.bitcast = bitcast i8* %ret.ptr to i32*
|
||||
store i32 %0, i32* %ret.ptr.bitcast, align 4
|
||||
call void @sleep(i64 %1, i8* undef, i8* %parentHandle)
|
||||
ret i32 undef
|
||||
}
|
||||
|
||||
define void @deadlock(i8* %0, i8* %parentHandle) {
|
||||
entry:
|
||||
%task.current = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @tail(i32 %0, i64 %1, i8* %2, i8* %parentHandle) {
|
||||
entry:
|
||||
%task.current = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
%3 = call i32 @delayedValue(i32 %0, i64 %1, i8* undef, i8* %parentHandle)
|
||||
ret i32 undef
|
||||
}
|
||||
|
||||
define void @ditchTail(i32 %0, i64 %1, i8* %2, i8* %parentHandle) {
|
||||
entry:
|
||||
%task.current = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
%ret.ditch = call i8* @runtime.alloc(i32 4, i8* null, i8* undef, i8* undef)
|
||||
call void @"(*internal/task.Task).setReturnPtr"(%"internal/task.Task"* %task.current, i8* %ret.ditch, i8* undef, i8* undef)
|
||||
%3 = call i32 @delayedValue(i32 %0, i64 %1, i8* undef, i8* %parentHandle)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @voidTail(i32 %0, i64 %1, i8* %2, i8* %parentHandle) {
|
||||
entry:
|
||||
%task.current = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
call void @ditchTail(i32 %0, i64 %1, i8* undef, i8* %parentHandle)
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @alternateTail(i32 %0, i32 %1, i64 %2, i8* %3, i8* %parentHandle) {
|
||||
entry:
|
||||
%task.current = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
%ret.ptr = call i8* @"(*internal/task.Task).getReturnPtr"(%"internal/task.Task"* %task.current, i8* undef, i8* undef)
|
||||
%ret.ptr.bitcast = bitcast i8* %ret.ptr to i32*
|
||||
store i32 %0, i32* %ret.ptr.bitcast, align 4
|
||||
%ret.alternate = call i8* @runtime.alloc(i32 4, i8* null, i8* undef, i8* undef)
|
||||
call void @"(*internal/task.Task).setReturnPtr"(%"internal/task.Task"* %task.current, i8* %ret.alternate, i8* undef, i8* undef)
|
||||
%4 = call i32 @delayedValue(i32 %1, i64 %2, i8* undef, i8* %parentHandle)
|
||||
ret i32 undef
|
||||
}
|
||||
|
||||
define i1 @coroutine(i32 %0, i64 %1, i8* %2, i8* %parentHandle) {
|
||||
entry:
|
||||
%call.return = alloca i32, align 4
|
||||
%coro.id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
|
||||
%coro.size = call i32 @llvm.coro.size.i32()
|
||||
%coro.alloc = call i8* @runtime.alloc(i32 %coro.size, i8* null, i8* undef, i8* undef)
|
||||
%coro.state = call i8* @llvm.coro.begin(token %coro.id, i8* %coro.alloc)
|
||||
%task.current2 = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
%task.state.parent = call i8* @"(*internal/task.Task).setState"(%"internal/task.Task"* %task.current2, i8* %coro.state, i8* undef, i8* undef)
|
||||
%task.retPtr = call i8* @"(*internal/task.Task).getReturnPtr"(%"internal/task.Task"* %task.current2, i8* undef, i8* undef)
|
||||
%task.retPtr.bitcast = bitcast i8* %task.retPtr to i1*
|
||||
%call.return.bitcast = bitcast i32* %call.return to i8*
|
||||
call void @llvm.lifetime.start.p0i8(i64 4, i8* %call.return.bitcast)
|
||||
%task.current = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
%call.return.bitcast1 = bitcast i32* %call.return to i8*
|
||||
call void @"(*internal/task.Task).setReturnPtr"(%"internal/task.Task"* %task.current, i8* %call.return.bitcast1, i8* undef, i8* undef)
|
||||
%3 = call i32 @delayedValue(i32 %0, i64 %1, i8* undef, i8* %parentHandle)
|
||||
%coro.save = call token @llvm.coro.save(i8* %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
|
||||
%4 = load i32, i32* %call.return, align 4
|
||||
call void @llvm.lifetime.end.p0i8(i64 4, i8* %call.return.bitcast)
|
||||
%5 = icmp eq i32 %4, 0
|
||||
store i1 %5, i1* %task.retPtr.bitcast, align 1
|
||||
call void @"(*internal/task.Task).returnTo"(%"internal/task.Task"* %task.current2, i8* %task.state.parent, i8* undef, i8* undef)
|
||||
br label %cleanup
|
||||
|
||||
suspend: ; preds = %entry, %cleanup
|
||||
%unused = call i1 @llvm.coro.end(i8* %coro.state, i1 false)
|
||||
ret i1 undef
|
||||
|
||||
cleanup: ; preds = %entry, %wakeup
|
||||
%coro.memFree = call i8* @llvm.coro.free(token %coro.id, i8* %coro.state)
|
||||
call void @runtime.free(i8* %coro.memFree, i8* undef, i8* undef)
|
||||
br label %suspend
|
||||
}
|
||||
|
||||
define void @doNothing(i8* %0, i8* %parentHandle) {
|
||||
entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
define i8 @coroutineTailRegression(i8* %0, i8* %parentHandle) {
|
||||
entry:
|
||||
%a = alloca i8, align 1
|
||||
%coro.id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
|
||||
%coro.size = call i32 @llvm.coro.size.i32()
|
||||
%coro.alloc = call i8* @runtime.alloc(i32 %coro.size, i8* null, i8* undef, i8* undef)
|
||||
%coro.state = call i8* @llvm.coro.begin(token %coro.id, i8* %coro.alloc)
|
||||
%task.current = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
%task.state.parent = call i8* @"(*internal/task.Task).setState"(%"internal/task.Task"* %task.current, i8* %coro.state, i8* undef, i8* undef)
|
||||
%task.retPtr = call i8* @"(*internal/task.Task).getReturnPtr"(%"internal/task.Task"* %task.current, i8* undef, i8* undef)
|
||||
store i8 5, i8* %a, align 1
|
||||
%coro.state.restore = call i8* @"(*internal/task.Task).setState"(%"internal/task.Task"* %task.current, i8* %task.state.parent, i8* undef, i8* undef)
|
||||
call void @"(*internal/task.Task).setReturnPtr"(%"internal/task.Task"* %task.current, i8* %task.retPtr, i8* undef, i8* undef)
|
||||
%val = call i8 @usePtr(i8* %a, i8* undef, i8* %parentHandle)
|
||||
br label %post.tail
|
||||
|
||||
suspend: ; preds = %post.tail, %cleanup
|
||||
%unused = call i1 @llvm.coro.end(i8* %coro.state, i1 false)
|
||||
ret i8 undef
|
||||
|
||||
cleanup: ; preds = %post.tail
|
||||
%coro.memFree = call i8* @llvm.coro.free(token %coro.id, i8* %coro.state)
|
||||
call void @runtime.free(i8* %coro.memFree, i8* undef, i8* undef)
|
||||
br label %suspend
|
||||
|
||||
post.tail: ; preds = %entry
|
||||
%coro.save = call token @llvm.coro.save(i8* %coro.state)
|
||||
%call.suspend = call i8 @llvm.coro.suspend(token %coro.save, i1 false)
|
||||
switch i8 %call.suspend, label %suspend [
|
||||
i8 0, label %unreachable
|
||||
i8 1, label %cleanup
|
||||
]
|
||||
|
||||
unreachable: ; preds = %post.tail
|
||||
unreachable
|
||||
}
|
||||
|
||||
define i8 @allocaTailRegression(i8* %0, i8* %parentHandle) {
|
||||
entry:
|
||||
%a = alloca i8, align 1
|
||||
%coro.id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
|
||||
%coro.size = call i32 @llvm.coro.size.i32()
|
||||
%coro.alloc = call i8* @runtime.alloc(i32 %coro.size, i8* null, i8* undef, i8* undef)
|
||||
%coro.state = call i8* @llvm.coro.begin(token %coro.id, i8* %coro.alloc)
|
||||
%task.current = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
%task.state.parent = call i8* @"(*internal/task.Task).setState"(%"internal/task.Task"* %task.current, i8* %coro.state, i8* undef, i8* undef)
|
||||
%task.retPtr = call i8* @"(*internal/task.Task).getReturnPtr"(%"internal/task.Task"* %task.current, i8* undef, i8* undef)
|
||||
call void @sleep(i64 1000000, i8* undef, i8* %parentHandle)
|
||||
%coro.save1 = call token @llvm.coro.save(i8* %coro.state)
|
||||
%call.suspend2 = call i8 @llvm.coro.suspend(token %coro.save1, i1 false)
|
||||
switch i8 %call.suspend2, label %suspend [
|
||||
i8 0, label %wakeup
|
||||
i8 1, label %cleanup
|
||||
]
|
||||
|
||||
wakeup: ; preds = %entry
|
||||
store i8 5, i8* %a, align 1
|
||||
%1 = call i8* @"(*internal/task.Task).setState"(%"internal/task.Task"* %task.current, i8* %task.state.parent, i8* undef, i8* undef)
|
||||
call void @"(*internal/task.Task).setReturnPtr"(%"internal/task.Task"* %task.current, i8* %task.retPtr, i8* undef, i8* undef)
|
||||
%2 = call i8 @usePtr(i8* %a, i8* undef, i8* %parentHandle)
|
||||
br label %post.tail
|
||||
|
||||
suspend: ; preds = %entry, %post.tail, %cleanup
|
||||
%unused = call i1 @llvm.coro.end(i8* %coro.state, i1 false)
|
||||
ret i8 undef
|
||||
|
||||
cleanup: ; preds = %entry, %post.tail
|
||||
%coro.memFree = call i8* @llvm.coro.free(token %coro.id, i8* %coro.state)
|
||||
call void @runtime.free(i8* %coro.memFree, i8* undef, i8* undef)
|
||||
br label %suspend
|
||||
|
||||
post.tail: ; preds = %wakeup
|
||||
%coro.save = call token @llvm.coro.save(i8* %coro.state)
|
||||
%call.suspend = call i8 @llvm.coro.suspend(token %coro.save, i1 false)
|
||||
switch i8 %call.suspend, label %suspend [
|
||||
i8 0, label %unreachable
|
||||
i8 1, label %cleanup
|
||||
]
|
||||
|
||||
unreachable: ; preds = %post.tail
|
||||
unreachable
|
||||
}
|
||||
|
||||
define i8 @usePtr(i8* %0, i8* %1, i8* %parentHandle) {
|
||||
entry:
|
||||
%coro.id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null)
|
||||
%coro.size = call i32 @llvm.coro.size.i32()
|
||||
%coro.alloc = call i8* @runtime.alloc(i32 %coro.size, i8* null, i8* undef, i8* undef)
|
||||
%coro.state = call i8* @llvm.coro.begin(token %coro.id, i8* %coro.alloc)
|
||||
%task.current = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
%task.state.parent = call i8* @"(*internal/task.Task).setState"(%"internal/task.Task"* %task.current, i8* %coro.state, i8* undef, i8* undef)
|
||||
%task.retPtr = call i8* @"(*internal/task.Task).getReturnPtr"(%"internal/task.Task"* %task.current, i8* undef, i8* undef)
|
||||
call void @sleep(i64 1000000, i8* undef, i8* %parentHandle)
|
||||
%coro.save = call token @llvm.coro.save(i8* %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
|
||||
%2 = load i8, i8* %0, align 1
|
||||
store i8 %2, i8* %task.retPtr, align 1
|
||||
call void @"(*internal/task.Task).returnTo"(%"internal/task.Task"* %task.current, i8* %task.state.parent, i8* undef, i8* undef)
|
||||
br label %cleanup
|
||||
|
||||
suspend: ; preds = %entry, %cleanup
|
||||
%unused = call i1 @llvm.coro.end(i8* %coro.state, i1 false)
|
||||
ret i8 undef
|
||||
|
||||
cleanup: ; preds = %entry, %wakeup
|
||||
%coro.memFree = call i8* @llvm.coro.free(token %coro.id, i8* %coro.state)
|
||||
call void @runtime.free(i8* %coro.memFree, i8* undef, i8* undef)
|
||||
br label %suspend
|
||||
}
|
||||
|
||||
define void @sleepGoroutine(i8* %0, i8* %parentHandle) {
|
||||
%task.current = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
call void @sleep(i64 1000000, i8* undef, i8* %parentHandle)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @progMain(i8* %0, i8* %parentHandle) {
|
||||
entry:
|
||||
%task.current = bitcast i8* %parentHandle to %"internal/task.Task"*
|
||||
call void @doNothing(i8* undef, i8* undef)
|
||||
%start.task = call %"internal/task.Task"* @"internal/task.createTask"(i8* undef, i8* undef)
|
||||
%start.task.bitcast = bitcast %"internal/task.Task"* %start.task to i8*
|
||||
call void @sleepGoroutine(i8* undef, i8* %start.task.bitcast)
|
||||
call void @sleep(i64 2000000, i8* undef, i8* %parentHandle)
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @main() {
|
||||
entry:
|
||||
%start.task = call %"internal/task.Task"* @"internal/task.createTask"(i8* undef, i8* undef)
|
||||
%start.task.bitcast = bitcast %"internal/task.Task"* %start.task to i8*
|
||||
call void @progMain(i8* undef, i8* %start.task.bitcast)
|
||||
call void @runtime.scheduler(i8* undef, i8* null)
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: argmemonly nounwind readonly
|
||||
declare token @llvm.coro.id(i32, i8* readnone, i8* nocapture readonly, i8*) #0
|
||||
|
||||
; Function Attrs: nounwind readnone
|
||||
declare i32 @llvm.coro.size.i32() #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i8* @llvm.coro.begin(token, i8* writeonly) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i8 @llvm.coro.suspend(token, i1) #2
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare i1 @llvm.coro.end(i8*, i1) #2
|
||||
|
||||
; Function Attrs: argmemonly nounwind readonly
|
||||
declare i8* @llvm.coro.free(token, i8* nocapture readonly) #0
|
||||
|
||||
; Function Attrs: nounwind
|
||||
declare token @llvm.coro.save(i8*) #2
|
||||
|
||||
; Function Attrs: argmemonly nofree nosync nounwind willreturn
|
||||
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #3
|
||||
|
||||
; Function Attrs: argmemonly nofree nosync nounwind willreturn
|
||||
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #3
|
||||
|
||||
attributes #0 = { argmemonly nounwind readonly }
|
||||
attributes #1 = { nounwind readnone }
|
||||
attributes #2 = { nounwind }
|
||||
attributes #3 = { argmemonly nofree nosync nounwind willreturn }
|
||||
@@ -36,7 +36,7 @@ func ExternalInt64AsPtr(mod llvm.Module, config *compileopts.Config) error {
|
||||
if strings.HasPrefix(fn.Name(), "llvm.") || strings.HasPrefix(fn.Name(), "runtime.") {
|
||||
// Do not try to modify the signature of internal LLVM functions and
|
||||
// assume that runtime functions are only temporarily exported for
|
||||
// coroutine lowering.
|
||||
// transforms.
|
||||
continue
|
||||
}
|
||||
if !fn.GetStringAttributeAtIndex(-1, "tinygo-methods").IsNil() {
|
||||
|
||||
Reference in New Issue
Block a user