Stack promotion of reference types

Hello :slight_smile:

As I was digging a little deeper into the topic of how memory management is done by the swift compiler, I read through the SIL documentation and came across the alloc_ref instruction.
The description states that

The optional stack attribute indicates that the object can be allocated on the stack instead on the heap.

From what I understand is that as long as the size of a type is fixed (e.g. simple struct) it can be stored on the stack.

Now trying to come up with an example where the [stack] flag is used didn't really satisfy my expectations:

final class Foo {
    let bar = 0
}

The SIL output (using swiftc -emit-silgen -O main.swift) gives me:

// Foo.__allocating_init()
sil hidden [exact_self_class] [ossa] @$s4main3FooCACycfC : $@convention(method) (@thick Foo.Type) -> @owned Foo {
// %0 "$metatype"
bb0(%0 : $@thick Foo.Type):
  %1 = alloc_ref $Foo                             // user: %3
  // function_ref Foo.init()
  %2 = function_ref @$s4main3FooCACycfc : $@convention(method) (@owned Foo) -> @owned Foo // user: %3
  %3 = apply %2(%1) : $@convention(method) (@owned Foo) -> @owned Foo // user: %4
  return %3 : $Foo                                // id: %4
} // end sil function '$s4main3FooCACycfC'

// Foo.init()
sil hidden [ossa] @$s4main3FooCACycfc : $@convention(method) (@owned Foo) -> @owned Foo {
// %0 "self"                                      // users: %2, %1
bb0(%0 : @owned $Foo):
  debug_value %0 : $Foo, let, name "self", argno 1 // id: %1
  %2 = mark_uninitialized [rootself] %0 : $Foo    // users: %10, %9, %3
  %3 = begin_borrow %2 : $Foo                     // users: %8, %4
  %4 = ref_element_addr %3 : $Foo, #Foo.bar       // user: %7
  // function_ref variable initialization expression of Foo.bar
  %5 = function_ref @$s4main3FooC3barSivpfi : $@convention(thin) () -> Int // user: %6
  %6 = apply %5() : $@convention(thin) () -> Int  // user: %7
  store %6 to [trivial] %4 : $*Int                // id: %7
  end_borrow %3 : $Foo                            // id: %8
  %9 = copy_value %2 : $Foo                       // user: %11
  destroy_value %2 : $Foo                         // id: %10
  return %9 : $Foo                                // id: %11
} // end sil function '$s4main3FooCACycfc'

Am I missing something here or do I not understand the concept of a fixed size type?

Further sources:

Any reading material on this topic are more than welcome !

2 Likes

Foo.init() won't perform stack promotion because it returns the value it allocates. Try something like

func f() -> Int {
  let foo = Foo()
  return foo.bar
}
1 Like