Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions .github/workflows/coroutine.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:

- name: Test coroutine build integration
if: matrix.llvm == 19
run: go test ./internal/build -run 'Test(CoroPlanBuilderRunsBeforeCodegenWithoutChangingIR|CoroPlanInputCanonicalizesPatchedRoot|BuildCoroPlanErrors|CoroEntryResolutionUsesPlanMatchedPackageCache|CoroEntryResolutionBuildsPreparedRuntimePackages|CoroEmissionCoverageStopsBeforeAnyPackageCodegen|CoroUnsupportedEntryResolutionReturnsErrorBeforeCodegen|CoroEmissionUniverseAcceptsModeTestVariants)$' -count=1
run: go test ./internal/build -run 'Test(CoroPlanBuilderRunsBeforeCodegenWithoutChangingIR|CoroPlanInputCanonicalizesPatchedRoot|ActiveCoroABIVersions|BuildCoroPlanErrors|CoroEntryResolutionUsesPlanMatchedPackageCache|CoroEntryResolutionBuildsPreparedRuntimePackages|CoroEmissionCoverageStopsBeforeAnyPackageCodegen|CoroUnsupportedEntryResolutionReturnsErrorBeforeCodegen|CoroEmissionUniverseAcceptsModeTestVariants)$' -count=1

- name: Test coroutine compiler integration
if: matrix.llvm == 19
Expand All @@ -54,7 +54,7 @@ jobs:
go test -race ./cl -run '^Test(CompilationCoroPlanObservationAndCacheRegistration|CoroEntryResolutionPlainPrimaryPreservesIR|ResolveFunctionSymbolUsesPrimaryAndExactPlan|CoroEntryRejectsUnsupportedBeforeCreatingSymbol|CoroEntryResolutionPreflightRejectsWholePlanBeforeCodegen|CoroEntryResolutionPreflightRejectsMissingPlanAndCache|Emission.*)$' -count=1

- name: Test structured LLVM coroutine builder
run: go test -tags='${{ matrix.tags }}' -v ./ssa -run '^TestCoroBuilder' -count=1
run: go test -tags='${{ matrix.tags }}' -v ./ssa -run '^TestCoro(Builder|Handle|Promise|RootFactory)' -count=1

- name: Test canonical coroutine plan digest and cache identity
run: |
Expand All @@ -63,7 +63,7 @@ jobs:
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)' -count=1
run: go test -tags='${{ matrix.tags }}' -v ./cl -run '^TestCoro(LeafPhysicalABI|PhysicalABI|ChildAwaitPhysicalABIV1|ExplicitAsyncRootFactoryV1|ExplicitRootFactoryV1)' -count=1

