Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a94dfb9
feat(coro): gate plain function dispatch ABI
cpunion Jul 16, 2026
a379120
feat(coro): certify closed dynamic call targets
cpunion Jul 16, 2026
2d4f25c
feat(coro): lower plain dispatch descriptors
cpunion Jul 16, 2026
188ee44
feat(coro): emit plain function dispatch
cpunion Jul 16, 2026
687723e
feat(coro): prove TLS destructor dispatch
cpunion Jul 16, 2026
b2e703c
ci(coro): cover plain TLS dispatch
cpunion Jul 16, 2026
cecd715
feat(coro): trust frozen TLS C leaves
cpunion Jul 16, 2026
8629ea5
feat(coro): demand ABI method table entries
cpunion Jul 16, 2026
c1b5f56
feat(coro): freeze frontend lowered call edges
cpunion Jul 16, 2026
8d09749
feat(ssa): resolve compiler-lowered runtime calls
cpunion Jul 16, 2026
d47b16b
build: sync coroutine bindings with upstream LLVM 22
cpunion Jul 16, 2026
e2d62df
target(wasm): link freestanding allocator without BDWGC
cpunion Jul 16, 2026
d9694a3
runtime(coro): add preemptive park and wake core
cpunion Jul 16, 2026
fa09d82
compiler(coro): lower synchronous Go style onto LLVM frames
cpunion Jul 16, 2026
95288ed
docs(coro): record executable park and wasm prototype
cpunion Jul 16, 2026
a63666d
test(coro): accept LLVM 20 GEP no-wrap flags
cpunion Jul 16, 2026
7bb9c10
ci(coro): accept target-selected nogc allocator
cpunion Jul 16, 2026
1734c3a
test(coro): keep debug parameters across frame lowering
cpunion Jul 16, 2026
6d0723f
runtime(coro): add closed static G spawn transaction
cpunion Jul 16, 2026
9335f0b
runtime(coro): cancel ready Gs on command main return
cpunion Jul 16, 2026
1f2e76c
compiler(coro): lower closed static goroutine spawn
cpunion Jul 16, 2026
14fb36e
docs(coro): record closed static spawn prototype
cpunion Jul 16, 2026
b61c8bb
test(coro): avoid copying build context lock
cpunion Jul 16, 2026
b2c8555
compiler(coro): reserve explicit status panic ABI
cpunion Jul 16, 2026
825e984
test(coro): link and run native static spawn island
cpunion Jul 16, 2026
020a8aa
runtime(coro): add terminal explicit panic state machine
cpunion Jul 16, 2026
f3bdeea
docs(coro): record runnable spawn and panic core
cpunion Jul 16, 2026
7448583
runtime(coro): export terminal panic handoff
cpunion Jul 16, 2026
1c8829e
compiler(coro): lower terminal explicit panic status
cpunion Jul 16, 2026
b6e3641
build(coro): retain explicit panic prepare hook
cpunion Jul 16, 2026
21797b5
test(coro): run terminal panic scheduler island
cpunion Jul 16, 2026
61b9937
docs(coro): record terminal panic prototype
cpunion Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 42 additions & 5 deletions .github/workflows/coroutine.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
matrix:
include:
- { llvm: 19, go: "1.24.2", tags: "llvm19" }
- { llvm: 20, go: "1.24.2", tags: "llvm20" }
- { llvm: 21, go: "1.24.2", tags: "llvm21" }
- { llvm: 22, go: "1.24.2", tags: "llvm22" }
- { llvm: 19, go: "1.26.5", tags: "llvm19" }
Expand Down Expand Up @@ -46,6 +47,7 @@ jobs:
- name: Test target-neutral coroutine runtime core
run: |
cd runtime
go test -race -shuffle=on ./internal/coroalloc -count=1
go test -race -shuffle=on ./internal/coro -count=1
# The complete LLGo runtime package intentionally owns symbols that
# collide with the host Go runtime. Use the real production adapter
Expand All @@ -57,14 +59,39 @@ jobs:
./internal/runtime/coro_program.go \
./internal/runtime/coro_sched.go \
./internal/runtime/coro_program_test.go \
-run '^TestCoroProgramV1' -count=1
-run '^TestCoroProgram' -count=1
GOOS=js GOARCH=wasm CGO_ENABLED=0 go test \
-tags=coro_runtime_adapter_test \
-exec="$(go env GOROOT)/lib/wasm/go_js_wasm_exec" \
./internal/runtime/coro_program.go \
./internal/runtime/coro_sched.go \
./internal/runtime/coro_program_test.go \
-run '^TestCoroProgramV1' -count=1
-run '^TestCoroProgram' -count=1

