Unsafe `withTaskGroup` optimization?

I'm filing this under compiler because I get different results between -Onone and -O. It might be an issue with the soon-to-be-deprecated swift-atomics package but my guess is otherwise.

The offending code is:

import Atomics

func inner(_ group: inout TaskGroup<Void>) async -> ManagedAtomic<Int> {
    let count = ManagedAtomic<Int>(314) // guard value
    for _ in 0..<10 {
        group.addTask { count.wrappingIncrement(ordering: .relaxed) }
    }
    return count
}

func outer1() async -> Int {
    async let count = withTaskGroup(of: Void.self, body: inner)
    return await count.load(ordering: .relaxed)
}

func outer2() async -> Int {
    let count = await withTaskGroup(of: Void.self, body: inner)
    return count.load(ordering: .relaxed)
}

await print(outer1(), outer2())

While I'm using .relaxed loads and stores, there is a data dependency between outer1() / outer2() and inner(): neither outer func can know what address to load the atomic value from until withTaskGroup returns which is documented as:

When compiled with -Onone, I get the expected result: 324, 324

With -O or -Osize, the result is a worrying 324, 0 on both x86 and M1

It seems that in optimized builds, when not using async let, the atomic value is loaded not just before the return of withTaskGroup(), but before it was initialized to its 314 guard value.

Version 15.2 (15C500b)

4 Likes

Definitely seems like a bug. Would you mind filing a report in our issue tracker?

3 Likes
2 Likes