- name: Test LLVM 22 tool configuration
if: matrix.llvm == 22
Expand Down
18 changes: 16 additions & 2 deletions cl/compilation.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,15 @@ type Compilation struct {
FuncRepABI string
// EnableCoroPhysicalABI permits the conservative leaf-only coroutine ABI
// lowering implemented by the current experimental slice. It requires entry
// resolution and does not enable await, dispatch, roots, or a scheduler.
// resolution and does not by itself enable await, dispatch, roots, or a
// scheduler.
EnableCoroPhysicalABI bool
// EnableCoroChildAwait permits the narrowly-scoped static child handoff ABI.
// It requires the physical ABI and emits typed factories for explicit async
// roots. A generated parent only publishes an initial-suspended child and
// suspends itself; a matching scheduler owns every resume and destroy
// operation.
EnableCoroChildAwait bool

// EmissionUniverse is the immutable, compilation-scoped set of exact SSA
// functions that cl may resolve while emitting this compilation. Active
Expand Down Expand Up @@ -85,13 +92,20 @@ func (c *Compilation) validateCoroABIIdentity(required bool) error {
if c.EnableCoroPhysicalABI {
wantCoroABI = coro.PhysicalABIV0
}
if c.EnableCoroChildAwait {
wantCoroABI = coro.PhysicalABIV1
}
wantSchedulerABI := coro.SchedulerNoneABIV0
if c.EnableCoroChildAwait {
wantSchedulerABI = coro.SchedulerChildAwaitABIV0
}
checks := []struct {
name string
got string
want string
}{
{"coroutine", c.CoroABI, wantCoroABI},
{"scheduler", c.SchedulerABI, coro.SchedulerNoneABIV0},
{"scheduler", c.SchedulerABI, wantSchedulerABI},
{"panic", c.PanicABI, coro.PanicLegacyABIV0},
{"function representation", c.FuncRepABI, coro.FuncRepABIV0},
}
Expand Down
25 changes: 25 additions & 0 deletions cl/compilation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,31 @@ 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)
}
newChildAwait := func() *Compilation {
return &Compilation{
EnableCoroEntryResolution: true,
EnableCoroPhysicalABI: true,
EnableCoroChildAwait: true,
CoroABI: coro.PhysicalABIV1,
SchedulerABI: coro.SchedulerChildAwaitABIV0,
PanicABI: coro.PanicLegacyABIV0,
FuncRepABI: coro.FuncRepABIV0,
}
}
childAwait := newChildAwait()
if err := childAwait.validateCoroABIIdentity(false); err != nil {
t.Fatalf("complete child-await ABI identity: %v", err)
}
wrongChildAwait := newChildAwait()
wrongChildAwait.CoroABI = coro.PhysicalABIV0
if err := wrongChildAwait.validateCoroABIIdentity(false); err == nil || !strings.Contains(err.Error(), "coroutine ABI") {
t.Fatalf("child-await physical ABI mismatch = %v", err)
}
wrongChildAwait = newChildAwait()
wrongChildAwait.SchedulerABI = coro.SchedulerNoneABIV0
if err := wrongChildAwait.validateCoroABIIdentity(false); err == nil || !strings.Contains(err.Error(), "scheduler ABI") {
t.Fatalf("child-await scheduler ABI mismatch = %v", err)
}
partial := newPhysical()
partial.SchedulerABI = ""
if err := partial.validateCoroABIIdentity(false); err == nil || !strings.Contains(err.Error(), "scheduler ABI") {
Expand Down
11 changes: 10 additions & 1 deletion cl/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ type context struct {
cacheRegistration bool // cached archive: skip observers; emitted IR is transient
pcLineSeq uint64
sourceParamBase int // hidden physical parameters before source params
currentCoro *coroBodyContext

patches Patches
blkInfos []blocks.Info
Expand Down Expand Up @@ -536,6 +537,7 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
}()
return p.patchType(f.Signature).(*types.Signature)
}()
sourceSig := sig
state := p.state
isInit := (f.Name() == "init" && sig.Recv() == nil)
if isInit && state == pkgHasPatch {
Expand Down Expand Up @@ -578,6 +580,9 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
fn.DisableTailCalls()
}
p.funcs[f] = fn
if physicalABI != nil && entry.childAwait {
p.emitCoroRootFactory(pkg, entry, *physicalABI, sourceSig, fn)
}
isCgo := isCgoExternSymbol(f)
if nblk := len(f.Blocks); nblk > 0 {
if p.prog.FuncInfoMetadataEnabled() {
Expand Down Expand Up @@ -639,7 +644,7 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
p.bvals = make(map[ssa.Value]llssa.Expr)
p.methodNilDerefChecks = collectMethodNilDerefChecks(f)
if physicalABI != nil {
p.compileCoroLeafBody(b, f, *physicalABI)
p.compileCoroPhysicalBody(b, f, *physicalABI)
b.EndBuild()
return
}
Expand Down Expand Up @@ -1205,6 +1210,10 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
}
switch v := iv.(type) {
case *ssa.Call:
if value, handled := p.tryCompileCoroStaticAwait(b, v); handled {
ret = value
break
}
ret = p.call(b, llssa.Call, &v.Call)
if p.rangeFuncCallNeedsDeferDrain(&v.Call) {
b.DeferStackDrain()
Expand Down
Loading