- name: Link named freestanding WebAssembly targets
if: matrix.llvm == 19 && matrix.go == '1.24.2'
env:
LLGO_WASM_TARGET_SMOKE: "1"
run: |
go test -v ./internal/crosscompile -run '^TestFreestandingWasmTargetToolchainSmoke$' -count=1
go build -o /tmp/llgo-wasm-target ./cmd/llgo
for target in wasip2 wasm-unknown; do
output="/tmp/llgo-${target}.wasm"
LLGO_BUILD_CACHE=off LDFLAGS='--export=main' \
/tmp/llgo-wasm-target build -target="$target" -o "$output" \
./internal/crosscompile/testdata/wasm_allocator
test "$(od -An -t x1 -N4 "$output" | tr -d ' \n')" = '0061736d'
symbols="$(llvm-nm --defined-only --format=just-symbols "$output")"
for symbol in main malloc free sbrk abort; do
grep -Fx "$symbol" <<<"$symbols"
done
! grep -E '^GC_' <<<"$symbols"
test -z "$(llvm-nm --undefined-only --format=just-symbols "$output")"
if command -v wasmtime >/dev/null; then
result="$(wasmtime run --invoke main "$output" 0 0)"
test "$result" = '0'
fi
done

- name: Compile coroutine runtime adapter across targets
if: matrix.llvm == 19
Expand All @@ -77,7 +104,11 @@ jobs:

- name: Test coroutine build integration
if: matrix.llvm == 19
run: go test ./internal/build -run 'Test(CoroPlanBuilderRunsBeforeCodegenWithoutChangingIR|CoroPlanInputCanonicalizesPatchedRoot|CoroPlanInputElidesOnlyFrontendNoInitCalls|RequiredCoroProgramRuntimePlanPlainClosureAndConflicts|ActiveCoroABIVersions|BuildCoroPlanErrors|CoroEntryResolutionUsesPlanMatchedPackageCache|CoroEntryResolutionBuildsPreparedRuntimePackages|CoroRuntimeLinkRequirements|CoroEmissionCoverageStopsBeforeAnyPackageCodegen|CoroUnsupportedEntryResolutionReturnsErrorBeforeCodegen|CoroEmissionUniverseAcceptsModeTestVariants|CoroProgramBootstrapRejectsInvalidRootsBeforePackageCodegen)$' -count=1
# Keep the focused workflow exhaustive for the build-side coroutine
# contract. This includes park effect seeding, frozen foreign noblock
# certificates, IRQUnsafe handling, the exact legacy PanicABI stop, and
# the native linked static-spawn scheduler-island execution smoke.
run: go test ./internal/build -run 'Coro|Coroutine' -timeout=10m -count=1

- name: Test coroutine compiler integration
if: matrix.llvm == 19
Expand All @@ -97,8 +128,14 @@ jobs:
go test -tags='${{ matrix.tags }}' ./internal/build -run '^Test(BuildCoroPlanInstallsArchiveDigest|CoroutinePlanInputsAffectFingerprint|CoroEntryResolutionUsesPlanMatchedPackageCache|CoroPlanDigestMetadataUsesEffectiveLLVMTarget|CoroPhysicalABICacheRegistrationPreservesCollectedFuncInfo)$' -count=1
go test -tags='${{ matrix.tags }}' ./cl -run '^Test(CompilationCoroABIIdentityValidation|CoroEntryResolutionCacheRegistrationWithDigest|CoroPhysicalABICacheRegistrationPreservesPhysicalMetadata)$' -count=1

