Compiler not specializing local existentials?

recently i think it has become fashionable to jettison traditional allergies to swift existentials, which made me wonder if i could get away with this pattern:

public
func f1(items:[Int], b:Bool) -> Int
{
    let foldable:any Foldable
    if  b 
    { 
        foldable = B.init(repeating: items)
    }
    else 
    {
        foldable = A.init(items: items)
    }
    return foldable.folded()
}

(why do this in a real code base? because the .folded() call might be complex and i would rather not repeat it in every branch.)

i compared it against the “proper” factorization, which looks like:

public
func f2(items:[Int], b:Bool) -> Int
{
    if  b 
    { 
        let foldable:B = .init(repeating: items)
        return foldable.folded()
    }
    else 
    {
        let foldable:A = .init(items: items)
        return foldable.folded()
    }
}

according to godbolt, the spelling that uses the local existential is still going through all the existential boxing and witness lookups that a fully dynamic existential would go through.

        lea     rbx, [rip + (full type metadata for output.B)+16]
        mov     qword ptr [rsp + 24], rbx
        lea     r14, [rip + (protocol witness table for output.B : output.Foldable in output)]
        mov     qword ptr [rsp + 32], r14
        mov     qword ptr [rsp], rax
        jmp     .LBB6_3
.LBB6_2:
        lea     rbx, [rip + (full type metadata for output.A)+16]
        mov     qword ptr [rsp + 24], rbx
        lea     r14, [rip + (protocol witness table for output.A : output.Foldable in output)]
        mov     qword ptr [rsp + 32], r14
        mov     qword ptr [rsp], r15
        mov     rdi, r15
        call    swift_retain@PLT
.LBB6_3:
        mov     r15, rsp
        mov     rdi, r15
        mov     rsi, rbx
        call    __swift_project_boxed_opaque_existential_1
        mov     rdi, rbx
        mov     rsi, r14
        mov     r13, rax
        call    ((extension in output):output.Foldable.folded() -> Swift.Int)
        mov     rbx, rax
        mov     rdi, r15
        call    __swift_destroy_boxed_opaque_existential_1
        mov     rax, rbx
        add     rsp, 40
        pop     rbx
        pop     r13
        pop     r14
        pop     r15
        ret

i checked it on 5.9 and also on main. any reason why the compiler can’t do this optimization?

The optimizer’s concrete existential peephole specifically only applies in the case where the existential value being opened is the target of a single initializing store with a concrete type. In your example the control flow join point prevents this from being the case.

3 Likes