SIL To SWIRL Spec - themaplelab/swan Wiki

SWIRLGen: SIL to SWIRL Compilation

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 : [email protected] 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 : [email protected] 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 : [email protected]_unowned T

SWIRL: %1 = assign %0, $`T`

strong_copy_unowned_value to assign

SIL:   %1 = strong_copy_unowned_value %0 : [email protected] 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 : [email protected]_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 : [email protected](block) T -> U

SWIRL: %1 = assign %0, $`@convention(block) T -> U`

copy_block_without_escaping to assign

SIL:   %1 = copy_block %0 : [email protected](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 : [email protected](thin) T -> U

SWIRL: %1 = function_ref @`function`, $`@convention(thin) T -> U`

dynamic_function_ref to function_ref

SIL:   %1 = dynamic_function_ref @function : [email protected](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 : [email protected](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 : [email protected](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 : [email protected](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 : [email protected](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 : [email protected](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 : [email protected](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, ...) : [email protected]_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 :construction:

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 : [email protected]_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 [email protected] P

SWIRL: COPY %0 to %1

init_existential_metatype to COPY

SIL:   %1 = init_existential_metatype %0 : [email protected]<rep> T.Type, [email protected]<rep> P.Type

SWIRL: COPY %0 to %1

open_existential_metatype to COPY

SIL:   %1 = open_existential_metatype %0 : [email protected]<rep> P.Type to [email protected]<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 :construction:

SIL:   %2 = init_block_storage_header %0 : $*@block_storage @callee_guaranteed () -> (), invoke %1 : [email protected](c) (@inout_aliasable @block_storage @callee_guaranteed () -> ()) -> (), type [email protected](block) () -> ()

SWIRL: %2 = new $`[email protected](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_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 : [email protected] T

SWIRL: COPY %0 to %1

ref_to_unmanaged to COPY

SIL:   %1 = ref_to_unmanaged %0 : $T to [email protected]_unmanaged T

SWIRL: COPY %0 to %1

umanaged_to_ref to COPY

SIL:   %1 = unmanaged_to_ref %0 : [email protected]_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 [email protected] 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 : [email protected](thin) T -> U to $T -> U

SWIRL: COPY %0 to %1

thick_to_objc_metatype to COPY

SIL:   %1 = thick_to_objc_metatype %0 : [email protected] T.Type to [email protected]_metatype T.Type

SWIRL: COPY %0 to %1

objc_to_thick_metatype to COPY

SIL:   %1 = objc_to_thick_metatype %0 : [email protected]_metatype T.Type to [email protected] T.Type

SWIRL: COPY %0 to %1

objc_metatype_to_object to COPY

SIL:   %1 = objc_metatype_to_object %0 : [email protected]_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 : [email protected]_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 :construction:

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.