- name: Test coroutine physical ABI lowering
run: go test -tags='${{ matrix.tags }}' -v ./cl -run '^TestCoro(LeafPhysicalABI|PhysicalABI|ChildAwaitPhysicalABIV1|ExplicitAsyncRootFactoryV1|ExplicitRootFactoryV1|ExplicitPlain|RootPackageAnchorV1)' -count=1
- name: Test coroutine physical ABI and function dispatch lowering
# Run every compiler test whose name is part of the coroutine contract;
# in particular this covers pure SSA aggregates/PHI and caller-frame
# park lowering on native64 and wasm32 before and after CoroSplit.
run: go test -tags='${{ matrix.tags }}' -v ./cl -run '^Test(Coro|EmissionUniverse(ActiveABIMethodTablesUseFrozenWrapperSymbols|ABIMethodDemandReferencesAreExactRecursiveAndOwnerScoped))' -count=1

- name: Test coroutine TLS function dispatch proof
run: go test -tags='${{ matrix.tags }}' -v ./internal/build -run '^TestCoroTLS' -count=1

- name: Test coroutine registry and control integration
run: go test -tags='${{ matrix.tags }}' -v ./internal/build -run '^Test(CollectLinkedCoroRootAnchors|ActiveCoroABIVersions|BuildCoroPlanErrors|CoroProgramManifest.*|CoroProgramBootstrap.*|SelectCoroProgramBootstrap.*|GenMainModule.*Coro.*)$' -count=1
Expand Down
11 changes: 8 additions & 3 deletions cl/blocks/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import (
)

type Info struct {
Kind llssa.DoAction
Next int
Kind llssa.DoAction
Next int
InLoop bool
}

// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -168,7 +169,11 @@ retry:
ret := make([]Info, n)
for i := 0; i < n; i++ {
iblk := order[i]
ret[iblk] = Info{states[iblk].kind(), order[i+1]}
ret[iblk] = Info{
Kind: states[iblk].kind(),
Next: order[i+1],
InLoop: states[iblk].inLoop,
}
}
return ret
}
Expand Down
2 changes: 1 addition & 1 deletion cl/blocks/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func TestFirstLoop(t *testing.T) {
blk.Preds = []*ssa.BasicBlock{blk}
blk.Succs = []*ssa.BasicBlock{blk}
infos := Infos([]*ssa.BasicBlock{blk})
if infos[0].Kind != llssa.DeferInLoop {
if infos[0].Kind != llssa.DeferInLoop || !infos[0].InLoop {
t.Fatal("TestFirstLoop")
}
}
Expand Down
44 changes: 40 additions & 4 deletions cl/compilation.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ type Compilation struct {
SchedulerABI string
PanicABI string
FuncRepABI string
// EnableCoroExplicitStatusPanicABI selects the target-wide explicit-status
// panic identity. The first lowering slice accepts only exact cleanup-free
// physical coroutine bodies whose explicit panic payload can outlive frame
// destruction; every wider hidden-outcome or unwind shape remains fail-closed.
EnableCoroExplicitStatusPanicABI bool
// EnableCoroPhysicalABI permits the conservative leaf-only coroutine ABI
// lowering implemented by the current experimental slice. It requires entry
// resolution and does not by itself enable await, dispatch, roots, or a
Expand All @@ -62,6 +67,15 @@ type Compilation struct {
// suspends itself; a matching scheduler owns every resume and destroy
// operation.
EnableCoroChildAwait bool
// EnableCoroPlainDispatch permits the first descriptor/context function-value
// ABI. Only a no-capture, non-suspending plain target at an ordinary scalar
// call is accepted by this capability; every wider dynamic form remains an
// unsupported preflight error.
EnableCoroPlainDispatch bool
// EnableCoroClosedStaticSpawn permits only the compilation-plan-certified
// closed static spawn transaction. The physical parent G is passed to both
// runtime hooks; no TLS lookup or indirect user callback is permitted.
EnableCoroClosedStaticSpawn bool
// EnableCoroProgramBootstrapRun selects the program-root scheduler ABI for
// package identities. The factory itself lives in the uncached entry module,
// but every linked archive must agree with the runtime driver contract.
Expand Down Expand Up @@ -103,11 +117,33 @@ func (c *Compilation) validateCoroABIIdentity(required bool) error {
if c.EnableCoroChildAwait {
wantSchedulerABI = coro.SchedulerChildAwaitABIV0
}
if c.EnableCoroProgramBootstrapRun {
if c.EnableCoroClosedStaticSpawn {
if !c.EnableCoroChildAwait {
return fmt.Errorf("coroutine closed static spawn requires child-await lowering")
}
if !c.EnableCoroProgramBootstrapRun {
return fmt.Errorf("coroutine closed static spawn requires the runnable program-bootstrap v2 scheduler")
}
wantSchedulerABI = coro.SchedulerProgramBootstrapClosedStaticSpawnABIV0
} else if c.EnableCoroProgramBootstrapRun {
if !c.EnableCoroChildAwait {
return fmt.Errorf("coroutine program bootstrap runtime requires child-await lowering")
}
wantSchedulerABI = coro.SchedulerProgramBootstrapABIV1
wantSchedulerABI = coro.SchedulerProgramBootstrapABIV2
}
if c.EnableCoroPlainDispatch && !c.EnableCoroEntryResolution {
return fmt.Errorf("coroutine plain dispatch requires coroutine entry resolution")
}
if c.EnableCoroExplicitStatusPanicABI && !c.EnableCoroEntryResolution {
return fmt.Errorf("coroutine explicit-status panic ABI requires coroutine entry resolution")
}
wantPanicABI := coro.PanicLegacyABIV0
if c.EnableCoroExplicitStatusPanicABI {
wantPanicABI = coro.PanicExplicitStatusABIV0
}
wantFuncRepABI := coro.FuncRepABIV0
if c.EnableCoroPlainDispatch {
wantFuncRepABI = coro.FuncRepABIV1
}
checks := []struct {
name string
Expand All @@ -116,8 +152,8 @@ func (c *Compilation) validateCoroABIIdentity(required bool) error {
}{
{"coroutine", c.CoroABI, wantCoroABI},
{"scheduler", c.SchedulerABI, wantSchedulerABI},
{"panic", c.PanicABI, coro.PanicLegacyABIV0},
{"function representation", c.FuncRepABI, coro.FuncRepABIV0},
{"panic", c.PanicABI, wantPanicABI},
{"function representation", c.FuncRepABI, wantFuncRepABI},
}
if !required {
populated := false
Expand Down
69 changes: 68 additions & 1 deletion cl/compilation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,62 @@ func TestCompilationCoroABIIdentityValidation(t *testing.T) {
if err := (&Compilation{EnableCoroEntryResolution: true, EnableCoroPhysicalABI: true}).validateCoroABIIdentity(false); err != nil {
t.Fatalf("omitted source ABI identity should use current defaults: %v", err)
}
newPlainDispatch := func() *Compilation {
return &Compilation{
EnableCoroEntryResolution: true,
EnableCoroPlainDispatch: true,
CoroABI: coro.EntryResolutionABIV0,
SchedulerABI: coro.SchedulerNoneABIV0,
PanicABI: coro.PanicLegacyABIV0,
FuncRepABI: coro.FuncRepABIV1,
}
}
plainDispatch := newPlainDispatch()
if err := plainDispatch.validateCoroABIIdentity(false); err != nil {
t.Fatalf("complete plain-dispatch ABI identity: %v", err)
}
wrongPlainDispatch := newPlainDispatch()
wrongPlainDispatch.FuncRepABI = coro.FuncRepABIV0
if err := wrongPlainDispatch.validateCoroABIIdentity(false); err == nil || !strings.Contains(err.Error(), "function representation ABI") {
t.Fatalf("plain-dispatch function representation mismatch = %v", err)
}
withoutEntry := newPlainDispatch()
withoutEntry.EnableCoroEntryResolution = false
if err := withoutEntry.validateCoroABIIdentity(false); err == nil || !strings.Contains(err.Error(), "requires coroutine entry resolution") {
t.Fatalf("plain-dispatch dependency error = %v", err)
}
if err := withoutEntry.preflightCoroPlan(); err == nil || !strings.Contains(err.Error(), "requires coroutine entry resolution") {
t.Fatalf("plain-dispatch preflight dependency error = %v", err)
}
newExplicitStatus := func() *Compilation {
compilation := newPhysical()
compilation.EnableCoroChildAwait = true
compilation.EnableCoroExplicitStatusPanicABI = true
compilation.CoroABI = coro.PhysicalABIV1
compilation.SchedulerABI = coro.SchedulerChildAwaitABIV0
compilation.PanicABI = coro.PanicExplicitStatusABIV0
return compilation
}
explicitStatus := newExplicitStatus()
if err := explicitStatus.validateCoroABIIdentity(false); err != nil {
t.Fatalf("complete explicit-status panic ABI identity: %v", err)
}
legacyIdentity := newExplicitStatus()
legacyIdentity.PanicABI = coro.PanicLegacyABIV0
if err := legacyIdentity.validateCoroABIIdentity(false); err == nil || !strings.Contains(err.Error(), "panic ABI") {
t.Fatalf("explicit-status panic ABI mismatch = %v", err)
}
withoutExplicitStatusEntry := newExplicitStatus()
withoutExplicitStatusEntry.EnableCoroEntryResolution = false
if err := withoutExplicitStatusEntry.validateCoroABIIdentity(false); err == nil || !strings.Contains(err.Error(), "requires coroutine entry resolution") {
t.Fatalf("explicit-status panic ABI dependency error = %v", err)
}
if err := withoutExplicitStatusEntry.preflightCoroPlan(); err == nil || !strings.Contains(err.Error(), "requires coroutine entry resolution") {
t.Fatalf("explicit-status panic ABI preflight dependency error = %v", err)
}
if err := explicitStatus.preflightCoroPlan(); err == nil || !strings.Contains(err.Error(), "requires a compilation CoroPlan") {
t.Fatalf("explicit-status panic ABI active preflight error = %v", err)
}
newChildAwait := func() *Compilation {
return &Compilation{
EnableCoroEntryResolution: true,
Expand All @@ -111,10 +167,21 @@ func TestCompilationCoroABIIdentityValidation(t *testing.T) {
}
programBootstrap := newChildAwait()
programBootstrap.EnableCoroProgramBootstrapRun = true
programBootstrap.SchedulerABI = coro.SchedulerProgramBootstrapABIV1
programBootstrap.SchedulerABI = coro.SchedulerProgramBootstrapABIV2
if err := programBootstrap.validateCoroABIIdentity(false); err != nil {
t.Fatalf("complete program-bootstrap ABI identity: %v", err)
}
closedStaticSpawn := newChildAwait()
closedStaticSpawn.EnableCoroProgramBootstrapRun = true
closedStaticSpawn.EnableCoroClosedStaticSpawn = true
closedStaticSpawn.SchedulerABI = coro.SchedulerProgramBootstrapClosedStaticSpawnABIV0
if err := closedStaticSpawn.validateCoroABIIdentity(false); err != nil {
t.Fatalf("complete closed-static-spawn ABI identity: %v", err)
}
closedStaticSpawn.EnableCoroProgramBootstrapRun = false
if err := closedStaticSpawn.validateCoroABIIdentity(false); err == nil || !strings.Contains(err.Error(), "runnable program-bootstrap v2") {
t.Fatalf("closed-static-spawn bootstrap dependency error = %v", err)
}
programBootstrap.EnableCoroChildAwait = false
if err := programBootstrap.validateCoroABIIdentity(false); err == nil || !strings.Contains(err.Error(), "requires child-await") {
t.Fatalf("program-bootstrap dependency error = %v", err)
Expand Down
Loading