Stack Protectors in Swift

I've noticed this runtime stack protection happening even in places where I would expect the compiler to provide things safe statically.

I'm using withUnsafeBytes(of: self) to work around the "outlined init with take" issue. It's important that I do that, because just applying it to one tiny enum improved the performance of lots of complex processing by up to 24%. Obviously I don't like to write this kind of code, and I don't recommend anybody does that by default, but it's a work-around for a demonstrable issue.

And it's still an issue, as of the latest nightly:

enum MyEnum {
    case one
    case four
}

struct HasAnEnum {
    var something: String
    var somethingTwo: String
    var value: MyEnum?
}

func test(_ input: HasAnEnum) -> Int {
    // This load results in very expensive runtime calls!
    let x = input.value
    if case .four = x { return 4 }
    return -1
}

func test2(_ input: HasAnEnum) -> Int {
    // Super ugly, but we can wrap this in a computed property to contain it.
    // It's so much faster.
    let x = withUnsafeBytes(of: input) {
        let offset = MemoryLayout<HasAnEnum>.offset(of: \.value)!
        return $0.load(fromByteOffset: offset, as: MyEnum?.self)
    }
    if case .four = x { return 4 }
    return -1
}

Godbolt

Unfortunately, I'm seeing that this load requires runtime stack protection checks. It seems strange - we get the offset from MemoryLayout, and the compiler constant-folds that (not seeing any runtime calls), and there doesn't seem to be any complex inter-procedural stuff going on. I'd expect the compiler to know that this is in-bounds.

Is this something that could reasonably be improved? Or does this involve some complex analysis that I'm not appreciating?

1 Like