Miscompiled code (Embedded Swift)

I was testing my code the same way I have for almost a year, I am compiling for wasm32-none-wasm and after a while my basically empty test program crashed as it ran out of memory.

I am using a minimal wasm malloc implementation which is unlikely to be the problem as it never had such issues, and the memory is otherwise completely managed by Swift's classes.

This wouldn't be the first time the compiler caused memory leaks, but before it was easy to narrow it down to variadic generics — this time I have no idea where to look. How do I find what's causing this and if it's the compiler?

The only thing that comes to mind would be adding lazy properties in a few places, are there issues with those in the embedded mode?


A lot of malloc calls come from result builders:

Due to embedded swift lacking variadic generics, types like VStack have to allocate — so this would be the only place repeatedly calling malloc as I didn't bother with caching it (since when variadic generics are fixed it should be implementable lazily). While this code is not efficient it should not leak memory.


I tested the allocator with this:

UnsafeMutableRawPointer
    .allocate(byteCount: 100000000, alignment: 1)
    .deallocate()

And the memory usage was consistent, so the allocator is definitely fine. My does not allocate manually so this is almost certainly something wrong with the compiler.

I then commented out the result builder use and the memory leak went away.

this is the code:

@resultBuilder
public struct FlatteningDrawableBuilder {
    public static func buildPartialBlock(first: some Drawable) -> [Image] { [first.flatten()] }
    
    public static func buildPartialBlock(first: [Image]) -> [Image] { first }

    public static func buildPartialBlock(accumulated: [Image], next: some Drawable) -> [Image] {
        var acc = accumulated
        acc.append(next.flatten())
        return acc
    }
    
    public static func buildArray(_ components: [[Image]]) -> [Image] { components.joined() }
}

I think the memory leak happens here

Update:

While in this program it seems to be related to the result builders, I also tested a program from a few months ago which worked perfectly fine, but now it also has a huge memory leak despite not using result builders at all.

After downloading one or two previous main toolchains (which still have this issue) I downgraded to the oldest main toolchain still on the download website, and the memory leak is gone in both programs.

I think you're running into the bug fixed by this PR: [embedded] Fix a memory leak caused by incorrect refcounting logic around doNotFreeBit by kubamracek · Pull Request #77121 · swiftlang/swift · GitHub

4 Likes

Thank you, I will wait until it's available in a newer toolchain and see if that fixes it :slightly_smiling_face: