SIL To SWIRL Spec - themaplelab/swan GitHub Wiki
This page specifies how we translate every SIL instruction into raw SWIRL. We try to show each SIL instruction translation in its minimal, example form. Refer to this for information on SIL.
SWIRL aims to support most SIL versions. Apple generally adds, removes, but rarely alters SIL instructions. Therefore, supporting multiple versions of SIL should not be problematic. The instructions listed here do not necessarily exist in the latest SIL documentation. Also, the SIL documentation is not always reliable.
NOTE: Type conversions are not necessarily correct. Example: SIL.rst implies that sometimes @convention is removed (e.g. class_method), but this is not to be trusted until we find it practically consistent.
alloc_stack to new
SIL: %1 = alloc_stack $T
SWIRL: %1 = new $`*T`
alloc_ref to new
SIL: %1 = alloc_ref $T
SWIRL: %1 = new $`T`
alloc_ref_dynamic to new
SIL: %1 = alloc_ref_dynamic %0 : $@thick T.Type, $T
SWIRL: %1 = new $`T`
alloc_box to new
SIL: %1 = alloc_box $T
SWIRL: %1 = new $`*T`
We treat boxes like pointers for now.
alloc_value_buffer to new
SIL: %1 = alloc_value_buffer $T in %0 : $*Builtin.UnsafeValueBuffer
SWIRL: %1 = new $`*T`
alloc_global to NOP
See Globals section.
dealloc_stack to NOP
dealloc_box to NOP
project_box to COPY
SIL: %1 = project_box %0 : $@box T
SWIRL: COPY %0 to %1
Copy the symbol because we treat boxes as pointers. Therefore, the box reference and address of the value inside the box are equivalent.
dealloc_ref to NOP
dealloc_partial_ref to NOP
dealloc_value_buffer to NOP
debug_value to ~NOP
Internally rename the operand to the source name. e.g.,
SIL: debug_value %0 : $HoverCar, let, name "self", argno 1
Will rename %0 to self.
debug_value_addr to ~NOP
Same as debug_value.
load to pointer_read
SIL: %1 = load %0 : $*T
SWIRL: %1 = pointer_read %0, $`T`
store to pointer_write
SIL: store %0 to %1 : $*T
SWIRL: pointer_write %0 to %1
load_borrow to pointer_read
SIL: %1 = load_borrow %0 : $*T
SWIRL: %1 = pointer_read %0, $`T`
store_borrow to pointer_write
SIL: store_borrow %0 to %1 : $*T
SWIRL: pointer_write %0 to %1
begin_borrow to COPY
SIL: %1 = begin_borrow %0 : $T
SWIRL: COPY %0 to %1
end_borrow to NOP
end_lifetime to NOP
copy_addr to MANY
SIL: copy_addr %0 to %1 : $*T
SWIRL: %new = pointer_read %0, $`T`
pointer_write %new to %1
copy_addr performs a deep copy, so we do not use COPY.
destroy_addr to NOP
index_addr to COPY
SIL: %2 = index_addr %0 : $*T, %1 : $Builtin.Int<n>
SWIRL: COPY %0 to %2
Arrays are just copies because we do not handle indices.
index_raw_pointer to COPY
SIL: %2 = index_raw_pointer %0 : $Builtin.RawPointer, %1 : $Builtin.Int<n>
SWIRL: COPY %0 to %2
begin_access to COPY
SIL: %1 = begin_access [read] [unknown] %0 : $*T
SWIRL: COPY %0 to %1
end_access to NOP
begin_unpaired_access to COPY
SIL: %2 = begin_unpaired_access [read] [dynamic] %0 : $*T, %1 : $*Builtin.UnsafeValueBuffer
SWIRL: COPY %0 to %2
end_unpaired_access to COPY
strong_retain to NOP
strong_release to NOP
copy_unowned_value to assign
SIL: %1 = copy_unowned_value %0 : $@sil_unowned T
SWIRL: %1 = assign %0, $`T`
strong_copy_unowned_value to assign
SIL: %1 = strong_copy_unowned_value %0 : $@unowned T
SWIRL: %1 = assign %0, $`T`
set_deallocating to NOP
strong_retain_unowned to NOP
unowned_retain to NOP
unowned_release to NOP
load_weak to pointer_read
SIL: %1 = load %0 : $*@sil_weak Optional<T>
SWIRL: %1 = pointer_read %0, $`Optional<T>`
store_weak to pointer_write
SIL: store_weak %0 to %1 : $*T
SWIRL: pointer_write %0 to %1
load_unowned to pointer_read
SIL: %1 = load %0 : $*@sil_unowned T
SWIRL: %1 = pointer_read %0, $`T`
store_unowned to pointer_write
SIL: store_unowned %0 to %1 : $*T
SWIRL: pointer_write %0 to %1
fix_lifetime to NOP
mark_dependence to COPY
SIL: %2 = mark_dependence %0 : $*T on %1 : $Builtin.NativeObject
SWIRL: COPY %0 to %2
is_unique to unary_op
SIL: %1 = is_unique %0 : $*T
SWIRL: %1 = unary_op [arb] %0, $`Builtin.Int1`
begin_cow_mutation to MANY
SIL: (%1, %2) = begin_cow_mutation %0 : $C
SWIRL: %2 = %0 // (COPY)
%1 = unary_op [arb] %0, $`Builtin.Int1`
end_cow_mutation to NOP
is_escaping_closure to unary_op
SIL: %1 = is_escaping_closure %0 : $@callee_guaranteed () -> ()
SWIRL: %1 = unary_op [arb] %0, $`Builtin.Int1`
The result will be either 1 or 0 and is always of type Builtin.Int1.
copy_block to assign
SIL: %1 = copy_block %0 : $@convention(block) T -> U
SWIRL: %1 = assign %0, $`@convention(block) T -> U`
copy_block_without_escaping to assign
SIL: %1 = copy_block %0 : $@convention(block) T -> U withoutEscaping %2 : $T -> U
SWIRL: %1 = assign %0, $`@convention(block) T -> U`
function_ref to function_ref
SIL: %1 = function_ref @function : $@convention(thin) T -> U
SWIRL: %1 = function_ref @`function`, $`@convention(thin) T -> U`
dynamic_function_ref to function_ref
SIL: %1 = dynamic_function_ref @function : $@convention(thin) T -> U
SWIRL: %1 = function_ref @`function`, $`@convention(thin) T -> U`
Treat as regular function ref for now. We have not yet investigated the dynamic replacement semantics.
prev_dynamic_function_ref to function_ref
SIL: %1 = prev_dynamic_function_ref @function : $@convention(thin) T -> U
SWIRL: %1 = function_ref @`function`, $`@convention(thin) T -> U`
Again, treat as regular function ref for now.
global_addr to singleton_read
SIL: %1 = global_addr @foo : $*Builtin.Word
SWIRL: %1 = singleton_read `foo` from `Globals_<module_name>`, $`*Builtin.Word`
We have a specific instruction for handling global references. Each module has its own unique Global "class" identifier.
integer_literal to literal
SIL: %1 = integer_literal $Builtin.Int<n>, 123
SWIRL: %1 = literal [integer] 123, $`Builtin.Int<n>`
float_integer to literal
SIL: %1 = float_literal $Builtin.FP<n>, 0x3F800000
SWIRL: %1 = literal [float] 0x3F800000, $`Builtin.FP<n>`
string_literal to literal
SIL: %1 = string_literal "asdf"
SWIRL: %1 = literal [string] "asdf", $`Builtin.RawPointer`
class_method to dynamic_ref
SIL: %1 = class_method %0 : $T, #T.method : $@convention(class_method) U -> V
SWIRL: %1 = dynamic_ref @`#T.method`, $`@convention(class_method) U -> V`
See the Dynamic Dispatch section for more information.
objc_method to builtin_ref
SIL: %1 = objc_method %0 : $T, #T.method!foreign : $@convention(objc_method) U -> V
SWIRL: %1 = builtin_ref @`#T.method!foreign`, $`@convention(thin) U -> V`
super_method to dynamic_ref
SIL: %1 = super_method %0 : $T, #Super.method : $@convention(thin) U -> V
SWIRL: %1 = dynamic_ref @`#Super.method`, $`@convention(thin) U -> V`
%0 is a metatype, so we treat this as a regular dynamic dispatch. In the future, we may add metatype dataflow resolution to handle cases like this.
objc_super_method to builtin_ref
SIL: %1 = super_method %0 : $T, #Super.method : $@convention(thin) U -> V
SWIRL: %1 = builtin_ref @`#Super.method`, $`@convention(thin) U -> V`
witness_method to dynamic_ref
SIL: %1 = witness_method $T, #Proto.method : $@convention(witness_method) <Self: Proto> U -> V
SWIRL: %1 = dynamic_ref @`#Proto.method`, $`@convention(thin) <Self: Proto> U -> V`
apply to apply
SIL: %r = apply %0(%1, %2, ...) : $(A, B, ...) -> R
SWIRL: %r = apply %0(%1, %2, ...), $`R`
begin_apply to apply
SIL: (%anyAddr, %float, %token) = begin_apply %0(%1, %2, ...) : $@yield_once () -> (@yields @inout %Any, @yields Float)
SWIRL: %anyAddr = apply %0(%1, %2, ...) : $`(@yields @inout %Any, @yields Float)`
The yielded value (%anyAddr) is optional, so a value will be automatically generated if it does not exist to stay consistent with the apply return type requirement. We ignore %float and %token. See Coroutines section for more information.
abort_apply to NOP
end_apply to NOP
partial_apply to new 🚧
SIL: %c = partial_apply %0(%1, %2, ...) : $(Z..., A, B, ...) -> R
SWIRL: %c = new $`R`
We do not handle closures, so we use the new instruction for now.
builtin to MANY
SIL: %1 = builtin "#foo"(%1 : $T, %2 : $U) : $V
SWIRL: %new = builtin_ref @`#foo`, $`Any`
%1 = apply %new(%1, %2), $`V`
Need to generate builtin_ref first (to which the result type is unknown and hence Any) and then apply the ref.
metatype to new
SIL: %1 = metatype $T.Type
SWIRL: %1 = new $`T.Type`
new for now. Wherever SIL uses a metatype, it should be obvious it is a metatype without looking at the value allocation. Therefore, we do not have any additional handling of metatype. These statements apply to all metatype instruction variants. Metatypes are important because they are used to bootstrap the AppDelegate, for instance.
value_metatype to new
SIL: %1 = value_metatype $T.Type, %0 : $T
SWIRL: %1 = new $`T.Type`
existential_metatype to new
SIL: %1 = existential_metatype $P.Type, %0 : $P
SWIRL: %1 = new $`P.Type`
objc_protocol to new
SIL: %0 = objc_protocol #ObjCProto : $Protocol
SWIRL: %1 = new $`Protocol`
retain_value to NOP
retain_value_addr to NOP
unmanaged_retain_value to NOP
copy_value to assign
SIL: %1 = copy_value %0 : $A
SWIRL: %1 = assign %0, $`A`
strong_copy_unmanaged_value to COPY
SIL: %1 = strong_copy_unmanaged_value %0 : $@sil_unmanaged A
SWIRL: %1 = assign %0, $`@owned A`
release_value to NOP
release_value_addr to NOP
unmanaged_release_value to NOP
destroy_value to NOP
autorelease_value to NOP
unmanaged_autorelease_value to NOP
tuple to MANY
SIL: %1 = tuple (%a : $A, %b : $B, ...)
or
%1 = tuple $(a:A, b:B, ...) (%a, %b, ...)
SWIRL: %1 = new $`(A, B, ...)`
field_write %a to %1, 0
field_write %b to %1, 1
field_write ... to %1, n
We treat tuples as objects and use tuple "indices" as the field identifiers.
tuple_extract to field_read
SIL: %1 = tuple_extract %0 : $(T...), 123
SWIRL: %1 = field_read %0, 123, $`(T...)`
tuple_element_addr to MANY
SIL: %1 = tuple_element_addr %0 : $*(T...), 123
SWIRL: %1 = new $`*U`
%new = field_read [alias %1], 123, $`U`
pointer_write %new to %1
// U is the type of the 123rd element of T
The result type can be statically determined by looking at the 123rd type of $*(T...). This determined type is represented by U.
destructure_tuple to MANY
SIL: (%elt1, ..., %eltn) = destructure_tuple %0 : $(Elt1Ty, ..., EltNTy)
SWIRL: %elt1 = field_read %0, 1, $`Elt1Ty`
...
%eltn = field_read %0, N, $`EltNTy`
struct to MANY
SIL: %1 = struct $S (%a : $A, %b : $B, ...)
SWIRL: %1 = new $`S`
field_write %a to %1, $`<field 0 from init spec>`
field_write %b to %1, $`<field 1 from init spec>`
...
The SIL parser provides information about the order of struct field names for initialization ("init spec").
struct_extract to field_read
SIL: %1 = struct_extract %0 : $S, #S.field
SWIRL: %1 = field_read %0, field, $`Any`
We do not know the result type from the plain-text SIL instruction, so we use Any as the return type.
struct_element_addr to MANY
SIL: %1 = struct_element_addr %0 : $*S, #S.field
SWIRL: %1 = new $`*Any`
%obj = pointer_read %0, $`*Any`
%new = field_read [alias %1] %obj, field, $`Any`
pointer_write %new to %1
We do not know the result type from the plain-text SIL instruction, so we use Any as the type for %1.
destructure_struct to MANY
SIL: (%elt1, ..., %eltn) = destructure_struct %0 : $S
SWIRL: %elt1 = field_read, %0, field1, $`Any`
...
%eltn = field_read, %0, fieldn, $`Any`
// --- OR ---
%elt1 = assign, %0, $`Any`
...
%eltn = assign, %0, $`Any`
If the structure field names are available (from the "init spec"), deconstruct the struct with field_read instructions. Otherwise, just copy the struct to the results as a fallback.
object to new
SIL: %1 = object $T (%a : $A, %b : $B, ...)
SWIRL: %1 = new $`T`
object is an instruction only used during static initialization. It only appears as the final instruction in a global variable initializer "function". We do not know the field names that object populates, so we only translate it to a new instruction and ignore the operands. However, this should be okay because SIL should not taint the static values in initialization. The static information written to the object is very likely uninteresting.
ref_element_addr to MANY
SIL: %1 = ref_element_addr %0 : $C, #C.field
SWIRL: %1 = new $`*Any`
%new = field_read [alias %1] %0, field, $`Any`
pointer_write %new to %1
We do not know the result type from the plain-text SIL instruction, so we use Any as the type for %1.
ref_tail_addr to assign
SIL: %1 = ref_tail_addr %0 : $C, $E
SWIRL: %1 = assign %0, $`*E`
This instruction appears this is a type of low-level array read, so we just use assign (for now).
enum to MANY
SIL: %1 = enum $U, #U.DataCase!enumelt, %0 : $T
SWIRL: %1 = new $`U`
$new = literal [string] "#U.DataCase!enumelt", $`Builtin.RawPointer`
field_write %new to %1, type
field_write %0 to %1, data
We treat an enum value as an object with two fields: type and data. We populate the type field with the case name (and use the decl name directly). Then, we populate the data field if the SIL instruction contains the optional operand.
unchecked_enum_data to field_read
SIL: %1 = unchecked_enum_data %0 : $U, #U.DataCase!enumelt
SWIRL: %1 = field_read %0, data, $`Any`
We do not know the result type from the plain-text SIL instruction, so we use Any as the type for %1.
init_enum_data_addr to MANY
SIL: %1 = init_enum_data_addr %0 : $*U, #U.DataCase!enumelt
SWIRL: %1 = new $`*Any`
%new = field_read [alias %1] %0, data, $`Any`
pointer_write %new to %1
We do not know the result type from the plain-text SIL instruction, so we use Any as the type for %1.
inject_enum_addr to MANY
SIL: inject_enum_addr %0 : $*U, #U.Case!enumelt
SWIRL: %new = literal [string] "#U.Case!enumelt", $`Builtin.RawPointer`
field_write %new to %1, type
unchecked_take_enum_data_addr to MANY
SIL: %1 = unchecked_take_enum_data_addr %0 : $*U, #U.DataCase!enumelt
SWIRL: %1 = new $`*Any`
%new = field_read [alias %1] %0, data, $`Any`
pointer_write %new to %1
We do not know the result type from the plain-text SIL instruction, so we use Any as the type for %1.
select_enum to switch_enum_assign
SIL: %n = select_enum %0 : $U, \
case #U.Case1!enumelt: %1, \
case #U.Case2!enumelt: %2, /* ... */ \
default %3 : $T
SWIRL: %n = switch_enum_assign %0, \
case "#U.Case1!enumelt" : %1, \
case "#U.Case2!enumelt" : %2, \
default %3, $`T`
select_enum_addr to MANY
SIL: %n = select_enum_addr %0 : $*U, \
case #U.Case1!enumelt: %1, \
case #U.Case2!enumelt: %2, /* ... */ \
default %3 : $T
SWIRL: %new = pointer_read %0
%n = switch_enum_assign %new, \
case "#U.Case1!enumelt" : %1, \
case "#U.Case2!enumelt" : %2, \
default %3, $T
NOTE: The following instructions that use COPY ignore the type mismatch between the 'from' and 'to' values. See the Types section for more information.
init_existential_addr to COPY
SIL: %1 = init_existential_addr %0 : $*P, $T
SWIRL: COPY %0 to %1
deinit_existential_addr to NOP
open_existential_addr to COPY
SIL: %1 = open_existential_addr immutable_access %0 : $*P to $*@opened P
SWIRL: COPY %0 to %1
init_existential_ref to COPY
SIL: %1 = init_existential_ref %0 : $C' : $C, $P
SWIRL: COPY %0 to %1
open_existential_ref to COPY
SIL: %1 = open_existential_ref %0 : $P to $@opened P
SWIRL: COPY %0 to %1
init_existential_metatype to COPY
SIL: %1 = init_existential_metatype %0 : $@<rep> T.Type, $@<rep> P.Type
SWIRL: COPY %0 to %1
open_existential_metatype to COPY
SIL: %1 = open_existential_metatype %0 : $@<rep> P.Type to $@<rep> (@opened P).Type
SWIRL: COPY %0 to %1
alloc_existential_box to new
SIL: %1 = alloc_existential_box $P, $T
SWIRL: %1 = new $`*P`
project_existential_box to COPY
SIL: %1 = project_existential_box $T in %0 : $P
SWIRL: COPY %0 to %1
open_existential_box to COPY
SIL: %1 = open_existential_box %0 : $P to $*@opened P
SWIRL: COPY %0 to %1
dealloc_existential_box to NOP
project_block_storage to COPY
SIL: %1 = project_block_storage %0 : $T
SWIRL: COPY %0 to %1
We use COPY for now, but SWAN 1.0 had this as a deep copy.
init_block_storage_header to new 🚧
SIL: %2 = init_block_storage_header %0 : $*@block_storage @callee_guaranteed () -> (), invoke %1 : $@convention(c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> (), type $@convention(block) () -> ()
SWIRL: %2 = new $`$@convention(block) () -> ()`
We do not adequately handle this instruction yet and translate it to new for now.
upcast to COPY
SIL: %1 = upcast %0 : $D to $B
SWIRL: COPY %0 to %1
address_to_pointer to COPY
SIL: %1 = address_to_pointer %0 : $*T to $Builtin.RawPointer
SWIRL: COPY %0 to %1
pointer_to_address to COPY
SIL: %1 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*T
SWIRL: COPY %0 to %1
unchecked_ref_cast to COPY
SIL: %1 = unchecked_ref_cast %0 : $A to $B
SWIRL: COPY %0 to %1
unchecked_ref_cast_addr to MANY
SIL: unchecked_ref_cast_addr $A in %0 : $*A to $B in %1 : $*B
SWIRL: %new = pointer_read %0, $`T`
pointer_write %new to %1
unchecked_addr_cast to COPY
SIL: %1 = unchecked_addr_cast %0 : $*A to $*B
SWIRL: COPY %0 to %1
unchecked_trivial_bit_cast to COPY
SIL: %1 = unchecked_trivial_bit_cast %0 : $Builtin.NativeObject to $Builtin.Word
SWIRL: COPY %0 to %1
unchecked_bitwise_cast to COPY
SIL: %1 = unchecked_bitwise_cast %0 : $A to $B
SWIRL: COPY %0 to %1
unchecked_ownership_conversion to COPY
SIL: %1 = unchecked_ownership_conversion %0 : $A, @guaranteed to @owned
SWIRL: COPY %0 to %1
ref_to_raw_pointer to COPY
SIL: %1 = ref_to_raw_pointer %0 : $C to $Builtin.RawPointer
SWIRL: COPY %0 to %1
raw_pointer_to_ref to COPY
SIL: %1 = raw_pointer_to_ref %0 : $Builtin.RawPointer to $C
SWIRL: COPY %0 to %1
ref_to_unowned to COPY
SIL: %1 = unowned_to_ref %0 : T
SWIRL: COPY %0 to %1
unowned_to_ref to COPY
SIL: %1 = unowned_to_ref %0 : $@unowned T
SWIRL: COPY %0 to %1
ref_to_unmanaged to COPY
SIL: %1 = ref_to_unmanaged %0 : $T to $@sil_unmanaged T
SWIRL: COPY %0 to %1
umanaged_to_ref to COPY
SIL: %1 = unmanaged_to_ref %0 : $@sil_unmanaged T to $T
SWIRL: COPY %0 to %1
convert_function to COPY
SIL: %1 = convert_function %0 : $T -> U to $T' -> U'
SWIRL: COPY %0 to %1
convert_escape_to_noescape to COPY
SIL: %1 = convert_escape_to_noescape %0 : $T -> U to $@noescape T' -> U'
SWIRL: COPY %0 to %1
classify_bridge_object to COPY
SIL: %1 = classify_bridge_object %0 : $Builtin.BridgeObject
SWIRL: COPY %0 to %1
value_to_bridge_object to COPY
SIL: %1 = value_to_bridge_object %0 : $T
SWIRL: COPY %0 to %1
ref_to_bridge_object to COPY
SIL: %2 = ref_to_bridge_object %0 : $C, %1 : $Builtin.Word
SWIRL: COPY %0 to %2
bridge_object_to_ref to COPY
SIL: %1 = bridge_object_to_ref %0 : $Builtin.BridgeObject to $C
SWIRL: COPY %0 to %1
bridge_object_to_word to COPY
SIL: %1 = bridge_object_to_word %0 : $Builtin.BridgeObject to $Builtin.Word
SWIRL: COPY %0 to %1
thin_to_thick_function to COPY
SIL: %1 = thin_to_thick_function %0 : $@convention(thin) T -> U to $T -> U
SWIRL: COPY %0 to %1
thick_to_objc_metatype to COPY
SIL: %1 = thick_to_objc_metatype %0 : $@thick T.Type to $@objc_metatype T.Type
SWIRL: COPY %0 to %1
objc_to_thick_metatype to COPY
SIL: %1 = objc_to_thick_metatype %0 : $@objc_metatype T.Type to $@thick T.Type
SWIRL: COPY %0 to %1
objc_metatype_to_object to COPY
SIL: %1 = objc_metatype_to_object %0 : $@objc_metatype Y.Type to $AnyObject
SWIRL: COPY %0 to %1
objc_existential_metatype_to_object to COPY
SIL: %1 = objc_existential_metatype_to_object %0 : $@objc_metatype P.Type to $AnyObject
SWIRL: COPY %0 to %1
unconditional_checked_cast to COPY
SIL: %1 = unconditional_checked_cast %0 : $A to $B
SWIRL: COPY %0 to %1
unconditional_checked_cast_addr to NOP
No result for instruction.
select_value to switch_value_assign
SIL: %n = select_value %0 : $U, \
case %c1: %r1, \
case %c2: %r2, /* ... */ \
default %r3 : $T
SWIRL: %n = switch_value_assign %0, \
case %c1 : %1r, \
case %c2 : %r2, \
default %r3, $`T`
keypath to new 🚧
SIL: %10 = keypath $ReferenceWritableKeyPath<ExposureDetectionViewModel, Published<Bool>>, (root $ExposureDetectionViewModel; stored_property #ExposureDetectionViewModel._isButtonHidden : $Published<Bool>)
SWIRL: %10 = new $`ReferenceWritableKeyPath<ExposureDetectionViewModel`
This is a rare undocumented instruction. It presumably implements Swift's keypath feature, and therefore might be important for us to properly handle. However, for now we just translate it to new because our parsing of this instruction is likely incomplete and we need to investigate its semantics further.
cond_fail to cond_fail
SIL: cond_fail %0 : $Builtin.Int1, "failure reason"
SWIRL: cond_fail %0
Ignore static failure message for debugger because it is uninteresting for dataflow.
unreachable to unreachable
SIL: unreachable
SWIRL: unreachable
return to return
SIL: return $0 : $T
SWIRL: return %0
throw to return
SIL: throw %0 : $T
SWIRL: return %0
yield to yield
SIL: yield %0 : $T, resume bb1, unwind bb2
SWIRL: yield (%0), resume bb1, unwind bb2
unwind to return
SIL: unwind
SWIRL: %dummy = new $`<function return type>`
return %dummy
br to br
SIL: br label(%0 : $A, %1 : $B, ...)
SWIRL: br label(%0, %1, ...)
cond_br to cond_br
SIL: cond_br %0 : $Builtin.Int1, true_label (%a : $A, %b : $B, ...), \
false_label (%x : $X, %y : $Y, ...)
SWIRL: cond_br %0, true bb1(%a, %b, ...), false bb2(%x, %y, ...)
switch_value to switch
SIL: switch_value %0 : $Builtin.Int<n>, case %1: label1, \
case %2: label2, \
..., \
default labelN
SWIRL: switch %0, case %1 : label1, case %2 : label2, ..., default labelN
switch_enum to switch_enum
SIL: switch_enum %0 : $U, case #U.Foo!enumelt: label1, \
case #U.Bar!enumelt: label2, \
..., \
default labelN
SWIRL: switch_enum %0, case "#U.Case1!enumelt" : label1, \
case "#U.Case2!enumelt" : label2, \
...
default labelN
switch_enum_addr to MANY
SIL: switch_enum_addr %0 : $*U, case #U.Foo!enumelt: label1, \
case #U.Bar!enumelt: label2, \
..., \
default labelN
SWIRL: %new = pointer_read %0, $`U`
switch_enum %new, case "#U.Case1!enumelt" : label1, \
case "#U.Case2!enumelt" : label2, \
...
default labelN
dynamic_method_br to MANY
SIL: dynamic_method_br %0 : $P, #X.method, bb1, bb2
SWIRL: %new = unary_op [arb] %0, $`Builtin.Int1`
cond_br %new, true bb1, false bb2
For now, treat this instruction as an arbitrary conditional branch.
checked_cast_br to MANY
SIL: checked_cast_br %0 : $A to $B, bb1, bb2
SWIRL: %new = unary_op [arb] %0, $Builtin.Int1
cond_br %new, true bb1, false bb2
For now, treat this instruction as an arbitrary conditional branch.
checked_cast_addr_br to MANY
SIL: checked_cast_value_br take_always Any in %1 : $*Any to T in %2 : $*T, bb1, bb2
SWIRL: %new = binary_op %1 [arb] %2, $`Builtin.Int1`
cond_br %new, true bb1, false bb2
For now, treat this instruction as an arbitrary conditional branch.
try_apply to MANY
SIL: try_apply %0(%1, %2, ...) : $(A, B, ...) -> (R, @error E), normal bb1, error bb2
SWIRL: %new = apply %0(%1, %2, ...), $`R`
%cond = new $`Builtin.Int1`
cond_br %cond, bb1(%new), bb2(%new)
We over-approximate try_apply by converting it to an apply and branching to both the normal and error block based. throw is treated as a return, and therefore %new represents both the regular return value and the $Error value, which is sound.