The SIL here was emitted for a -Onone Swift 3.1 build, and is completely subject to change with optimisations and future versions of Swift.
For the following code:
func aaa(_ key: UnsafeRawPointer) {}
class A {
var key = "aaa"
}
let a = A()
aaa(&a.key)
A simplified look at the SIL emitted
(we're not considering an extra parameter for now – as it doesn't make a difference in unoptimised builds)
is as follows for the call to aaa
:
// get reference to function 'aaa'.
%13 = function_ref @main.aaa (Swift.UnsafeRawPointer) -> ()
// load the reference to the instance of A from the global variable 'a'.
%14 = load %8 : $*A
// create temporary variable to hold a.key
%15 = alloc_stack $String
// get a reference to a.key's getter
%16 = class_method %14 : $A, #A.key!getter.1 : (A) -> () -> String
// call a.key's getter
%17 = apply %16(%14) : $(A) -> String
// store the result to the temporary variable.
store %17 to %15 : $*String
// get the address of the temporary variable.
%19 = address_to_pointer %15 : $*String to $Builtin.RawPointer
// wrap the address in a UnsafeRawPointer.
%20 = struct $UnsafeRawPointer (%19 : $Builtin.RawPointer)
// finally, call 'aaa' with the pointer as the argument.
%23 = apply %13(%20) : $(UnsafeRawPointer) -> ()
For the following code:
func aaa(_ key: UnsafeRawPointer) {}
class A {
final var key = "aaa"
}
let a = A()
aaa(&a.key)
A simplified look at the SIL emitted for the call to aaa
:
// get reference to function 'aaa'.
%13 = function_ref @main.aaa (Swift.UnsafeRawPointer) -> ()
// load the reference to the instance of A from the global variable 'a'.
%14 = load %8 : $*A
// get the address of the 'key' property on the instance.
%15 = ref_element_addr %14 : $A, #A.key
// convert that address to a Builtin.RawPointer.
%16 = address_to_pointer %15 : $*String to $Builtin.RawPointer
// wrap the pointer in an UnsafeRawPointer.
%17 = struct $UnsafeRawPointer (%16 : $Builtin.RawPointer)
// finally, call 'aaa' with the pointer as the argument.
%20 = apply %13(%17) : $(UnsafeRawPointer) -> ()
For the following code:
func aaa(_ key: inout String) {}
class A {
var key = "aaa"
}
let a = A()
aaa(&a.key)
A 'simplified' look at the SIL emitted for the call to aaa
:
// get reference to function 'aaa'.
%8 = function_ref @main.aaa (inout Swift.String) -> ()
// load the reference to the instance of A from the global variable 'a'.
%9 = load %3 : $*A
// create temporary UnsafeValueBuffer (I'm actually not too sure what this is used for).
%10 = alloc_stack $Builtin.UnsafeValueBuffer
// create temporary variable to potentially store the value from a.key's getter,
// if a direct pointer to the storage cannot be provided.
%11 = alloc_stack $String
// get a pointer to the temporary variable.
%12 = address_to_pointer %11 : $*String to $Builtin.RawPointer
// get a reference to a.key's materializeForSet accessor.
%13 = class_method %9 : $A, #A.key!materializeForSet.1 : $(Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, A) -> (Builtin.RawPointer, Optional<Builtin.RawPointer>)
// call materializeForSet with the pointer to the temporary storage for a.key, along with the UnsafeValueBuffer and instance of A.
%14 = apply %13(%12, %10, %9) : $(Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, A) -> (Builtin.RawPointer, Optional<Builtin.RawPointer>)
// get the first element of the tuple returned – which is the pointer to a.key,
// this is either a direct pointer, or a pointer to the temporary variable (in this case, it's the former).
%15 = tuple_extract %14 : $(Builtin.RawPointer, Optional<Builtin.RawPointer>), 0
// get the second element of the tuple returned – simply a flag to know if we have a direct pointer.
%16 = tuple_extract %14 : $(Builtin.RawPointer, Optional<Builtin.RawPointer>), 1
// convert the pointer address to a *String address.
%17 = pointer_to_address %15 : $Builtin.RawPointer to [strict] $*String
// ensure that the instance of A is not deallocated until after we're done using the pointer to a.key.
%18 = mark_dependence %17 : $*String on %9 : $A
// call 'aaa' with the reference to a.key, passed as an @inout argument (which is by-reference).
%19 = apply %8(%18) : (@inout String) -> ()
// switch over the flag of whether we have a direct pointer to a.key.
//
// If we do have a direct pointer (flag will be .none), go to bb1 (which skips to bb3).
//
// If we don't (flag will be .some, with a wrapped value of a reference to the setter),
// go to bb2, which handles writing the value back to a.key's setter.
//
switch_enum %16 : $Optional<Builtin.RawPointer>,
case #Optional.some!enumelt.1: bb2,
case #Optional.none!enumelt: bb1
bb1:
br bb3
bb2(%22 : $Builtin.RawPointer):
// cast the wrapped value to a reference to a.key's setter, then apply with the pointer to the temporary variable,
// as well as the UnsafeValueBuffer, instance of A, and metatype of A.
%23 = pointer_to_thin_function %22 : $Builtin.RawPointer to $(Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, @inout A, @thick A.Type) -> ()
%24 = alloc_stack $A
store %9 to %24 : $*A
%26 = metatype $@thick A.Type
%27 = address_to_pointer %18 : $*String to $Builtin.RawPointer
%28 = apply %23(%27, %10, %24, %26) : $(Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, @inout A, @thick A.Type) -> ()
dealloc_stack %24 : $*A
br bb3
bb3:
dealloc_stack %11 : $*String
dealloc_stack %10 : $*Builtin.UnsafeValueBuffer
And the SIL for a.key
's materializeForSet
acccessor:
// A.key.materializeForSet
sil hidden @main.A.key.materializeForSet : Swift.String : $(Builtin.RawPointer, @inout Builtin.UnsafeValueBuffer, @guaranteed A) -> (Builtin.RawPointer, Optional<Builtin.RawPointer>) {
bb0(%0 : $Builtin.RawPointer, %1 : $*Builtin.UnsafeValueBuffer, %2 : $A):
// simply get a reference to the instance's storage for the 'key' property.
%3 = ref_element_addr %2 : $A, #A.key
// convert this reference to a Builtin.RawPointer.
%4 = address_to_pointer %3 : $*String to $Builtin.RawPointer
// simply return the pointer, along with a flag to indicate that it's a direct pointer to the storage.
%5 = enum $Optional<Builtin.RawPointer>, #Optional.none!enumelt
%6 = tuple (%4 : $Builtin.RawPointer, %5 : $Optional<Builtin.RawPointer>)
return %6 : $(Builtin.RawPointer, Optional<Builtin.RawPointer>)
}
Which shows that we're getting a direct pointer to a.key
's storage.