mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
5352 lines
194 KiB
ReStructuredText
5352 lines
194 KiB
ReStructuredText
.. @raise litre.TestsAreMissing
|
|
.. highlight:: none
|
|
|
|
Swift Intermediate Language (SIL)
|
|
=================================
|
|
|
|
.. contents::
|
|
|
|
Abstract
|
|
--------
|
|
|
|
SIL is an SSA-form IR with high-level semantic information designed to implement
|
|
the Swift programming language. SIL accommodates the following use cases:
|
|
|
|
- A set of guaranteed high-level optimizations that provide a predictable
|
|
baseline for runtime and diagnostic behavior.
|
|
- Diagnostic dataflow analysis passes that enforce Swift language requirements,
|
|
such as definitive initialization of variables and constructors, code
|
|
reachability, switch coverage.
|
|
- High-level optimization passes, including retain/release optimization,
|
|
dynamic method devirtualization, closure inlining, memory allocation promotion,
|
|
and generic function instantiation.
|
|
- A stable distribution format that can be used to distribute "fragile"
|
|
inlineable or generic code with Swift library modules, to be optimized into
|
|
client binaries.
|
|
|
|
In contrast to LLVM IR, SIL is a generally target-independent format
|
|
representation that can be used for code distribution, but it can also express
|
|
target-specific concepts as well as LLVM can.
|
|
|
|
SIL in the Swift Compiler
|
|
-------------------------
|
|
|
|
At a high level, the Swift compiler follows a strict pipeline architecture:
|
|
|
|
- The *Parse* module constructs an AST from Swift source code.
|
|
- The *Sema* module type-checks the AST and annotates it with type information.
|
|
- The *SILGen* module generates *raw SIL* from an AST.
|
|
- A series of *Guaranteed Optimization Passes* and *Diagnostic Passes* are run
|
|
over the raw SIL both to perform optimizations and to emit
|
|
language-specific diagnostics. These are always run, even at -Onone, and
|
|
produce *canonical SIL*.
|
|
- General SIL *Optimization Passes* optionally run over the canonical SIL to
|
|
improve performance of the resulting executable. These are enabled and
|
|
controlled by the optimization level and are not run at -Onone.
|
|
- *IRGen* lowers canonical SIL to LLVM IR.
|
|
- The LLVM backend (optionally) applies LLVM optimizations, runs the LLVM code
|
|
generator and emits binary code.
|
|
|
|
The stages pertaining to SIL processing in particular are as follows:
|
|
|
|
SILGen
|
|
~~~~~~
|
|
|
|
SILGen produces *raw SIL* by walking a type-checked Swift AST.
|
|
The form of SIL emitted by SILGen has the following properties:
|
|
|
|
- Variables are represented by loading and storing mutable memory locations
|
|
instead of being in strict SSA form. This is similar to the initial
|
|
``alloca``-heavy LLVM IR emitted by frontends such as Clang. However, Swift
|
|
represents variables as reference-counted "boxes" in the most general case,
|
|
which can be retained, released, and captured into closures.
|
|
- Dataflow requirements, such as definitive assignment, function returns,
|
|
switch coverage (TBD), etc. have not yet been enforced.
|
|
- ``transparent`` function optimization has not yet been honored.
|
|
|
|
These properties are addressed by subsequent guaranteed optimization and
|
|
diagnostic passes which are always run against the raw SIL.
|
|
|
|
Guaranteed Optimization and Diagnostic Passes
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
After SILGen, a deterministic sequence of optimization passes is run over the
|
|
raw SIL. We do not want the diagnostics produced by the compiler to change as
|
|
the compiler evolves, so these passes are intended to be simple and
|
|
predictable.
|
|
|
|
- **Mandatory inlining** inlines calls to "transparent" functions.
|
|
- **Memory promotion** is implemented as two optimization phases, the first
|
|
of which performs capture analysis to promote ``alloc_box`` instructions to
|
|
``alloc_stack``, and the second of which promotes non-address-exposed ``alloc_stack``
|
|
instructions to SSA registers.
|
|
- **Constant propagation** folds constant expressions and propagates the constant values.
|
|
If an arithmetic overflow occurs during the constant expression computation, a diagnostic
|
|
is issued.
|
|
- **Return analysis** verifies that each function returns a value on every
|
|
code path and doesn't "fall off the end" of its definition, which is an error.
|
|
It also issues an error when a ``noreturn`` function returns.
|
|
- **Critical edge splitting** splits all critical edges from terminators that
|
|
don't support arbitrary basic block arguments (all non cond_branch
|
|
terminators).
|
|
|
|
If all diagnostic passes succeed, the final result is the
|
|
*canonical SIL* for the program.
|
|
|
|
TODO:
|
|
|
|
- Generic specialization
|
|
- Basic ARC optimization for acceptable performance at -Onone.
|
|
|
|
General Optimization Passes
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
SIL captures language-specific type information, making it possible to
|
|
perform high-level optimizations that are difficult to perform on LLVM
|
|
IR.
|
|
|
|
- **Generic Specialization** analyzes specialized calls to generic
|
|
functions and generates new specialized version of the
|
|
functions. Then it rewrites all specialized usages of the generic
|
|
to a direct call of the appropriate specialized function.
|
|
- **Witness and VTable Devirtualization** for a given type looks up
|
|
the associated method from a class's vtable or a type witness table
|
|
and replaces the indirect virtual call with a call to the mapped
|
|
function.
|
|
- **Performance Inlining**
|
|
- **Reference Counting Optimizations**
|
|
- **Memory Promotion/Optimizations**
|
|
- **High-level domain specific optimizations** The Swift compiler implements
|
|
high-level optimizations on basic Swift containers such as Array or String.
|
|
Domain specific optimizations require a defined interface between
|
|
the standard library and the optimizer. More details can be found here:
|
|
:ref:`HighLevelSILOptimizations`
|
|
|
|
Syntax
|
|
------
|
|
|
|
SIL is reliant on Swift's type system and declarations, so SIL syntax
|
|
is an extension of Swift's. A ``.sil`` file is a Swift source file
|
|
with added SIL definitions. The Swift source is parsed only for its
|
|
declarations; Swift ``func`` bodies (except for nested declarations)
|
|
and top-level code are ignored by the SIL parser. In a ``.sil`` file,
|
|
there are no implicit imports; the ``swift`` and/or ``Builtin``
|
|
standard modules must be imported explicitly if used.
|
|
|
|
Here is an example of a ``.sil`` file::
|
|
|
|
sil_stage canonical
|
|
|
|
import Swift
|
|
|
|
// Define types used by the SIL function.
|
|
|
|
struct Point {
|
|
var x : Double
|
|
var y : Double
|
|
}
|
|
|
|
class Button {
|
|
func onClick()
|
|
func onMouseDown()
|
|
func onMouseUp()
|
|
}
|
|
|
|
// Declare a Swift function. The body is ignored by SIL.
|
|
func taxicabNorm(_ a:Point) -> Double {
|
|
return a.x + a.y
|
|
}
|
|
|
|
// Define a SIL function.
|
|
// The name @_T5norms11taxicabNormfT1aV5norms5Point_Sd is the mangled name
|
|
// of the taxicabNorm Swift function.
|
|
sil @_T5norms11taxicabNormfT1aV5norms5Point_Sd : $(Point) -> Double {
|
|
bb0(%0 : $Point):
|
|
// func Swift.+(Double, Double) -> Double
|
|
%1 = function_ref @_Tsoi1pfTSdSd_Sd
|
|
%2 = struct_extract %0 : $Point, #Point.x
|
|
%3 = struct_extract %0 : $Point, #Point.y
|
|
%4 = apply %1(%2, %3) : $(Double, Double) -> Double
|
|
return %4 : Double
|
|
}
|
|
|
|
// Define a SIL vtable. This matches dynamically-dispatched method
|
|
// identifiers to their implementations for a known static class type.
|
|
sil_vtable Button {
|
|
#Button.onClick!1: @_TC5norms6Button7onClickfS0_FT_T_
|
|
#Button.onMouseDown!1: @_TC5norms6Button11onMouseDownfS0_FT_T_
|
|
#Button.onMouseUp!1: @_TC5norms6Button9onMouseUpfS0_FT_T_
|
|
}
|
|
|
|
SIL Stage
|
|
~~~~~~~~~
|
|
::
|
|
|
|
decl ::= sil-stage-decl
|
|
sil-stage-decl ::= 'sil_stage' sil-stage
|
|
|
|
sil-stage ::= 'raw'
|
|
sil-stage ::= 'canonical'
|
|
|
|
There are different invariants on SIL depending on what stage of processing
|
|
has been applied to it.
|
|
|
|
* **Raw SIL** is the form produced by SILGen that has not been run through
|
|
guaranteed optimizations or diagnostic passes. Raw SIL may not have a
|
|
fully-constructed SSA graph. It may contain dataflow errors. Some instructions
|
|
may be represented in non-canonical forms, such as ``assign`` and
|
|
``destroy_addr`` for non-address-only values. Raw SIL should not be used
|
|
for native code generation or distribution.
|
|
|
|
* **Canonical SIL** is SIL as it exists after guaranteed optimizations and
|
|
diagnostics. Dataflow errors must be eliminated, and certain instructions
|
|
must be canonicalized to simpler forms. Performance optimization and native
|
|
code generation are derived from this form, and a module can be distributed
|
|
containing SIL in this (or later) forms.
|
|
|
|
SIL files declare the processing stage of the included SIL with one of the
|
|
declarations ``sil_stage raw`` or ``sil_stage canonical`` at top level. Only
|
|
one such declaration may appear in a file.
|
|
|
|
SIL Types
|
|
~~~~~~~~~
|
|
::
|
|
|
|
sil-type ::= '$' '*'? generic-parameter-list? type
|
|
|
|
SIL types are introduced with the ``$`` sigil. SIL's type system is
|
|
closely related to Swift's, and so the type after the ``$`` is parsed
|
|
largely according to Swift's type grammar.
|
|
|
|
Type Lowering
|
|
`````````````
|
|
|
|
A *formal type* is the type of a value in Swift, such as an expression
|
|
result. Swift's formal type system intentionally abstracts over a
|
|
large number of representational issues like ownership transfer
|
|
conventions and directness of arguments. However, SIL aims to
|
|
represent most such implementation details, and so these differences
|
|
deserve to be reflected in the SIL type system. *Type lowering* is
|
|
the process of turning a formal type into its *lowered type*.
|
|
|
|
It is important to be aware that the lowered type of a declaration
|
|
need not be the lowered type of the formal type of that declaration.
|
|
For example, the lowered type of a declaration reference:
|
|
|
|
- will usually be thin,
|
|
|
|
- will frequently be uncurried,
|
|
|
|
- may have a non-Swift calling convention,
|
|
|
|
- may use bridged types in its interface, and
|
|
|
|
- may use ownership conventions that differ from Swift's default
|
|
conventions.
|
|
|
|
Abstraction Difference
|
|
``````````````````````
|
|
|
|
Generic functions working with values of unconstrained type must
|
|
generally work with them indirectly, e.g. by allocating sufficient
|
|
memory for them and then passing around pointers to that memory.
|
|
Consider a generic function like this:
|
|
|
|
::
|
|
|
|
func generateArray<T>(n : Int, generator : () -> T) -> [T]
|
|
|
|
The function ``generator`` will be expected to store its result
|
|
indirectly into an address passed in an implicit parameter. There's
|
|
really just no reasonable alternative when working with a value of
|
|
arbitrary type:
|
|
|
|
- We don't want to generate a different copy of ``generateArray`` for
|
|
every type ``T``.
|
|
|
|
- We don't want to give every type in the language a common
|
|
representation.
|
|
|
|
- We don't want to dynamically construct a call to ``generator``
|
|
depending on the type ``T``.
|
|
|
|
But we also don't want the existence of the generic system to force
|
|
inefficiencies on non-generic code. For example, we'd like a function
|
|
of type ``() -> Int`` to be able to return its result directly; and
|
|
yet, ``() -> Int`` is a valid substitution of ``() -> T``, and a
|
|
caller of ``generateArray<Int>`` should be able to pass an arbitrary
|
|
``() -> Int`` in as the generator.
|
|
|
|
Therefore, the representation of a formal type in a generic context
|
|
may differ from the representation of a substitution of that formal type.
|
|
We call such differences *abstraction differences*.
|
|
|
|
SIL's type system is designed to make abstraction differences always
|
|
result in differences between SIL types. The goal is that a properly-
|
|
abstracted value should be correctly usable at any level of substitution.
|
|
|
|
In order to achieve this, the formal type of a generic entity should
|
|
always be lowered using the abstraction pattern of its unsubstituted
|
|
formal type. For example, consider the following generic type:
|
|
|
|
::
|
|
|
|
struct Generator<T> {
|
|
var fn : () -> T
|
|
}
|
|
var intGen : Generator<Int>
|
|
|
|
``intGen.fn`` has the substituted formal type ``() -> Int``, which
|
|
would normally lower to the type ``@callee_owned () -> Int``, i.e.
|
|
returning its result directly. But if that type is properly lowered
|
|
with the pattern of its unsubstituted type ``() -> T``, it becomes
|
|
``@callee_owned () -> @out Int``.
|
|
|
|
When a type is lowered using the abstraction pattern of an
|
|
unrestricted type, it is lowered as if the pattern were replaced with
|
|
a type sharing the same structure but replacing all materializable
|
|
types with fresh type variables.
|
|
|
|
For example, if ``g`` has type ``Generator<(Int, Int) -> Float>``, ``g.fn`` is
|
|
lowered using the pattern ``() -> T``, which eventually causes ``(Int, Int)
|
|
-> Float`` to be lowered using the pattern ``T``, which is the same as
|
|
lowering it with the pattern ``U -> V``; the result is that ``g.fn``
|
|
has the following lowered type::
|
|
|
|
@callee_owned () -> @owned @callee_owned (@in (Int, Int)) -> @out Float.
|
|
|
|
As another example, suppose that ``h`` has type
|
|
``Generator<(Int, inout Int) -> Float>``. Neither ``(Int, inout Int)``
|
|
nor ``inout Int`` are potential results of substitution because they
|
|
aren't materializable, so ``h.fn`` has the following lowered type::
|
|
|
|
@callee_owned () -> @owned @callee_owned (@in Int, @inout Int) -> @out Float
|
|
|
|
This system has the property that abstraction patterns are preserved
|
|
through repeated substitutions. That is, you can consider a lowered
|
|
type to encode an abstraction pattern; lowering ``T`` by ``R`` is
|
|
equivalent to lowering ``T`` by (``S`` lowered by ``R``).
|
|
|
|
SILGen has procedures for converting values between abstraction
|
|
patterns.
|
|
|
|
At present, only function and tuple types are changed by abstraction
|
|
differences.
|
|
|
|
Legal SIL Types
|
|
```````````````
|
|
|
|
The type of a value in SIL shall be:
|
|
|
|
- a loadable legal SIL type, ``$T``,
|
|
|
|
- the address of a legal SIL type, ``$*T``, or
|
|
|
|
A type ``T`` is a *legal SIL type* if:
|
|
|
|
- it is a function type which satisfies the constraints (below) on
|
|
function types in SIL,
|
|
|
|
- it is a metatype type which describes its representation,
|
|
|
|
- it is a tuple type whose element types are legal SIL types,
|
|
|
|
- it is ``Optional<U>``, where ``U`` is a legal SIL type,
|
|
|
|
- it is a legal Swift type that is not a function, tuple, optional,
|
|
metatype, or l-value type, or
|
|
|
|
- it is a ``@box`` containing a legal SIL type.
|
|
|
|
Note that types in other recursive positions in the type grammar are
|
|
still formal types. For example, the instance type of a metatype or
|
|
the type arguments of a generic type are still formal Swift types, not
|
|
lowered SIL types.
|
|
|
|
Address Types
|
|
`````````````
|
|
|
|
The *address of T* ``$*T`` is a pointer to memory containing a value
|
|
of any reference or value type ``$T``. This can be an internal
|
|
pointer into a data structure. Addresses of loadable types can be
|
|
loaded and stored to access values of those types.
|
|
|
|
Addresses of address-only types (see below) can only be used with
|
|
instructions that manipulate their operands indirectly by address, such
|
|
as ``copy_addr`` or ``destroy_addr``, or as arguments to functions.
|
|
It is illegal to have a value of type ``$T`` if ``T`` is address-only.
|
|
|
|
Addresses are not reference-counted pointers like class values are. They
|
|
cannot be retained or released.
|
|
|
|
Address types are not *first-class*: they cannot appear in recursive
|
|
positions in type expressions. For example, the type ``$**T`` is not
|
|
a legal type.
|
|
|
|
The address of an address cannot be directly taken. ``$**T`` is not a representable
|
|
type. Values of address type thus cannot be allocated, loaded, or stored
|
|
(though addresses can of course be loaded from and stored to).
|
|
|
|
Addresses can be passed as arguments to functions if the corresponding
|
|
parameter is indirect. They cannot be returned.
|
|
|
|
Box Types
|
|
`````````
|
|
|
|
Captured local variables and the payloads of ``indirect`` value types are stored
|
|
on the heap. The type ``@box T`` is a reference-counted type that references
|
|
a box containing a mutable value of type ``T``. Boxes always use Swift-native
|
|
reference counting, so they can be queried for uniqueness and cast to the
|
|
``Builtin.NativeObject`` type.
|
|
|
|
Metatype Types
|
|
``````````````
|
|
|
|
A concrete or existential metatype in SIL must describe its representation.
|
|
This can be:
|
|
|
|
- ``@thin``, meaning that it requires no storage and thus necessarily
|
|
represents an exact type (only allowed for concrete metatypes);
|
|
|
|
- ``@thick``, meaning that it stores a reference to a type or (if a
|
|
concrete class) a subclass of that type; or
|
|
|
|
- ``@objc``, meaning that it stores a reference to a class type (or a
|
|
subclass thereof) using an Objective-C class object representation
|
|
rather than the native Swift type-object representation.
|
|
|
|
Function Types
|
|
``````````````
|
|
|
|
Function types in SIL are different from function types in Swift in a
|
|
number of ways:
|
|
|
|
- A SIL function type may be generic. For example, accessing a
|
|
generic function with ``function_ref`` will give a value of
|
|
generic function type.
|
|
|
|
- A SIL function type may be declared ``@noescape``. This is required for any
|
|
function type passed to a parameter not declared with ``@escaping``
|
|
declaration modifier. ``@noescape`` function types may be either
|
|
``@convention(thin)`` or ``@callee_guaranteed``. They have an
|
|
unowned context--the context's lifetime must be independently guaranteed.
|
|
|
|
- A SIL function type declares its conventional treatment of its
|
|
context value:
|
|
|
|
- If it is ``@convention(thin)``, the function requires no context value.
|
|
Such types may also be declared ``@noescape``, which trivially has no effect
|
|
passing the context value.
|
|
|
|
- If it is ``@callee_guaranteed``, the context value is treated as a direct
|
|
parameter. This implies ``@convention(thick)``. If the function type is also
|
|
``@noescape``, then the context value is unowned, otherwise it is
|
|
guaranteed.
|
|
|
|
- If it is ``@callee_owned``, the context value is treated as an owned direct
|
|
parameter. This implies ``@convention(thick)`` and is mutually exclusive
|
|
with ``@noescape``.
|
|
|
|
- If it is ``@convention(block)``, the context value is treated as an unowned
|
|
direct parameter.
|
|
|
|
- Other function type conventions are described in ``Properties of Types`` and
|
|
``Calling Convention``.
|
|
|
|
- A SIL function type declares the conventions for its parameters.
|
|
The parameters are written as an unlabeled tuple; the elements of that
|
|
tuple must be legal SIL types, optionally decorated with one of the
|
|
following convention attributes.
|
|
|
|
The value of an indirect parameter has type ``*T``; the value of a
|
|
direct parameter has type ``T``.
|
|
|
|
- An ``@in`` parameter is indirect. The address must be of an
|
|
initialized object; the function is responsible for destroying
|
|
the value held there.
|
|
|
|
- An ``@inout`` parameter is indirect. The address must be of an
|
|
initialized object. The memory must remain initialized for the duration
|
|
of the call until the function returns. The function may mutate the
|
|
pointee, and furthermore may weakly assume that there are no aliasing
|
|
reads from or writes to the argument, though must preserve a valid
|
|
value at the argument so that well-ordered aliasing violations do not
|
|
compromise memory safety. This allows for optimizations such as local
|
|
load and store propagation, introduction or elimination of temporary
|
|
copies, and promotion of the ``@inout`` parameter to an ``@owned`` direct
|
|
parameter and result pair, but does not admit "take" optimization out
|
|
of the parameter or other optimization that would leave memory in an
|
|
uninitialized state.
|
|
|
|
- An ``@inout_aliasable`` parameter is indirect. The address must be of an
|
|
initialized object. The memory must remain initialized for the duration
|
|
of the call until the function returns. The function may mutate the
|
|
pointee, and must assume that other aliases may mutate it as well. These
|
|
aliases however can be assumed to be well-typed and well-ordered; ill-typed
|
|
accesses and data races to the parameter are still undefined.
|
|
|
|
- An ``@owned`` parameter is an owned direct parameter.
|
|
|
|
- A ``@guaranteed`` parameter is a guaranteed direct parameter.
|
|
|
|
- An ``@in_guaranteed`` parameter is indirect. The address must be of an
|
|
initialized object; both the caller and callee promise not to mutate the
|
|
pointee, allowing the callee to read it.
|
|
|
|
- An ``@in_constant`` parameter is indirect. The address must be of an
|
|
initialized object; the function will treat the value held there as read-only.
|
|
|
|
- Otherwise, the parameter is an unowned direct parameter.
|
|
|
|
- A SIL function type declares the conventions for its results.
|
|
The results are written as an unlabeled tuple; the elements of that
|
|
tuple must be legal SIL types, optionally decorated with one of the
|
|
following convention attributes. Indirect and direct results may
|
|
be interleaved.
|
|
|
|
Indirect results correspond to implicit arguments of type ``*T`` in
|
|
function entry blocks and in the arguments to ``apply`` and ``try_apply``
|
|
instructions. These arguments appear in the order in which they appear
|
|
in the result list, always before any parameters.
|
|
|
|
Direct results correspond to direct return values of type ``T``. A
|
|
SIL function type has a ``return type`` derived from its direct results
|
|
in the following way: when there is a single direct result, the return
|
|
type is the type of that result; otherwise, it is the tuple type of the
|
|
types of all the direct results, in the order they appear in the results
|
|
list. The return type is the type of the operand of ``return``
|
|
instructions, the type of ``apply`` instructions, and the type of
|
|
the normal result of ``try_apply`` instructions.
|
|
|
|
- An ``@out`` result is indirect. The address must be of an
|
|
uninitialized object. The function is required to leave an
|
|
initialized value there unless it terminates with a ``throw``
|
|
instruction or it has a non-Swift calling convention.
|
|
|
|
- An ``@owned`` result is an owned direct result.
|
|
|
|
- An ``@autoreleased`` result is an autoreleased direct result.
|
|
If there is an autoreleased result, it must be the only direct result.
|
|
|
|
- Otherwise, the parameter is an unowned direct result.
|
|
|
|
A direct parameter or result of trivial type must always be unowned.
|
|
|
|
An owned direct parameter or result is transferred to the recipient,
|
|
which becomes responsible for destroying the value. This means that
|
|
the value is passed at +1.
|
|
|
|
An unowned direct parameter or result is instantaneously valid at the
|
|
point of transfer. The recipient does not need to worry about race
|
|
conditions immediately destroying the value, but should copy it
|
|
(e.g. by ``strong_retain``\ ing an object pointer) if the value will be
|
|
needed sooner rather than later.
|
|
|
|
A guaranteed direct parameter is like an unowned direct parameter
|
|
value, except that it is guaranteed by the caller to remain valid
|
|
throughout the execution of the call. This means that any
|
|
``strong_retain``, ``strong_release`` pairs in the callee on the
|
|
argument can be eliminated.
|
|
|
|
An autoreleased direct result must have a type with a retainable
|
|
pointer representation. Autoreleased results are nominally transferred
|
|
at +0, but the runtime takes steps to ensure that a +1 can be safely
|
|
transferred, and those steps require precise code-layout control.
|
|
Accordingly, the SIL pattern for an autoreleased convention looks exactly
|
|
like the SIL pattern for an owned convention, and the extra runtime
|
|
instrumentation is inserted on both sides when the SIL is lowered into
|
|
LLVM IR. An autoreleased ``apply`` of a function that is defined with
|
|
an autoreleased result has the effect of a +1 transfer of the result.
|
|
An autoreleased ``apply`` of a function that is not defined with
|
|
an autoreleased result has the effect of performing a strong retain in
|
|
the caller. A non-autoreleased ``apply`` of a function that is defined
|
|
with an autoreleased result has the effect of performing an
|
|
autorelease in the callee.
|
|
|
|
- SIL function types may provide an optional error result, written by
|
|
placing ``@error`` on a result. An error result is always
|
|
implicitly ``@owned``. Only functions with a native calling
|
|
convention may have an error result.
|
|
|
|
A function with an error result cannot be called with ``apply``.
|
|
It must be called with ``try_apply``.
|
|
There is one exception to this rule: a function with an error result can be
|
|
called with ``apply [nothrow]`` if the compiler can prove that the function
|
|
does not actually throw.
|
|
|
|
``return`` produces a normal result of the function. To return
|
|
an error result, use ``throw``.
|
|
|
|
Type lowering lowers the ``throws`` annotation on formal function
|
|
types into more concrete error propagation:
|
|
|
|
- For native Swift functions, ``throws`` is turned into an error
|
|
result.
|
|
|
|
- For non-native Swift functions, ``throws`` is turned in an
|
|
explicit error-handling mechanism based on the imported API. The
|
|
importer only imports non-native methods and types as ``throws``
|
|
when it is possible to do this automatically.
|
|
|
|
Coroutine Types
|
|
```````````````
|
|
|
|
A coroutine is a function which can suspend itself and return control to
|
|
its caller without terminating the function. That is, it does not need to
|
|
obey a strict stack discipline.
|
|
|
|
SIL supports two kinds of coroutine: ``@yield_many`` and ``@yield_once``.
|
|
Either of these attributes may be written before a function type to
|
|
indicate that it is a coroutine type.
|
|
|
|
A coroutine type may declare any number of *yielded values*, which is to
|
|
say, values which are provided to the caller at a yield point. Yielded
|
|
values are written in the result list of a function type, prefixed by
|
|
the ``@yields`` attribute. A yielded value may have a convention attribute,
|
|
taken from the set of parameter attributes and interpreted as if the yield
|
|
site were calling back to the calling function.
|
|
|
|
Currently, a coroutine may not have normal results.
|
|
|
|
Coroutine functions may be used in many of the same ways as normal
|
|
function values. However, they cannot be called with the standard
|
|
``apply`` or ``try_apply`` instructions. A non-throwing yield-once
|
|
coroutine can be called with the ``begin_apply`` instruction. There
|
|
is no support yet for calling a throwing yield-once coroutine or for
|
|
calling a yield-many coroutine of any kind.
|
|
|
|
Coroutines may contain the special ``yield`` and ``unwind`` instructions.
|
|
|
|
A ``@yield_many`` coroutine may yield as many times as it desires.
|
|
A ``@yield_once`` coroutine may yield exactly once before returning,
|
|
although it may also ``throw`` before reaching that point.
|
|
|
|
This coroutine representation is well-suited to coroutines whose control
|
|
flow is tightly integrated with their callers and which intend to pass
|
|
information back and forth. This matches the needs of generalized
|
|
accessor and generator features in Swift. It is not a particularly good
|
|
match for ``async``/``await``-style features; a simpler representation
|
|
would probably do fine for that.
|
|
|
|
Properties of Types
|
|
```````````````````
|
|
|
|
SIL classifies types into additional subgroups based on ABI stability and
|
|
generic constraints:
|
|
|
|
- *Loadable types* are types with a fully exposed concrete representation:
|
|
|
|
* Reference types
|
|
* Builtin value types
|
|
* Fragile struct types in which all element types are loadable
|
|
* Tuple types in which all element types are loadable
|
|
* Class protocol types
|
|
* Archetypes constrained by a class protocol
|
|
|
|
A *loadable aggregate type* is a tuple or struct type that is loadable.
|
|
|
|
A *trivial type* is a loadable type with trivial value semantics.
|
|
Values of trivial type can be loaded and stored without any retain or
|
|
release operations and do not need to be destroyed.
|
|
|
|
- *Runtime-sized types* are restricted value types for which the compiler
|
|
does not know the size of the type statically:
|
|
|
|
* Resilient value types
|
|
* Fragile struct or tuple types that contain resilient types as elements at
|
|
any depth
|
|
* Archetypes not constrained by a class protocol
|
|
|
|
- *Address-only types* are restricted value types which cannot be
|
|
loaded or otherwise worked with as SSA values:
|
|
|
|
* Runtime-sized types
|
|
* Non-class protocol types
|
|
* @weak types
|
|
|
|
Values of address-only type ("address-only values") must reside in
|
|
memory and can only be referenced in SIL by address. Addresses of
|
|
address-only values cannot be loaded from or stored to. SIL provides
|
|
special instructions for indirectly manipulating address-only
|
|
values, such as ``copy_addr`` and ``destroy_addr``.
|
|
|
|
Some additional meaningful categories of type:
|
|
|
|
- A *heap object reference* type is a type whose representation consists of a
|
|
single strong-reference-counted pointer. This includes all class types,
|
|
the ``Builtin.NativeObject`` and ``AnyObject`` types, and
|
|
archetypes that conform to one or more class protocols.
|
|
- A *reference type* is more general in that its low-level representation may
|
|
include additional global pointers alongside a strong-reference-counted
|
|
pointer. This includes all heap object reference types and adds
|
|
thick function types and protocol/protocol composition types that conform to
|
|
one or more class protocols. All reference types can be ``retain``-ed and
|
|
``release``-d. Reference types also have *ownership semantics* for their
|
|
referenced heap object; see `Reference Counting`_ below.
|
|
- A type with *retainable pointer representation* is guaranteed to
|
|
be compatible (in the C sense) with the Objective-C ``id`` type.
|
|
The value at runtime may be ``nil``. This includes classes,
|
|
class metatypes, block functions, and class-bounded existentials with
|
|
only Objective-C-compatible protocol constraints, as well as one
|
|
level of ``Optional`` or ``ImplicitlyUnwrappedOptional`` applied to any of the
|
|
above. Types with retainable pointer representation can be returned
|
|
via the ``@autoreleased`` return convention.
|
|
|
|
SILGen does not always map Swift function types one-to-one to SIL function
|
|
types. Function types are transformed in order to encode additional attributes:
|
|
|
|
- The **convention** of the function, indicated by the
|
|
|
|
.. parsed-literal::
|
|
|
|
@convention(*convention*)
|
|
|
|
attribute. This is similar to the language-level ``@convention``
|
|
attribute, though SIL extends the set of supported conventions with
|
|
additional distinctions not exposed at the language level:
|
|
|
|
- ``@convention(thin)`` indicates a "thin" function reference, which uses
|
|
the Swift calling convention with no special "self" or "context" parameters.
|
|
- ``@convention(thick)`` indicates a "thick" function reference, which
|
|
uses the Swift calling convention and carries a reference-counted context
|
|
object used to represent captures or other state required by the function.
|
|
This attribute is implied by ``@callee_owned`` or ``@callee_guaranteed``.
|
|
- ``@convention(block)`` indicates an Objective-C compatible block reference.
|
|
The function value is represented as a reference to the block object,
|
|
which is an ``id``-compatible Objective-C object that embeds its invocation
|
|
function within the object. The invocation function uses the C calling
|
|
convention.
|
|
- ``@convention(c)`` indicates a C function reference. The function value
|
|
carries no context and uses the C calling convention.
|
|
- ``@convention(objc_method)`` indicates an Objective-C method implementation.
|
|
The function uses the C calling convention, with the SIL-level ``self``
|
|
parameter (by SIL convention mapped to the final formal parameter)
|
|
mapped to the ``self`` and ``_cmd`` arguments of the implementation.
|
|
- ``@convention(method)`` indicates a Swift instance method implementation.
|
|
The function uses the Swift calling convention, using the special ``self``
|
|
parameter.
|
|
- ``@convention(witness_method)`` indicates a Swift protocol method
|
|
implementation. The function's polymorphic convention is emitted in such
|
|
a way as to guarantee that it is polymorphic across all possible
|
|
implementors of the protocol.
|
|
|
|
- The **fully uncurried representation** of the function type, with
|
|
all of the curried argument clauses flattened into a single argument
|
|
clause. For instance, a curried function ``func foo(_ x:A)(y:B) -> C``
|
|
might be emitted as a function of type ``((y:B), (x:A)) -> C``. The
|
|
exact representation depends on the function's `calling
|
|
convention`_, which determines the exact ordering of currying
|
|
clauses. Methods are treated as a form of curried function.
|
|
|
|
Layout Compatible Types
|
|
```````````````````````
|
|
|
|
(This section applies only to Swift 1.0 and will hopefully be obviated in
|
|
future releases.)
|
|
|
|
SIL tries to be ignorant of the details of type layout, and low-level
|
|
bit-banging operations such as pointer casts are generally undefined. However,
|
|
as a concession to implementation convenience, some types are allowed to be
|
|
considered **layout compatible**. Type ``T`` is *layout compatible* with type
|
|
``U`` iff:
|
|
|
|
- an address of type ``$*U`` can be cast by
|
|
``address_to_pointer``/``pointer_to_address`` to ``$*T`` and a valid value
|
|
of type ``T`` can be loaded out (or indirectly used, if ``T`` is address-
|
|
only),
|
|
- if ``T`` is a nontrivial type, then ``retain_value``/``release_value`` of
|
|
the loaded ``T`` value is equivalent to ``retain_value``/``release_value`` of
|
|
the original ``U`` value.
|
|
|
|
This is not always a commutative relationship; ``T`` can be layout-compatible
|
|
with ``U`` whereas ``U`` is not layout-compatible with ``T``. If the layout
|
|
compatible relationship does extend both ways, ``T`` and ``U`` are
|
|
**commutatively layout compatible**. It is however always transitive; if ``T``
|
|
is layout-compatible with ``U`` and ``U`` is layout-compatible with ``V``, then
|
|
``T`` is layout-compatible with ``V``. All types are layout-compatible with
|
|
themselves.
|
|
|
|
The following types are considered layout-compatible:
|
|
|
|
- ``Builtin.RawPointer`` is commutatively layout compatible with all heap
|
|
object reference types, and ``Optional`` of heap object reference types.
|
|
(Note that ``RawPointer`` is a trivial type, so does not have ownership
|
|
semantics.)
|
|
- ``Builtin.RawPointer`` is commutatively layout compatible with
|
|
``Builtin.Word``.
|
|
- Structs containing a single stored property are commutatively layout
|
|
compatible with the type of that property.
|
|
- A heap object reference is commutatively layout compatible with any type
|
|
that can correctly reference the heap object. For instance, given a class
|
|
``B`` and a derived class ``D`` inheriting from ``B``, a value of
|
|
type ``B`` referencing an instance of type ``D`` is layout compatible with
|
|
both ``B`` and ``D``, as well as ``Builtin.NativeObject`` and
|
|
``AnyObject``. It is not layout compatible with an unrelated class
|
|
type ``E``.
|
|
- For payloaded enums, the payload type of the first payloaded case is
|
|
layout-compatible with the enum (*not* commutatively).
|
|
|
|
Values and Operands
|
|
~~~~~~~~~~~~~~~~~~~
|
|
::
|
|
|
|
sil-identifier ::= [A-Za-z_0-9]+
|
|
sil-value-name ::= '%' sil-identifier
|
|
sil-value ::= sil-value-name
|
|
sil-value ::= 'undef'
|
|
sil-operand ::= sil-value ':' sil-type
|
|
|
|
SIL values are introduced with the ``%`` sigil and named by an
|
|
alphanumeric identifier, which references the instruction or basic block
|
|
argument that produces the value. SIL values may also refer to the keyword
|
|
'undef', which is a value of undefined contents.
|
|
|
|
Unlike LLVM IR, SIL instructions that take value operands *only* accept
|
|
value operands. References to literal constants, functions, global variables, or
|
|
other entities require specialized instructions such as ``integer_literal``,
|
|
``function_ref``, ``global_addr``, etc.
|
|
|
|
Functions
|
|
~~~~~~~~~
|
|
::
|
|
|
|
decl ::= sil-function
|
|
sil-function ::= 'sil' sil-linkage? sil-function-name ':' sil-type
|
|
'{' sil-basic-block+ '}'
|
|
sil-function-name ::= '@' [A-Za-z_0-9]+
|
|
|
|
SIL functions are defined with the ``sil`` keyword. SIL function names
|
|
are introduced with the ``@`` sigil and named by an alphanumeric
|
|
identifier. This name will become the LLVM IR name for the function,
|
|
and is usually the mangled name of the originating Swift declaration.
|
|
The ``sil`` syntax declares the function's name and SIL type, and
|
|
defines the body of the function inside braces. The declared type must
|
|
be a function type, which may be generic.
|
|
|
|
Basic Blocks
|
|
~~~~~~~~~~~~
|
|
::
|
|
|
|
sil-basic-block ::= sil-label sil-instruction-def* sil-terminator
|
|
sil-label ::= sil-identifier ('(' sil-argument (',' sil-argument)* ')')? ':'
|
|
sil-argument ::= sil-value-name ':' sil-type
|
|
|
|
sil-instruction-result ::= sil-value-name
|
|
sil-instruction-result ::= '(' (sil-value-name (',' sil-value-name)*)? ')'
|
|
sil-instruction-source-info ::= (',' sil-scope-ref)? (',' sil-loc)?
|
|
sil-instruction-def ::=
|
|
(sil-instruction-result '=')? sil-instruction sil-instruction-source-info
|
|
|
|
A function body consists of one or more basic blocks that correspond
|
|
to the nodes of the function's control flow graph. Each basic block
|
|
contains one or more instructions and ends with a terminator
|
|
instruction. The function's entry point is always the first basic
|
|
block in its body.
|
|
|
|
In SIL, basic blocks take arguments, which are used as an alternative to LLVM's
|
|
phi nodes. Basic block arguments are bound by the branch from the predecessor
|
|
block::
|
|
|
|
sil @iif : $(Builtin.Int1, Builtin.Int64, Builtin.Int64) -> Builtin.Int64 {
|
|
bb0(%cond : $Builtin.Int1, %ifTrue : $Builtin.Int64, %ifFalse : $Builtin.Int64):
|
|
cond_br %cond : $Builtin.Int1, then, else
|
|
then:
|
|
br finish(%ifTrue : $Builtin.Int64)
|
|
else:
|
|
br finish(%ifFalse : $Builtin.Int64)
|
|
finish(%result : $Builtin.Int64):
|
|
return %result : $Builtin.Int64
|
|
}
|
|
|
|
Arguments to the entry point basic block, which has no predecessor,
|
|
are bound by the function's caller::
|
|
|
|
sil @foo : $(Int) -> Int {
|
|
bb0(%x : $Int):
|
|
return %x : $Int
|
|
}
|
|
|
|
sil @bar : $(Int, Int) -> () {
|
|
bb0(%x : $Int, %y : $Int):
|
|
%foo = function_ref @foo
|
|
%1 = apply %foo(%x) : $(Int) -> Int
|
|
%2 = apply %foo(%y) : $(Int) -> Int
|
|
%3 = tuple ()
|
|
return %3 : $()
|
|
}
|
|
|
|
|
|
Debug Information
|
|
~~~~~~~~~~~~~~~~~
|
|
::
|
|
|
|
sil-scope-ref ::= 'scope' [0-9]+
|
|
sil-scope ::= 'sil_scope' [0-9]+ '{'
|
|
sil-loc
|
|
'parent' scope-parent
|
|
('inlined_at' sil-scope-ref)?
|
|
'}'
|
|
scope-parent ::= sil-function-name ':' sil-type
|
|
scope-parent ::= sil-scope-ref
|
|
sil-loc ::= 'loc' string-literal ':' [0-9]+ ':' [0-9]+
|
|
|
|
Each instruction may have a debug location and a SIL scope reference
|
|
at the end. Debug locations consist of a filename, a line number, and
|
|
a column number. If the debug location is omitted, it defaults to the
|
|
location in the SIL source file. SIL scopes describe the position
|
|
inside the lexical scope structure that the Swift expression a SIL
|
|
instruction was generated from had originally. SIL scopes also hold
|
|
inlining information.
|
|
|
|
|
|
Declaration References
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
::
|
|
|
|
sil-decl-ref ::= '#' sil-identifier ('.' sil-identifier)* sil-decl-subref?
|
|
sil-decl-subref ::= '!' sil-decl-subref-part ('.' sil-decl-uncurry-level)? ('.' sil-decl-lang)?
|
|
sil-decl-subref ::= '!' sil-decl-uncurry-level ('.' sil-decl-lang)?
|
|
sil-decl-subref ::= '!' sil-decl-lang
|
|
sil-decl-subref-part ::= 'getter'
|
|
sil-decl-subref-part ::= 'setter'
|
|
sil-decl-subref-part ::= 'allocator'
|
|
sil-decl-subref-part ::= 'initializer'
|
|
sil-decl-subref-part ::= 'enumelt'
|
|
sil-decl-subref-part ::= 'destroyer'
|
|
sil-decl-subref-part ::= 'deallocator'
|
|
sil-decl-subref-part ::= 'globalaccessor'
|
|
sil-decl-subref-part ::= 'ivardestroyer'
|
|
sil-decl-subref-part ::= 'ivarinitializer'
|
|
sil-decl-subref-part ::= 'defaultarg' '.' [0-9]+
|
|
sil-decl-uncurry-level ::= [0-9]+
|
|
sil-decl-lang ::= 'foreign'
|
|
|
|
Some SIL instructions need to reference Swift declarations directly. These
|
|
references are introduced with the ``#`` sigil followed by the fully qualified
|
|
name of the Swift declaration. Some Swift declarations are
|
|
decomposed into multiple entities at the SIL level. These are distinguished by
|
|
following the qualified name with ``!`` and one or more ``.``-separated component
|
|
entity discriminators:
|
|
|
|
- ``getter``: the getter function for a ``var`` declaration
|
|
- ``setter``: the setter function for a ``var`` declaration
|
|
- ``allocator``: a ``struct`` or ``enum`` constructor, or a ``class``\ 's *allocating constructor*
|
|
- ``initializer``: a ``class``\ 's *initializing constructor*
|
|
- ``enumelt``: a member of a ``enum`` type.
|
|
- ``destroyer``: a class's destroying destructor
|
|
- ``deallocator``: a class's deallocating destructor
|
|
- ``globalaccessor``: the addressor function for a global variable
|
|
- ``ivardestroyer``: a class's ivar destroyer
|
|
- ``ivarinitializer``: a class's ivar initializer
|
|
- ``defaultarg.``\ *n*: the default argument-generating function for
|
|
the *n*\ -th argument of a Swift ``func``
|
|
- ``foreign``: a specific entry point for C/Objective-C interoperability
|
|
|
|
Methods and curried function definitions in Swift also have multiple
|
|
"uncurry levels" in SIL, representing the function at each possible
|
|
partial application level. For a curried function declaration::
|
|
|
|
// Module example
|
|
func foo(_ x:A)(y:B)(z:C) -> D
|
|
|
|
The declaration references and types for the different uncurry levels are as
|
|
follows::
|
|
|
|
#example.foo!0 : $@convention(thin) (x:A) -> (y:B) -> (z:C) -> D
|
|
#example.foo!1 : $@convention(thin) ((y:B), (x:A)) -> (z:C) -> D
|
|
#example.foo!2 : $@convention(thin) ((z:C), (y:B), (x:A)) -> D
|
|
|
|
The deepest uncurry level is referred to as the **natural uncurry level**. In
|
|
this specific example, the reference at the natural uncurry level is
|
|
``#example.foo!2``. Note that the uncurried argument clauses are composed
|
|
right-to-left, as specified in the `calling convention`_. For uncurry levels
|
|
less than the uncurry level, the entry point itself is ``@convention(thin)`` but
|
|
returns a thick function value carrying the partially applied arguments for its
|
|
context.
|
|
|
|
`Dynamic dispatch`_ instructions such as ``class method`` require their method
|
|
declaration reference to be uncurried to at least uncurry level 1 (which applies
|
|
both the "self" argument and the method arguments), because uncurry level zero
|
|
represents the application of the method to its "self" argument, as in
|
|
``foo.method``, which is where the dynamic dispatch semantically occurs
|
|
in Swift.
|
|
|
|
Linkage
|
|
~~~~~~~
|
|
::
|
|
|
|
sil-linkage ::= 'public'
|
|
sil-linkage ::= 'hidden'
|
|
sil-linkage ::= 'shared'
|
|
sil-linkage ::= 'private'
|
|
sil-linkage ::= 'public_external'
|
|
sil-linkage ::= 'hidden_external'
|
|
|
|
A linkage specifier controls the situations in which two objects in
|
|
different SIL modules are *linked*, i.e. treated as the same object.
|
|
|
|
A linkage is *external* if it ends with the suffix ``external``. An
|
|
object must be a definition if its linkage is not external.
|
|
|
|
All functions, global variables, and witness tables have linkage.
|
|
The default linkage of a definition is ``public``. The default linkage of a
|
|
declaration is ``public_external``. (These may eventually change to ``hidden``
|
|
and ``hidden_external``, respectively.)
|
|
|
|
On a global variable, an external linkage is what indicates that the
|
|
variable is not a definition. A variable lacking an explicit linkage
|
|
specifier is presumed a definition (and thus gets the default linkage
|
|
for definitions, ``public``.)
|
|
|
|
Definition of the *linked* relation
|
|
```````````````````````````````````
|
|
|
|
Two objects are linked if they have the same name and are mutually
|
|
visible:
|
|
|
|
- An object with ``public`` or ``public_external`` linkage is always
|
|
visible.
|
|
|
|
- An object with ``hidden``, ``hidden_external``, or ``shared``
|
|
linkage is visible only to objects in the same Swift module.
|
|
|
|
- An object with ``private`` linkage is visible only to objects in
|
|
the same SIL module.
|
|
|
|
Note that the *linked* relationship is an equivalence relation: it is
|
|
reflexive, symmetric, and transitive.
|
|
|
|
Requirements on linked objects
|
|
``````````````````````````````
|
|
|
|
If two objects are linked, they must have the same type.
|
|
|
|
If two objects are linked, they must have the same linkage, except:
|
|
|
|
- A ``public`` object may be linked to a ``public_external`` object.
|
|
|
|
- A ``hidden`` object may be linked to a ``hidden_external`` object.
|
|
|
|
If two objects are linked, at most one may be a definition, unless:
|
|
|
|
- both objects have ``shared`` linkage or
|
|
|
|
- at least one of the objects has an external linkage.
|
|
|
|
If two objects are linked, and both are definitions, then the
|
|
definitions must be semantically equivalent. This equivalence may
|
|
exist only on the level of user-visible semantics of well-defined
|
|
code; it should not be taken to guarantee that the linked definitions
|
|
are exactly operationally equivalent. For example, one definition of
|
|
a function might copy a value out of an address parameter, while
|
|
another may have had an analysis applied to prove that said value is
|
|
not needed.
|
|
|
|
If an object has any uses, then it must be linked to a definition
|
|
with non-external linkage.
|
|
|
|
Summary
|
|
```````
|
|
|
|
- ``public`` definitions are unique and visible everywhere in the
|
|
program. In LLVM IR, they will be emitted with ``external``
|
|
linkage and ``default`` visibility.
|
|
|
|
- ``hidden`` definitions are unique and visible only within the
|
|
current Swift module. In LLVM IR, they will be emitted with
|
|
``external`` linkage and ``hidden`` visibility.
|
|
|
|
- ``private`` definitions are unique and visible only within the
|
|
current SIL module. In LLVM IR, they will be emitted with
|
|
``private`` linkage.
|
|
|
|
- ``shared`` definitions are visible only within the current Swift
|
|
module. They can be linked only with other ``shared``
|
|
definitions, which must be equivalent; therefore, they only need
|
|
to be emitted if actually used. In LLVM IR, they will be emitted
|
|
with ``linkonce_odr`` linkage and ``hidden`` visibility.
|
|
|
|
- ``public_external`` and ``hidden_external`` objects always have
|
|
visible definitions somewhere else. If this object nonetheless
|
|
has a definition, it's only for the benefit of optimization or
|
|
analysis. In LLVM IR, declarations will have ``external`` linkage
|
|
and definitions (if actually emitted as definitions) will have
|
|
``available_externally`` linkage.
|
|
|
|
|
|
VTables
|
|
~~~~~~~
|
|
::
|
|
|
|
decl ::= sil-vtable
|
|
sil-vtable ::= 'sil_vtable' identifier '{' sil-vtable-entry* '}'
|
|
|
|
sil-vtable-entry ::= sil-decl-ref ':' sil-linkage? sil-function-name
|
|
|
|
SIL represents dynamic dispatch for class methods using the `class_method`_,
|
|
`super_method`_, `objc_method`_, and `objc_super_method`_ instructions.
|
|
|
|
The potential destinations for `class_method`_ and `super_method`_ are
|
|
tracked in ``sil_vtable`` declarations for every class type. The declaration
|
|
contains a mapping from every method of the class (including those inherited
|
|
from its base class) to the SIL function that implements the method for that
|
|
class::
|
|
|
|
class A {
|
|
func foo()
|
|
func bar()
|
|
func bas()
|
|
}
|
|
|
|
sil @A_foo : $@convention(thin) (@owned A) -> ()
|
|
sil @A_bar : $@convention(thin) (@owned A) -> ()
|
|
sil @A_bas : $@convention(thin) (@owned A) -> ()
|
|
|
|
sil_vtable A {
|
|
#A.foo!1: @A_foo
|
|
#A.bar!1: @A_bar
|
|
#A.bas!1: @A_bas
|
|
}
|
|
|
|
class B : A {
|
|
func bar()
|
|
}
|
|
|
|
sil @B_bar : $@convention(thin) (@owned B) -> ()
|
|
|
|
sil_vtable B {
|
|
#A.foo!1: @A_foo
|
|
#A.bar!1: @B_bar
|
|
#A.bas!1: @A_bas
|
|
}
|
|
|
|
class C : B {
|
|
func bas()
|
|
}
|
|
|
|
sil @C_bas : $@convention(thin) (@owned C) -> ()
|
|
|
|
sil_vtable C {
|
|
#A.foo!1: @A_foo
|
|
#A.bar!1: @B_bar
|
|
#A.bas!1: @C_bas
|
|
}
|
|
|
|
Note that the declaration reference in the vtable is to the least-derived method
|
|
visible through that class (in the example above, ``B``'s vtable references
|
|
``A.bar`` and not ``B.bar``, and ``C``'s vtable references ``A.bas`` and not
|
|
``C.bas``). The Swift AST maintains override relationships between declarations
|
|
that can be used to look up overridden methods in the SIL vtable for a derived
|
|
class (such as ``C.bas`` in ``C``'s vtable).
|
|
|
|
In case the SIL function is a thunk, the function name is preceded with the
|
|
linkage of the original implementing function.
|
|
|
|
Witness Tables
|
|
~~~~~~~~~~~~~~
|
|
::
|
|
|
|
decl ::= sil-witness-table
|
|
sil-witness-table ::= 'sil_witness_table' sil-linkage?
|
|
normal-protocol-conformance '{' sil-witness-entry* '}'
|
|
|
|
SIL encodes the information needed for dynamic dispatch of generic types into
|
|
witness tables. This information is used to produce runtime dispatch tables when
|
|
generating binary code. It can also be used by SIL optimizations to specialize
|
|
generic functions. A witness table is emitted for every declared explicit
|
|
conformance. Generic types share one generic witness table for all of their
|
|
instances. Derived classes inherit the witness tables of their base class.
|
|
|
|
::
|
|
|
|
protocol-conformance ::= normal-protocol-conformance
|
|
protocol-conformance ::= 'inherit' '(' protocol-conformance ')'
|
|
protocol-conformance ::= 'specialize' '<' substitution* '>'
|
|
'(' protocol-conformance ')'
|
|
protocol-conformance ::= 'dependent'
|
|
normal-protocol-conformance ::= identifier ':' identifier 'module' identifier
|
|
|
|
Witness tables are keyed by *protocol conformance*, which is a unique identifier
|
|
for a concrete type's conformance to a protocol.
|
|
|
|
- A *normal protocol conformance* names a (potentially unbound generic) type,
|
|
the protocol it conforms to, and the module in which the type or extension
|
|
declaration that provides the conformance appears. These correspond 1:1 to
|
|
protocol conformance declarations in the source code.
|
|
- If a derived class conforms to a protocol through inheritance from its base
|
|
class, this is represented by an *inherited protocol conformance*, which
|
|
simply references the protocol conformance for the base class.
|
|
- If an instance of a generic type conforms to a protocol, it does so with a
|
|
*specialized conformance*, which provides the generic parameter bindings
|
|
to the normal conformance, which should be for a generic type.
|
|
|
|
Witness tables are only directly associated with normal conformances.
|
|
Inherited and specialized conformances indirectly reference the witness table of
|
|
the underlying normal conformance.
|
|
|
|
::
|
|
|
|
sil-witness-entry ::= 'base_protocol' identifier ':' protocol-conformance
|
|
sil-witness-entry ::= 'method' sil-decl-ref ':' sil-function-name
|
|
sil-witness-entry ::= 'associated_type' identifier
|
|
sil-witness-entry ::= 'associated_type_protocol'
|
|
'(' identifier ':' identifier ')' ':' protocol-conformance
|
|
|
|
Witness tables consist of the following entries:
|
|
|
|
- *Base protocol entries* provide references to the protocol conformances that
|
|
satisfy the witnessed protocols' inherited protocols.
|
|
- *Method entries* map a method requirement of the protocol to a SIL function
|
|
that implements that method for the witness type. One method entry must exist
|
|
for every required method of the witnessed protocol.
|
|
- *Associated type entries* map an associated type requirement of the protocol
|
|
to the type that satisfies that requirement for the witness type. Note that
|
|
the witness type is a source-level Swift type and not a SIL type. One
|
|
associated type entry must exist for every required associated type of the
|
|
witnessed protocol.
|
|
- *Associated type protocol entries* map a protocol requirement on an associated
|
|
type to the protocol conformance that satisfies that requirement for the
|
|
associated type.
|
|
|
|
Default Witness Tables
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
::
|
|
|
|
decl ::= sil-default-witness-table
|
|
sil-default-witness-table ::= 'sil_default_witness_table'
|
|
identifier minimum-witness-table-size
|
|
'{' sil-default-witness-entry* '}'
|
|
minimum-witness-table-size ::= integer
|
|
|
|
SIL encodes requirements with resilient default implementations in a default
|
|
witness table. We say a requirement has a resilient default implementation if
|
|
the following conditions hold:
|
|
|
|
- The requirement has a default implementation
|
|
- The requirement is either the last requirement in the protocol, or all
|
|
subsequent requirements also have resilient default implementations
|
|
|
|
The set of requirements with resilient default implementations is stored in
|
|
protocol metadata.
|
|
|
|
The minimum witness table size is the size of the witness table, in words,
|
|
not including any requirements with resilient default implementations.
|
|
|
|
Any conforming witness table must have a size between the minimum size, and
|
|
the maximum size, which is equal to the minimum size plus the number of
|
|
default requirements.
|
|
|
|
At load time, if the runtime encounters a witness table with fewer than the
|
|
maximum number of witnesses, the witness table is copied, with default
|
|
witnesses copied in. This ensures that callers can always expect to find
|
|
the correct number of requirements in each witness table, and new
|
|
requirements can be added by the framework author, without breaking client
|
|
code, as long as the new requirements have resilient default implementations.
|
|
|
|
Default witness tables are keyed by the protocol itself. Only protocols with
|
|
public visibility need a default witness table; private and internal protocols
|
|
are never seen outside the module, therefore there are no resilience issues
|
|
with adding new requirements.
|
|
|
|
::
|
|
|
|
sil-default-witness-entry ::= 'method' sil-decl-ref ':' sil-function-name
|
|
|
|
Default witness tables currently contain only one type of entry:
|
|
|
|
- *Method entries* map a method requirement of the protocol to a SIL function
|
|
that implements that method in a manner suitable for all witness types.
|
|
|
|
Global Variables
|
|
~~~~~~~~~~~~~~~~
|
|
::
|
|
|
|
decl ::= sil-global-variable
|
|
static-initializer ::= '=' '{' sil-instruction-def* '}'
|
|
sil-global-variable ::= 'sil_global' sil-linkage identifier ':' sil-type
|
|
(static-initializer)?
|
|
|
|
SIL representation of a global variable.
|
|
|
|
Global variable access is performed by the ``alloc_global``, ``global_addr``
|
|
and ``global_value`` instructions.
|
|
|
|
A global can have a static initializer if its initial value can be
|
|
composed of literals. The static initializer is represented as a list of
|
|
literal and aggregate instructions where the last instruction is the top-level
|
|
value of the static initializer::
|
|
|
|
sil_global hidden @$S4test3varSiv : $Int {
|
|
%0 = integer_literal $Builtin.Int64, 27
|
|
%initval = struct $Int (%0 : $Builtin.Int64)
|
|
}
|
|
|
|
If a global does not have a static initializer, the ``alloc_global``
|
|
instruction must be performed prior an access to initialize the storage.
|
|
Once a global's storage has been initialized, ``global_addr`` is used to
|
|
project the value.
|
|
|
|
If the last instruction in the static initializer is an ``object`` instruction
|
|
the global variable is a statically initialized object. In this case the
|
|
variable cannot be used as l-value, i.e. the reference to the object cannot be
|
|
modified. As a consequence the variable cannot be accessed with ``global_addr``
|
|
but only with ``global_value``.
|
|
|
|
Dataflow Errors
|
|
---------------
|
|
|
|
*Dataflow errors* may exist in raw SIL. Swift's semantics defines these
|
|
conditions as errors, so they must be diagnosed by diagnostic
|
|
passes and must not exist in canonical SIL.
|
|
|
|
Definitive Initialization
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Swift requires that all local variables be initialized before use. In
|
|
constructors, all instance variables of a struct, enum, or class type must
|
|
be initialized before the object is used and before the constructor is returned
|
|
from.
|
|
|
|
Unreachable Control Flow
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The ``unreachable`` terminator is emitted in raw SIL to mark incorrect control
|
|
flow, such as a non-``Void`` function failing to ``return`` a value, or a
|
|
``switch`` statement failing to cover all possible values of its subject.
|
|
The guaranteed dead code elimination pass can eliminate truly unreachable
|
|
basic blocks, or ``unreachable`` instructions may be dominated by applications
|
|
of functions returning uninhabited types. An ``unreachable`` instruction that
|
|
survives guaranteed DCE and is not immediately preceded by a no-return
|
|
application is a dataflow error.
|
|
|
|
Runtime Failure
|
|
---------------
|
|
|
|
Some operations, such as failed unconditional `checked conversions`_ or the
|
|
``Builtin.trap`` compiler builtin, cause a *runtime failure*, which
|
|
unconditionally terminates the current actor. If it can be proven that a
|
|
runtime failure will occur or did occur, runtime failures may be reordered so
|
|
long as they remain well-ordered relative to operations external to the actor
|
|
or the program as a whole. For instance, with overflow checking on integer
|
|
arithmetic enabled, a simple ``for`` loop that reads inputs in from one or more
|
|
arrays and writes outputs to another array, all local
|
|
to the current actor, may cause runtime failure in the update operations::
|
|
|
|
// Given unknown start and end values, this loop may overflow
|
|
for var i = unknownStartValue; i != unknownEndValue; ++i {
|
|
...
|
|
}
|
|
|
|
It is permitted to hoist the overflow check and associated runtime failure out
|
|
of the loop itself and check the bounds of the loop prior to entering it, so
|
|
long as the loop body has no observable effect outside of the current actor.
|
|
|
|
Undefined Behavior
|
|
------------------
|
|
|
|
Incorrect use of some operations is *undefined behavior*, such as invalid
|
|
unchecked casts involving ``Builtin.RawPointer`` types, or use of compiler
|
|
builtins that lower to LLVM instructions with undefined behavior at the LLVM
|
|
level. A SIL program with undefined behavior is meaningless, much like undefined
|
|
behavior in C, and has no predictable semantics. Undefined behavior should not
|
|
be triggered by valid SIL emitted by a correct Swift program using a correct
|
|
standard library, but cannot in all cases be diagnosed or verified at the SIL
|
|
level.
|
|
|
|
Calling Convention
|
|
------------------
|
|
|
|
This section describes how Swift functions are emitted in SIL.
|
|
|
|
Swift Calling Convention @convention(swift)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The Swift calling convention is the one used by default for native Swift
|
|
functions.
|
|
|
|
Tuples in the input type of the function are recursively destructured into
|
|
separate arguments, both in the entry point basic block of the callee, and
|
|
in the ``apply`` instructions used by callers::
|
|
|
|
func foo(_ x:Int, y:Int)
|
|
|
|
sil @foo : $(x:Int, y:Int) -> () {
|
|
entry(%x : $Int, %y : $Int):
|
|
...
|
|
}
|
|
|
|
func bar(_ x:Int, y:(Int, Int))
|
|
|
|
sil @bar : $(x:Int, y:(Int, Int)) -> () {
|
|
entry(%x : $Int, %y0 : $Int, %y1 : $Int):
|
|
...
|
|
}
|
|
|
|
func call_foo_and_bar() {
|
|
foo(1, 2)
|
|
bar(4, (5, 6))
|
|
}
|
|
|
|
sil @call_foo_and_bar : $() -> () {
|
|
entry:
|
|
...
|
|
%foo = function_ref @foo : $(x:Int, y:Int) -> ()
|
|
%foo_result = apply %foo(%1, %2) : $(x:Int, y:Int) -> ()
|
|
...
|
|
%bar = function_ref @bar : $(x:Int, y:(Int, Int)) -> ()
|
|
%bar_result = apply %bar(%4, %5, %6) : $(x:Int, y:(Int, Int)) -> ()
|
|
}
|
|
|
|
Calling a function with trivial value types as inputs and outputs
|
|
simply passes the arguments by value. This Swift function::
|
|
|
|
func foo(_ x:Int, y:Float) -> UnicodeScalar
|
|
|
|
foo(x, y)
|
|
|
|
gets called in SIL as::
|
|
|
|
%foo = constant_ref $(Int, Float) -> UnicodeScalar, @foo
|
|
%z = apply %foo(%x, %y) : $(Int, Float) -> UnicodeScalar
|
|
|
|
Reference Counts
|
|
````````````````
|
|
|
|
*NOTE* This section only is speaking in terms of rules of thumb. The
|
|
actual behavior of arguments with respect to arguments is defined by
|
|
the argument's convention attribute (e.g. ``@owned``), not the
|
|
calling convention itself.
|
|
|
|
Reference type arguments are passed in at +1 retain count and consumed
|
|
by the callee. A reference type return value is returned at +1 and
|
|
consumed by the caller. Value types with reference type components
|
|
have their reference type components each retained and released the
|
|
same way. This Swift function::
|
|
|
|
class A {}
|
|
|
|
func bar(_ x:A) -> (Int, A) { ... }
|
|
|
|
bar(x)
|
|
|
|
gets called in SIL as::
|
|
|
|
%bar = function_ref @bar : $(A) -> (Int, A)
|
|
strong_retain %x : $A
|
|
%z = apply %bar(%x) : $(A) -> (Int, A)
|
|
// ... use %z ...
|
|
%z_1 = tuple_extract %z : $(Int, A), 1
|
|
strong_release %z_1
|
|
|
|
When applying a thick function value as a callee, the function value is also
|
|
consumed at +1 retain count.
|
|
|
|
Address-Only Types
|
|
``````````````````
|
|
|
|
For address-only arguments, the caller allocates a copy and passes the address
|
|
of the copy to the callee. The callee takes ownership of the copy and is
|
|
responsible for destroying or consuming the value, though the caller must still
|
|
deallocate the memory. For address-only return values, the
|
|
caller allocates an uninitialized buffer and passes its address as the first
|
|
argument to the callee. The callee must initialize this buffer before
|
|
returning. This Swift function::
|
|
|
|
@API struct A {}
|
|
|
|
func bas(_ x:A, y:Int) -> A { return x }
|
|
|
|
var z = bas(x, y)
|
|
// ... use z ...
|
|
|
|
gets called in SIL as::
|
|
|
|
%bas = function_ref @bas : $(A, Int) -> A
|
|
%z = alloc_stack $A
|
|
%x_arg = alloc_stack $A
|
|
copy_addr %x to [initialize] %x_arg : $*A
|
|
apply %bas(%z, %x_arg, %y) : $(A, Int) -> A
|
|
dealloc_stack %x_arg : $*A // callee consumes %x.arg, caller deallocs
|
|
// ... use %z ...
|
|
destroy_addr %z : $*A
|
|
dealloc_stack stack %z : $*A
|
|
|
|
The implementation of ``@bas`` is then responsible for consuming ``%x_arg`` and
|
|
initializing ``%z``.
|
|
|
|
Tuple arguments are destructured regardless of the
|
|
address-only-ness of the tuple type. The destructured fields are passed
|
|
individually according to the above convention. This Swift function::
|
|
|
|
@API struct A {}
|
|
|
|
func zim(_ x:Int, y:A, (z:Int, w:(A, Int)))
|
|
|
|
zim(x, y, (z, w))
|
|
|
|
gets called in SIL as::
|
|
|
|
%zim = function_ref @zim : $(x:Int, y:A, (z:Int, w:(A, Int))) -> ()
|
|
%y_arg = alloc_stack $A
|
|
copy_addr %y to [initialize] %y_arg : $*A
|
|
%w_0_addr = element_addr %w : $*(A, Int), 0
|
|
%w_0_arg = alloc_stack $A
|
|
copy_addr %w_0_addr to [initialize] %w_0_arg : $*A
|
|
%w_1_addr = element_addr %w : $*(A, Int), 1
|
|
%w_1 = load %w_1_addr : $*Int
|
|
apply %zim(%x, %y_arg, %z, %w_0_arg, %w_1) : $(x:Int, y:A, (z:Int, w:(A, Int))) -> ()
|
|
dealloc_stack %w_0_arg
|
|
dealloc_stack %y_arg
|
|
|
|
Variadic Arguments
|
|
``````````````````
|
|
|
|
Variadic arguments and tuple elements are packaged into an array and passed as
|
|
a single array argument. This Swift function::
|
|
|
|
func zang(_ x:Int, (y:Int, z:Int...), v:Int, w:Int...)
|
|
|
|
zang(x, (y, z0, z1), v, w0, w1, w2)
|
|
|
|
gets called in SIL as::
|
|
|
|
%zang = function_ref @zang : $(x:Int, (y:Int, z:Int...), v:Int, w:Int...) -> ()
|
|
%zs = <<make array from %z1, %z2>>
|
|
%ws = <<make array from %w0, %w1, %w2>>
|
|
apply %zang(%x, %y, %zs, %v, %ws) : $(x:Int, (y:Int, z:Int...), v:Int, w:Int...) -> ()
|
|
|
|
Function Currying
|
|
`````````````````
|
|
|
|
Curried function definitions in Swift emit multiple SIL entry points, one for
|
|
each "uncurry level" of the function. When a function is uncurried, its
|
|
outermost argument clauses are combined into a tuple in right-to-left order.
|
|
For the following declaration::
|
|
|
|
func curried(_ x:A)(y:B)(z:C)(w:D) -> Int {}
|
|
|
|
The types of the SIL entry points are as follows::
|
|
|
|
sil @curried_0 : $(x:A) -> (y:B) -> (z:C) -> (w:D) -> Int { ... }
|
|
sil @curried_1 : $((y:B), (x:A)) -> (z:C) -> (w:D) -> Int { ... }
|
|
sil @curried_2 : $((z:C), (y:B), (x:A)) -> (w:D) -> Int { ... }
|
|
sil @curried_3 : $((w:D), (z:C), (y:B), (x:A)) -> Int { ... }
|
|
|
|
@inout Arguments
|
|
````````````````
|
|
|
|
``@inout`` arguments are passed into the entry point by address. The callee
|
|
does not take ownership of the referenced memory. The referenced memory must
|
|
be initialized upon function entry and exit. If the ``@inout`` argument
|
|
refers to a fragile physical variable, then the argument is the address of that
|
|
variable. If the ``@inout`` argument refers to a logical property, then the
|
|
argument is the address of a caller-owned writeback buffer. It is the caller's
|
|
responsibility to initialize the buffer by storing the result of the property
|
|
getter prior to calling the function and to write back to the property
|
|
on return by loading from the buffer and invoking the setter with the final
|
|
value. This Swift function::
|
|
|
|
func inout(_ x: inout Int) {
|
|
x = 1
|
|
}
|
|
|
|
gets lowered to SIL as::
|
|
|
|
sil @inout : $(@inout Int) -> () {
|
|
entry(%x : $*Int):
|
|
%1 = integer_literal $Int, 1
|
|
store %1 to %x
|
|
return
|
|
}
|
|
|
|
Swift Method Calling Convention @convention(method)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The method calling convention is currently identical to the freestanding
|
|
function convention. Methods are considered to be curried functions, taking
|
|
the "self" argument as their outer argument clause, and the method arguments
|
|
as the inner argument clause(s). When uncurried, the "self" argument is thus
|
|
passed last::
|
|
|
|
struct Foo {
|
|
func method(_ x:Int) -> Int {}
|
|
}
|
|
|
|
sil @Foo_method_1 : $((x : Int), @inout Foo) -> Int { ... }
|
|
|
|
Witness Method Calling Convention @convention(witness_method)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The witness method calling convention is used by protocol witness methods in
|
|
`witness tables`_. It is identical to the ``method`` calling convention
|
|
except that its handling of generic type parameters. For non-witness methods,
|
|
the machine-level convention for passing type parameter metadata may be
|
|
arbitrarily dependent on static aspects of the function signature, but because
|
|
witnesses must be polymorphically dispatchable on their ``Self`` type,
|
|
the ``Self``-related metadata for a witness must be passed in a maximally
|
|
abstracted manner.
|
|
|
|
C Calling Convention @convention(c)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
In Swift's C module importer, C types are always mapped to Swift types
|
|
considered trivial by SIL. SIL does not concern itself with platform
|
|
ABI requirements for indirect return, register vs. stack passing, etc.; C
|
|
function arguments and returns in SIL are always by value regardless of the
|
|
platform calling convention.
|
|
|
|
SIL (and therefore Swift) cannot currently invoke variadic C functions.
|
|
|
|
Objective-C Calling Convention @convention(objc_method)
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Reference Counts
|
|
````````````````
|
|
|
|
Objective-C methods use the same argument and return value ownership rules as
|
|
ARC Objective-C. Selector families and the ``ns_consumed``,
|
|
``ns_returns_retained``, etc. attributes from imported Objective-C definitions
|
|
are honored.
|
|
|
|
Applying a ``@convention(block)`` value does not consume the block.
|
|
|
|
Method Currying
|
|
```````````````
|
|
|
|
In SIL, the "self" argument of an Objective-C method is uncurried to the last
|
|
argument of the uncurried type, just like a native Swift method::
|
|
|
|
@objc class NSString {
|
|
func stringByPaddingToLength(Int) withString(NSString) startingAtIndex(Int)
|
|
}
|
|
|
|
sil @NSString_stringByPaddingToLength_withString_startingAtIndex \
|
|
: $((Int, NSString, Int), NSString)
|
|
|
|
That ``self`` is passed as the first argument at the IR level is abstracted
|
|
away in SIL, as is the existence of the ``_cmd`` selector argument.
|
|
|
|
Type Based Alias Analysis
|
|
-------------------------
|
|
|
|
SIL supports two types of Type Based Alias Analysis (TBAA): Class TBAA and
|
|
Typed Access TBAA.
|
|
|
|
Class TBAA
|
|
~~~~~~~~~~
|
|
|
|
Class instances and other *heap object references* are pointers at the
|
|
implementation level, but unlike SIL addresses, they are first class values and
|
|
can be ``capture``-d and aliased. Swift, however, is memory-safe and statically
|
|
typed, so aliasing of classes is constrained by the type system as follows:
|
|
|
|
* A ``Builtin.NativeObject`` may alias any native Swift heap object,
|
|
including a Swift class instance, a box allocated by ``alloc_box``,
|
|
or a thick function's closure context.
|
|
It may not alias natively Objective-C class instances.
|
|
* An ``AnyObject`` or ``Builtin.BridgeObject`` may alias any class instance,
|
|
whether Swift or Objective-C, but may not alias non-class-instance
|
|
heap objects.
|
|
* Two values of the same class type ``$C`` may alias. Two values of related
|
|
class type ``$B`` and ``$D``, where there is a subclass relationship between
|
|
``$B`` and ``$D``, may alias. Two values of unrelated class types may not
|
|
alias. This includes different instantiations of a generic class type, such
|
|
as ``$C<Int>`` and ``$C<Float>``, which currently may never alias.
|
|
* Without whole-program visibility, values of archetype or protocol type must
|
|
be assumed to potentially alias any class instance. Even if it is locally
|
|
apparent that a class does not conform to that protocol, another component
|
|
may introduce a conformance by an extension. Similarly, a generic class
|
|
instance, such as ``$C<T>`` for archetype ``T``, must be assumed to
|
|
potentially alias concrete instances of the generic type, such as
|
|
``$C<Int>``, because ``Int`` is a potential substitution for ``T``.
|
|
|
|
A violation of the above aliasing rules only results in undefined
|
|
behavior if the aliasing references are dereferenced within Swift code.
|
|
For example,
|
|
``_SwiftNativeNS[Array|Dictionary|String]`` classes alias with
|
|
``NS[Array|Dictionary|String]`` classes even though they are not
|
|
statically related. Since Swift never directly accesses stored
|
|
properties on the Foundation classes, this aliasing does not pose a
|
|
danger.
|
|
|
|
Typed Access TBAA
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
Define a *typed access* of an address or reference as one of the following:
|
|
|
|
* Any instruction that performs a typed read or write operation upon the memory
|
|
at the given location (e.x. ``load``, ``store``).
|
|
* Any instruction that yields a typed offset of the pointer by performing a
|
|
typed projection operation (e.x. ``ref_element_addr``,
|
|
``tuple_element_addr``).
|
|
|
|
With limited exceptions, it is undefined behavior to perform a typed access to
|
|
an address or reference addressed memory is not bound to the relevant type.
|
|
|
|
This allows the optimizer to assume that two addresses cannot alias if
|
|
there does not exist a substitution of archetypes that could cause one
|
|
of the types to be the type of a subobject of the other. Additionally,
|
|
this applies to the types of the values from which the addresses were
|
|
derived via a typed projection.
|
|
|
|
Consider the following SIL::
|
|
|
|
struct Element {
|
|
var i: Int
|
|
}
|
|
struct S1 {
|
|
var elt: Element
|
|
}
|
|
struct S2 {
|
|
var elt: Element
|
|
}
|
|
%adr1 = struct_element_addr %ptr1 : $*S1, #S.elt
|
|
%adr2 = struct_element_addr %ptr2 : $*S2, #S.elt
|
|
|
|
The optimizer may assume that ``%adr1`` does not alias with ``%adr2``
|
|
because the values that the addresses are derived from (``%ptr1`` and
|
|
``%ptr2``) have unrelated types. However, in the following example,
|
|
the optimizer cannot assume that ``%adr1`` does not alias with
|
|
``%adr2`` because ``%adr2`` is derived from a cast, and any subsequent
|
|
typed operations on the address will refer to the common ``Element`` type::
|
|
|
|
%adr1 = struct_element_addr %ptr1 : $*S1, #S.elt
|
|
%adr2 = pointer_to_address %ptr2 : $Builtin.RawPointer to $*Element
|
|
|
|
Exceptions to typed access TBAA rules are only allowed for blessed
|
|
alias-introducing operations. This permits limited type-punning. The only
|
|
current exception is the non-struct ``pointer_to_address`` variant. The
|
|
optimizer must be able to defensively determine that none of the *roots* of an
|
|
address are alias-introducing operations. An address root is the operation that
|
|
produces the address prior to applying any typed projections, indexing, or
|
|
casts. The following are valid address roots:
|
|
|
|
* Object allocation that generates an address, such as ``alloc_stack``
|
|
and ``alloc_box``.
|
|
|
|
* Address-type function arguments. These are crucially *not* considered
|
|
alias-introducing operations. It is illegal for the SIL optimizer to
|
|
form a new function argument from an arbitrary address-type
|
|
value. Doing so would require the optimizer to guarantee that the
|
|
new argument is both has a non-alias-introducing address root and
|
|
can be properly represented by the calling convention (address types
|
|
do not have a fixed representation).
|
|
|
|
* A strict cast from an untyped pointer, ``pointer_to_address [strict]``. It is
|
|
illegal for ``pointer_to_address [strict]`` to derive its address from an
|
|
alias-introducing operation's value. A type punned address may only be
|
|
produced from an opaque pointer via a non-strict ``pointer_to_address`` at the
|
|
point of conversion.
|
|
|
|
Address-to-address casts, via ``unchecked_addr_cast``, transparently
|
|
forward their source's address root, just like typed projections.
|
|
|
|
Address-type basic block arguments can be conservatively considered
|
|
aliasing-introducing operations; they are uncommon enough not to
|
|
matter and may eventually be prohibited altogether.
|
|
|
|
Although some pointer producing intrinsics exist, they do not need to be
|
|
considered alias-introducing exceptions to TBAA rules. ``Builtin.inttoptr``
|
|
produces a ``Builtin.RawPointer`` which is not interesting because by definition
|
|
it may alias with everything. Similarly, the LLVM builtins ``Builtin.bitcast``
|
|
and ``Builtin.trunc|sext|zextBitCast`` cannot produce typed pointers. These
|
|
pointer values must be converted to an address via ``pointer_to_address`` before
|
|
typed access can occur. Whether the ``pointer_to_address`` is strict determines
|
|
whether aliasing may occur.
|
|
|
|
Memory may be rebound to an unrelated type. Addresses to unrelated types may
|
|
alias as long as typed access only occurs while memory is bound to the relevant
|
|
type. Consequently, the optimizer cannot outright assume that addresses accessed
|
|
as unrelated types are nonaliasing. For example, pointer comparison cannot be
|
|
eliminated simply because the two addresses derived from those pointers are
|
|
accessed as unrelated types at different program points.
|
|
|
|
Value Dependence
|
|
----------------
|
|
|
|
In general, analyses can assume that independent values are
|
|
independently assured of validity. For example, a class method may
|
|
return a class reference::
|
|
|
|
bb0(%0 : $MyClass):
|
|
%1 = class_method %0 : $MyClass, #MyClass.foo!1
|
|
%2 = apply %1(%0) : $@convention(method) (@guaranteed MyClass) -> @owned MyOtherClass
|
|
// use of %2 goes here; no use of %1
|
|
strong_release %2 : $MyOtherClass
|
|
strong_release %1 : $MyClass
|
|
|
|
The optimizer is free to move the release of ``%1`` to immediately
|
|
after the call here, because ``%2`` can be assumed to be an
|
|
independently-managed value, and because Swift generally permits the
|
|
reordering of destructors.
|
|
|
|
However, some instructions do create values that are intrinsically
|
|
dependent on their operands. For example, the result of
|
|
``ref_element_addr`` will become a dangling pointer if the base is
|
|
released too soon. This is captured by the concept of *value dependence*,
|
|
and any transformation which can reorder of destruction of a value
|
|
around another operation must remain conscious of it.
|
|
|
|
A value ``%1`` is said to be *value-dependent* on a value ``%0`` if:
|
|
|
|
- ``%1`` is the result and ``%0`` is the first operand of one of the
|
|
following instructions:
|
|
|
|
- ``ref_element_addr``
|
|
- ``struct_element_addr``
|
|
- ``tuple_element_addr``
|
|
- ``unchecked_take_enum_data_addr``
|
|
- ``pointer_to_address``
|
|
- ``address_to_pointer``
|
|
- ``index_addr``
|
|
- ``index_raw_pointer``
|
|
- possibly some other conversions
|
|
|
|
- ``%1`` is the result of ``mark_dependence`` and ``%0`` is either of
|
|
the operands.
|
|
|
|
- ``%1`` is the value address of a box allocation instruction of which
|
|
``%0`` is the box reference.
|
|
|
|
- ``%1`` is the result of a ``struct``, ``tuple``, or ``enum``
|
|
instruction and ``%0`` is an operand.
|
|
|
|
- ``%1`` is the result of projecting out a subobject of ``%0``
|
|
with ``tuple_extract``, ``struct_extract``, ``unchecked_enum_data``,
|
|
``select_enum``, or ``select_enum_addr``.
|
|
|
|
- ``%1`` is the result of ``select_value`` and ``%0`` is one of the cases.
|
|
|
|
- ``%1`` is a basic block parameter and ``%0`` is the corresponding
|
|
argument from a branch to that block.
|
|
|
|
- ``%1`` is the result of a ``load`` from ``%0``. However, the value
|
|
dependence is cut after the first attempt to manage the value of
|
|
``%1``, e.g. by retaining it.
|
|
|
|
- Transitivity: there exists a value ``%2`` which ``%1`` depends on
|
|
and which depends on ``%0``. However, transitivity does not apply
|
|
to different subobjects of a struct, tuple, or enum.
|
|
|
|
Note, however, that an analysis is not required to track dependence
|
|
through memory. Nor is it required to consider the possibility of
|
|
dependence being established "behind the scenes" by opaque code, such
|
|
as by a method returning an unsafe pointer to a class property. The
|
|
dependence is required to be locally obvious in a function's SIL
|
|
instructions. Precautions must be taken against this either by SIL
|
|
generators (by using ``mark_dependence`` appropriately) or by the user
|
|
(by using the appropriate intrinsics and attributes with unsafe
|
|
language or library features).
|
|
|
|
Only certain types of SIL value can carry value-dependence:
|
|
|
|
- SIL address types
|
|
- unmanaged pointer types:
|
|
|
|
- ``@sil_unmanaged`` types
|
|
- ``Builtin.RawPointer``
|
|
- aggregates containing such a type, such as ``UnsafePointer``,
|
|
possibly recursively
|
|
|
|
- non-trivial types (but they can be independently managed)
|
|
|
|
This rule means that casting a pointer to an integer type breaks
|
|
value-dependence. This restriction is necessary so that reading an
|
|
``Int`` from a class doesn't force the class to be kept around!
|
|
A class holding an unsafe reference to an object must use some
|
|
sort of unmanaged pointer type to do so.
|
|
|
|
This rule does not include generic or resilient value types which
|
|
might contain unmanaged pointer types. Analyses are free to assume
|
|
that e.g. a ``copy_addr`` of a generic or resilient value type yields
|
|
an independently-managed value. The extension of value dependence to
|
|
types containing obvious unmanaged pointer types is an affordance to
|
|
make the use of such types more convenient; it does not shift the
|
|
ultimate responsibility for assuring the safety of unsafe
|
|
language/library features away from the user.
|
|
|
|
Instruction Set
|
|
---------------
|
|
|
|
Allocation and Deallocation
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
These instructions allocate and deallocate memory.
|
|
|
|
alloc_stack
|
|
```````````
|
|
::
|
|
|
|
sil-instruction ::= 'alloc_stack' sil-type (',' debug-var-attr)*
|
|
|
|
%1 = alloc_stack $T
|
|
// %1 has type $*T
|
|
|
|
Allocates uninitialized memory that is sufficiently aligned on the stack
|
|
to contain a value of type ``T``. The result of the instruction is the address
|
|
of the allocated memory.
|
|
|
|
If a type is runtime-sized, the compiler must emit code to potentially
|
|
dynamically allocate memory. So there is no guarantee that the allocated
|
|
memory is really located on the stack.
|
|
|
|
``alloc_stack`` marks the start of the lifetime of the value; the
|
|
allocation must be balanced with a ``dealloc_stack`` instruction to
|
|
mark the end of its lifetime. All ``alloc_stack`` allocations must be
|
|
deallocated prior to returning from a function. If a block has multiple
|
|
predecessors, the stack height and order of allocations must be consistent
|
|
coming from all predecessor blocks. ``alloc_stack`` allocations must be
|
|
deallocated in last-in, first-out stack order.
|
|
|
|
The memory is not retainable. To allocate a retainable box for a value
|
|
type, use ``alloc_box``.
|
|
|
|
alloc_ref
|
|
`````````
|
|
::
|
|
|
|
sil-instruction ::= 'alloc_ref'
|
|
('[' 'objc' ']')?
|
|
('[' 'stack' ']')?
|
|
('[' 'tail_elems' sil-type '*' sil-operand ']')*
|
|
sil-type
|
|
|
|
%1 = alloc_ref [stack] $T
|
|
%1 = alloc_ref [tail_elems $E * %2 : Builtin.Word] $T
|
|
// $T must be a reference type
|
|
// %1 has type $T
|
|
// $E is the type of the tail-allocated elements
|
|
// %2 must be of a builtin integer type
|
|
|
|
Allocates an object of reference type ``T``. The object will be initialized
|
|
with retain count 1; its state will be otherwise uninitialized. The
|
|
optional ``objc`` attribute indicates that the object should be
|
|
allocated using Objective-C's allocation methods (``+allocWithZone:``).
|
|
|
|
The optional ``stack`` attribute indicates that the object can be allocated
|
|
on the stack instead on the heap. In this case the instruction must have
|
|
balanced with a ``dealloc_ref [stack]`` instruction to mark the end of the
|
|
object's lifetime.
|
|
Note that the ``stack`` attribute only specifies that stack allocation is
|
|
possible. The final decision on stack allocation is done during llvm IR
|
|
generation. This is because the decision also depends on the object size,
|
|
which is not necessarily known at SIL level.
|
|
|
|
The optional ``tail_elems`` attributes specifies the amount of space to be
|
|
reserved for tail-allocated arrays of given element types and element counts.
|
|
If there are more than one ``tail_elems`` attributes then the tail arrays are
|
|
allocated in the specified order.
|
|
The count-operand must be of a builtin integer type.
|
|
The instructions ``ref_tail_addr`` and ``tail_addr`` can be used to project
|
|
the tail elements.
|
|
The ``objc`` attribute cannot be used together with ``tail_elems``.
|
|
|
|
alloc_ref_dynamic
|
|
`````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'alloc_ref_dynamic'
|
|
('[' 'objc' ']')?
|
|
('[' 'tail_elems' sil-type '*' sil-operand ']')*
|
|
sil-operand ',' sil-type
|
|
|
|
%1 = alloc_ref_dynamic %0 : $@thick T.Type, $T
|
|
%1 = alloc_ref_dynamic [objc] %0 : $@objc_metatype T.Type, $T
|
|
%1 = alloc_ref_dynamic [tail_elems $E * %2 : Builtin.Word] %0 : $@thick T.Type, $T
|
|
// $T must be a class type
|
|
// %1 has type $T
|
|
// $E is the type of the tail-allocated elements
|
|
// %2 must be of a builtin integer type
|
|
|
|
Allocates an object of class type ``T`` or a subclass thereof. The
|
|
dynamic type of the resulting object is specified via the metatype
|
|
value ``%0``. The object will be initialized with retain count 1; its
|
|
state will be otherwise uninitialized.
|
|
|
|
The optional ``tail_elems`` and ``objc`` attributes have the same effect as
|
|
for ``alloc_ref``. See ``alloc_ref`` for details.
|
|
|
|
alloc_box
|
|
`````````
|
|
::
|
|
|
|
sil-instruction ::= 'alloc_box' sil-type (',' debug-var-attr)*
|
|
|
|
%1 = alloc_box $T
|
|
// %1 has type $@box T
|
|
|
|
Allocates a reference-counted ``@box`` on the heap large enough to hold a value
|
|
of type ``T``, along with a retain count and any other metadata required by the
|
|
runtime. The result of the instruction is the reference-counted ``@box``
|
|
reference that owns the box. The ``project_box`` instruction is used to retrieve
|
|
the address of the value inside the box.
|
|
|
|
The box will be initialized with a retain count of 1; the storage will be
|
|
uninitialized. The box owns the contained value, and releasing it to a retain
|
|
count of zero destroys the contained value as if by ``destroy_addr``.
|
|
Releasing a box is undefined behavior if the box's value is uninitialized.
|
|
To deallocate a box whose value has not been initialized, ``dealloc_box``
|
|
should be used.
|
|
|
|
alloc_value_buffer
|
|
``````````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'alloc_value_buffer' sil-type 'in' sil-operand
|
|
|
|
%1 = alloc_value_buffer $(Int, T) in %0 : $*Builtin.UnsafeValueBuffer
|
|
// The operand must have the exact type shown.
|
|
// The result has type $*(Int, T).
|
|
|
|
Given the address of an unallocated value buffer, allocate space in it
|
|
for a value of the given type. This instruction has undefined
|
|
behavior if the value buffer is currently allocated.
|
|
|
|
The type operand must be a lowered object type.
|
|
|
|
alloc_global
|
|
````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'alloc_global' sil-global-name
|
|
|
|
alloc_global @foo
|
|
|
|
Initialize the storage for a global variable. This instruction has
|
|
undefined behavior if the global variable has already been initialized.
|
|
|
|
The type operand must be a lowered object type.
|
|
|
|
dealloc_stack
|
|
`````````````
|
|
::
|
|
|
|
sil-instruction ::= 'dealloc_stack' sil-operand
|
|
|
|
dealloc_stack %0 : $*T
|
|
// %0 must be of $*T type
|
|
|
|
Deallocates memory previously allocated by ``alloc_stack``. The
|
|
allocated value in memory must be uninitialized or destroyed prior to
|
|
being deallocated. This instruction marks the end of the lifetime for
|
|
the value created by the corresponding ``alloc_stack`` instruction. The operand
|
|
must be the shallowest live ``alloc_stack`` allocation preceding the
|
|
deallocation. In other words, deallocations must be in last-in, first-out
|
|
stack order.
|
|
|
|
dealloc_box
|
|
```````````
|
|
::
|
|
|
|
sil-instruction ::= 'dealloc_box' sil-operand
|
|
|
|
dealloc_box %0 : $@box T
|
|
|
|
Deallocates a box, bypassing the reference counting mechanism. The box
|
|
variable must have a retain count of one. The boxed type must match the
|
|
type passed to the corresponding ``alloc_box`` exactly, or else
|
|
undefined behavior results.
|
|
|
|
This does not destroy the boxed value. The contents of the
|
|
value must have been fully uninitialized or destroyed before
|
|
``dealloc_box`` is applied.
|
|
|
|
project_box
|
|
```````````
|
|
::
|
|
|
|
sil-instruction ::= 'project_box' sil-operand
|
|
|
|
%1 = project_box %0 : $@box T
|
|
|
|
// %1 has type $*T
|
|
|
|
Given a ``@box T`` reference, produces the address of the value inside the box.
|
|
|
|
dealloc_ref
|
|
```````````
|
|
::
|
|
|
|
sil-instruction ::= 'dealloc_ref' ('[' 'stack' ']')? sil-operand
|
|
|
|
dealloc_ref [stack] %0 : $T
|
|
// $T must be a class type
|
|
|
|
Deallocates an uninitialized class type instance, bypassing the reference
|
|
counting mechanism.
|
|
|
|
The type of the operand must match the allocated type exactly, or else
|
|
undefined behavior results.
|
|
|
|
The instance must have a retain count of one.
|
|
|
|
This does not destroy stored properties of the instance. The contents
|
|
of stored properties must be fully uninitialized at the time
|
|
``dealloc_ref`` is applied.
|
|
|
|
The ``stack`` attribute indicates that the instruction is the balanced
|
|
deallocation of its operand which must be a ``alloc_ref [stack]``.
|
|
In this case the instruction marks the end of the object's lifetime but
|
|
has no other effect.
|
|
|
|
dealloc_partial_ref
|
|
```````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'dealloc_partial_ref' sil-operand sil-metatype
|
|
|
|
dealloc_partial_ref %0 : $T, %1 : $U.Type
|
|
// $T must be a class type
|
|
// $T must be a subclass of U
|
|
|
|
Deallocates a partially-initialized class type instance, bypassing
|
|
the reference counting mechanism.
|
|
|
|
The type of the operand must be a supertype of the allocated type, or
|
|
else undefined behavior results.
|
|
|
|
The instance must have a retain count of one.
|
|
|
|
All stored properties in classes more derived than the given metatype
|
|
value must be initialized, and all other stored properties must be
|
|
uninitialized. The initialized stored properties are destroyed before
|
|
deallocating the memory for the instance.
|
|
|
|
This does not destroy the reference type instance. The contents of the
|
|
heap object must have been fully uninitialized or destroyed before
|
|
``dealloc_ref`` is applied.
|
|
|
|
dealloc_value_buffer
|
|
````````````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'dealloc_value_buffer' sil-type 'in' sil-operand
|
|
|
|
dealloc_value_buffer $(Int, T) in %0 : $*Builtin.UnsafeValueBuffer
|
|
// The operand must have the exact type shown.
|
|
|
|
Given the address of a value buffer, deallocate the storage in it.
|
|
This instruction has undefined behavior if the value buffer is not
|
|
currently allocated, or if it was allocated with a type other than the
|
|
type operand.
|
|
|
|
The type operand must be a lowered object type.
|
|
|
|
project_value_buffer
|
|
````````````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'project_value_buffer' sil-type 'in' sil-operand
|
|
|
|
%1 = project_value_buffer $(Int, T) in %0 : $*Builtin.UnsafeValueBuffer
|
|
// The operand must have the exact type shown.
|
|
// The result has type $*(Int, T).
|
|
|
|
Given the address of a value buffer, return the address of the value
|
|
storage in it. This instruction has undefined behavior if the value
|
|
buffer is not currently allocated, or if it was allocated with a type
|
|
other than the type operand.
|
|
|
|
The result is the same value as was originally returned by
|
|
``alloc_value_buffer``.
|
|
|
|
The type operand must be a lowered object type.
|
|
|
|
Debug Information
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
Debug information is generally associated with allocations (alloc_stack or
|
|
alloc_box) by having a Decl node attached to the allocation with a SILLocation.
|
|
For declarations that have no allocation we have explicit instructions for
|
|
doing this. This is used by 'let' declarations, which bind a value to a name
|
|
and for var decls who are promoted into registers. The decl they refer to is
|
|
attached to the instruction with a SILLocation.
|
|
|
|
debug_value
|
|
```````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= debug_value sil-operand (',' debug-var-attr)*
|
|
|
|
debug_value %1 : $Int
|
|
|
|
This indicates that the value of a declaration with loadable type has changed
|
|
value to the specified operand. The declaration in question is identified by
|
|
the SILLocation attached to the debug_value instruction.
|
|
|
|
The operand must have loadable type.
|
|
|
|
::
|
|
|
|
debug-var-attr ::= 'var'
|
|
debug-var-attr ::= 'let'
|
|
debug-var-attr ::= 'name' string-literal
|
|
debug-var-attr ::= 'argno' integer-literal
|
|
|
|
There are a number of attributes that provide details about the source
|
|
variable that is being described, including the name of the
|
|
variable. For function and closure arguments ``argno`` is the number
|
|
of the function argument starting with 1.
|
|
|
|
debug_value_addr
|
|
````````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= debug_value_addr sil-operand (',' debug-var-attr)*
|
|
|
|
debug_value_addr %7 : $*SomeProtocol
|
|
|
|
This indicates that the value of a declaration with address-only type
|
|
has changed value to the specified operand. The declaration in
|
|
question is identified by the SILLocation attached to the
|
|
debug_value_addr instruction.
|
|
|
|
|
|
Accessing Memory
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
load
|
|
````
|
|
::
|
|
|
|
sil-instruction ::= 'load' sil-operand
|
|
|
|
%1 = load %0 : $*T
|
|
// %0 must be of a $*T address type for loadable type $T
|
|
// %1 will be of type $T
|
|
|
|
Loads the value at address ``%0`` from memory. ``T`` must be a loadable type.
|
|
This does not affect the reference count, if any, of the loaded value; the
|
|
value must be retained explicitly if necessary. It is undefined behavior to
|
|
load from uninitialized memory or to load from an address that points to
|
|
deallocated storage.
|
|
|
|
store
|
|
`````
|
|
::
|
|
|
|
sil-instruction ::= 'store' sil-value 'to' sil-operand
|
|
|
|
store %0 to %1 : $*T
|
|
// $T must be a loadable type
|
|
|
|
Stores the value ``%0`` to memory at address ``%1``. The type of %1 is ``*T``
|
|
and the type of ``%0 is ``T``, which must be a loadable type. This will
|
|
overwrite the memory at ``%1``. If ``%1`` already references a value that
|
|
requires ``release`` or other cleanup, that value must be loaded before being
|
|
stored over and cleaned up. It is undefined behavior to store to an address
|
|
that points to deallocated storage.
|
|
|
|
load_borrow
|
|
```````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'load_borrow' sil-value
|
|
|
|
%1 = load_borrow %0 : $*T
|
|
// $T must be a loadable type
|
|
|
|
Loads the value ``%1`` from the memory location ``%0``. The ``load_borrow``
|
|
instruction creates a borrowed scope in which a read-only borrow value ``%1``
|
|
can be used to read the value stored in ``%0``. The end of scope is delimited
|
|
by an ``end_borrow`` instruction. All ``load_borrow`` instructions must be
|
|
paired with exactly one ``end_borrow`` instruction along any path through the
|
|
program. Until ``end_borrow``, it is illegal to invalidate or store to ``%0``.
|
|
|
|
end_borrow
|
|
``````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'end_borrow' sil-value 'from' sil-value : sil-type, sil-type
|
|
|
|
end_borrow %1 from %0 : $T, $T
|
|
end_borrow %1 from %0 : $T, $*T
|
|
end_borrow %1 from %0 : $*T, $T
|
|
end_borrow %1 from %0 : $*T, $*T
|
|
// We allow for end_borrow to be specified in between values and addresses
|
|
// all of the same type T.
|
|
|
|
Ends the scope for which the SILValue ``%1`` is borrowed from the SILValue
|
|
``%0``. Must be paired with at most 1 borrowing instruction (like
|
|
``load_borrow``) along any path through the program. In the region in between
|
|
the borrow instruction and the ``end_borrow``, the original SILValue can not be
|
|
modified. This means that:
|
|
|
|
1. If ``%0`` is an address, ``%0`` can not be written to.
|
|
2. If ``%0`` is a non-trivial value, ``%0`` can not be destroyed.
|
|
|
|
We require that ``%1`` and ``%0`` have the same type ignoring SILValueCategory.
|
|
|
|
assign
|
|
``````
|
|
::
|
|
|
|
sil-instruction ::= 'assign' sil-value 'to' sil-operand
|
|
|
|
assign %0 to %1 : $*T
|
|
// $T must be a loadable type
|
|
|
|
Represents an abstract assignment of the value ``%0`` to memory at address
|
|
``%1`` without specifying whether it is an initialization or a normal store.
|
|
The type of %1 is ``*T`` and the type of ``%0`` is ``T``, which must be a
|
|
loadable type. This will overwrite the memory at ``%1`` and destroy the value
|
|
currently held there.
|
|
|
|
The purpose of the ``assign`` instruction is to simplify the
|
|
definitive initialization analysis on loadable variables by removing
|
|
what would otherwise appear to be a load and use of the current value.
|
|
It is produced by SILGen, which cannot know which assignments are
|
|
meant to be initializations. If it is deemed to be an initialization,
|
|
it can be replaced with a ``store``; otherwise, it must be replaced
|
|
with a sequence that also correctly destroys the current value.
|
|
|
|
This instruction is only valid in Raw SIL and is rewritten as appropriate
|
|
by the definitive initialization pass.
|
|
|
|
mark_uninitialized
|
|
``````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'mark_uninitialized' '[' mu_kind ']' sil-operand
|
|
mu_kind ::= 'var'
|
|
mu_kind ::= 'rootself'
|
|
mu_kind ::= 'crossmodulerootself'
|
|
mu_kind ::= 'derivedself'
|
|
mu_kind ::= 'derivedselfonly'
|
|
mu_kind ::= 'delegatingself'
|
|
|
|
%2 = mark_uninitialized [var] %1 : $*T
|
|
// $T must be an address
|
|
|
|
Indicates that a symbolic memory location is uninitialized, and must be
|
|
explicitly initialized before it escapes or before the current function returns.
|
|
This instruction returns its operands, and all accesses within the function must
|
|
be performed against the return value of the mark_uninitialized instruction.
|
|
|
|
The kind of mark_uninitialized instruction specifies the type of data
|
|
the mark_uninitialized instruction refers to:
|
|
|
|
- ``var``: designates the start of a normal variable live range
|
|
- ``rootself``: designates ``self`` in a struct, enum, or root class
|
|
- ``crossmodulerootself``: same as ``rootself``, but in a case where it's not
|
|
really safe to treat ``self`` as a root because the original module might add
|
|
more stored properties. This is only used for Swift 4 compatibility.
|
|
- ``derivedself``: designates ``self`` in a derived (non-root) class
|
|
- ``derivedselfonly``: designates ``self`` in a derived (non-root) class whose stored properties have already been initialized
|
|
- ``delegatingself``: designates ``self`` on a struct, enum, or class in a delegating constructor (one that calls self.init)
|
|
|
|
The purpose of the ``mark_uninitialized`` instruction is to enable
|
|
definitive initialization analysis for global variables (when marked as
|
|
'globalvar') and instance variables (when marked as 'rootinit'), which need to
|
|
be distinguished from simple allocations.
|
|
|
|
It is produced by SILGen, and is only valid in Raw SIL. It is rewritten as
|
|
appropriate by the definitive initialization pass.
|
|
|
|
mark_function_escape
|
|
````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'mark_function_escape' sil-operand (',' sil-operand)
|
|
|
|
%2 = mark_function_escape %1 : $*T
|
|
|
|
Indicates that a function definition closes over a symbolic memory location.
|
|
This instruction is variadic, and all of its operands must be addresses.
|
|
|
|
The purpose of the ``mark_function_escape`` instruction is to enable
|
|
definitive initialization analysis for global variables and instance variables,
|
|
which are not represented as box allocations.
|
|
|
|
It is produced by SILGen, and is only valid in Raw SIL. It is rewritten as
|
|
appropriate by the definitive initialization pass.
|
|
|
|
mark_uninitialized_behavior
|
|
```````````````````````````
|
|
::
|
|
|
|
init-case ::= sil-value sil-apply-substitution-list? '(' sil-value ')' ':' sil-type
|
|
set-case ::= sil-value sil-apply-substitution-list? '(' sil-value ')' ':' sil-type
|
|
sil-instruction ::= 'mark_uninitialized_behavior' init-case set-case
|
|
|
|
mark_uninitialized_behavior %init<Subs>(%storage) : $T -> U,
|
|
%set<Subs>(%self) : $V -> W
|
|
|
|
Indicates that a logical property is uninitialized at this point and needs to be
|
|
initialized by the end of the function and before any escape point for this
|
|
instruction. Assignments to the property trigger the behavior's ``init`` or
|
|
``set`` logic based on the logical initialization state of the property.
|
|
|
|
It is expected that the ``init-case`` is passed some sort of storage and the
|
|
``set`` case is passed ``self``.
|
|
|
|
This is only valid in Raw SIL.
|
|
|
|
copy_addr
|
|
`````````
|
|
::
|
|
|
|
sil-instruction ::= 'copy_addr' '[take]'? sil-value
|
|
'to' '[initialization]'? sil-operand
|
|
|
|
copy_addr [take] %0 to [initialization] %1 : $*T
|
|
// %0 and %1 must be of the same $*T address type
|
|
|
|
Loads the value at address ``%0`` from memory and assigns a copy of it back into
|
|
memory at address ``%1``. A bare ``copy_addr`` instruction when ``T`` is a
|
|
non-trivial type::
|
|
|
|
copy_addr %0 to %1 : $*T
|
|
|
|
is equivalent to::
|
|
|
|
%new = load %0 : $*T // Load the new value from the source
|
|
%old = load %1 : $*T // Load the old value from the destination
|
|
strong_retain %new : $T // Retain the new value
|
|
strong_release %old : $T // Release the old
|
|
store %new to %1 : $*T // Store the new value to the destination
|
|
|
|
except that ``copy_addr`` may be used even if ``%0`` is of an address-only
|
|
type. The ``copy_addr`` may be given one or both of the ``[take]`` or
|
|
``[initialization]`` attributes:
|
|
|
|
* ``[take]`` destroys the value at the source address in the course of the
|
|
copy.
|
|
* ``[initialization]`` indicates that the destination address is uninitialized.
|
|
Without the attribute, the destination address is treated as already
|
|
initialized, and the existing value will be destroyed before the new value
|
|
is stored.
|
|
|
|
The three attributed forms thus behave like the following loadable type
|
|
operations::
|
|
|
|
// take-assignment
|
|
copy_addr [take] %0 to %1 : $*T
|
|
// is equivalent to:
|
|
%new = load %0 : $*T
|
|
%old = load %1 : $*T
|
|
// no retain of %new!
|
|
strong_release %old : $T
|
|
store %new to %1 : $*T
|
|
|
|
// copy-initialization
|
|
copy_addr %0 to [initialization] %1 : $*T
|
|
// is equivalent to:
|
|
%new = load %0 : $*T
|
|
strong_retain %new : $T
|
|
// no load/release of %old!
|
|
store %new to %1 : $*T
|
|
|
|
// take-initialization
|
|
copy_addr [take] %0 to [initialization] %1 : $*T
|
|
// is equivalent to:
|
|
%new = load %0 : $*T
|
|
// no retain of %new!
|
|
// no load/release of %old!
|
|
store %new to %1 : $*T
|
|
|
|
If ``T`` is a trivial type, then ``copy_addr`` is always equivalent to its
|
|
take-initialization form.
|
|
|
|
destroy_addr
|
|
````````````
|
|
::
|
|
|
|
sil-instruction ::= 'destroy_addr' sil-operand
|
|
|
|
destroy_addr %0 : $*T
|
|
// %0 must be of an address $*T type
|
|
|
|
Destroys the value in memory at address ``%0``. If ``T`` is a non-trivial type,
|
|
This is equivalent to::
|
|
|
|
%1 = load %0
|
|
strong_release %1
|
|
|
|
except that ``destroy_addr`` may be used even if ``%0`` is of an
|
|
address-only type. This does not deallocate memory; it only destroys the
|
|
pointed-to value, leaving the memory uninitialized.
|
|
|
|
If ``T`` is a trivial type, then ``destroy_addr`` is a no-op.
|
|
|
|
index_addr
|
|
``````````
|
|
::
|
|
|
|
sil-instruction ::= 'index_addr' sil-operand ',' sil-operand
|
|
|
|
%2 = index_addr %0 : $*T, %1 : $Builtin.Int<n>
|
|
// %0 must be of an address type $*T
|
|
// %1 must be of a builtin integer type
|
|
// %2 will be of type $*T
|
|
|
|
Given an address that references into an array of values, returns the address
|
|
of the ``%1``-th element relative to ``%0``. The address must reference into
|
|
a contiguous array. It is undefined to try to reference offsets within a
|
|
non-array value, such as fields within a homogeneous struct or tuple type, or
|
|
bytes within a value, using ``index_addr``. (``Int8`` address types have no
|
|
special behavior in this regard, unlike ``char*`` or ``void*`` in C.) It is
|
|
also undefined behavior to index out of bounds of an array, except to index
|
|
the "past-the-end" address of the array.
|
|
|
|
tail_addr
|
|
`````````
|
|
::
|
|
|
|
sil-instruction ::= 'tail_addr' sil-operand ',' sil-operand ',' sil-type
|
|
|
|
%2 = tail_addr %0 : $*T, %1 : $Builtin.Int<n>, $E
|
|
// %0 must be of an address type $*T
|
|
// %1 must be of a builtin integer type
|
|
// %2 will be of type $*E
|
|
|
|
Given an address of an array of ``%1`` values, returns the address of an
|
|
element which is tail-allocated after the array.
|
|
This instruction is equivalent to ``index_addr`` except that the resulting
|
|
address is aligned-up to the tail-element type ``$E``.
|
|
|
|
This instruction is used to project the N-th tail-allocated array from an
|
|
object which is created by an ``alloc_ref`` with multiple ``tail_elems``.
|
|
The first operand is the address of an element of the (N-1)-th array, usually
|
|
the first element. The second operand is the number of elements until the end
|
|
of that array. The result is the address of the first element of the N-th array.
|
|
|
|
It is undefined behavior if the provided address, count and type do not match
|
|
the actual layout of tail-allocated arrays of the underlying object.
|
|
|
|
index_raw_pointer
|
|
`````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'index_raw_pointer' sil-operand ',' sil-operand
|
|
|
|
%2 = index_raw_pointer %0 : $Builtin.RawPointer, %1 : $Builtin.Int<n>
|
|
// %0 must be of $Builtin.RawPointer type
|
|
// %1 must be of a builtin integer type
|
|
// %2 will be of type $Builtin.RawPointer
|
|
|
|
Given a ``Builtin.RawPointer`` value ``%0``, returns a pointer value at the
|
|
byte offset ``%1`` relative to ``%0``.
|
|
|
|
bind_memory
|
|
```````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'bind_memory' sil-operand ',' sil-operand 'to' sil-type
|
|
|
|
bind_memory %0 : $Builtin.RawPointer, %1 : $Builtin.Word to $T
|
|
// %0 must be of $Builtin.RawPointer type
|
|
// %1 must be of $Builtin.Word type
|
|
|
|
Binds memory at ``Builtin.RawPointer`` value ``%0`` to type ``$T`` with enough
|
|
capacity to hold ``%1`` values. See SE-0107: UnsafeRawPointer.
|
|
|
|
begin_access
|
|
````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'begin_access' '[' sil-access ']' '[' sil-enforcement ']' sil-operand
|
|
sil-access ::= init
|
|
sil-access ::= read
|
|
sil-access ::= modify
|
|
sil-access ::= deinit
|
|
sil-enforcement ::= unknown
|
|
sil-enforcement ::= static
|
|
sil-enforcement ::= dynamic
|
|
sil-enforcement ::= unsafe
|
|
|
|
Begins an access to the target memory.
|
|
|
|
The operand must be a *root address derivation*:
|
|
|
|
- a function argument,
|
|
- an ``alloc_stack`` instruction,
|
|
- a ``project_box`` instruction,
|
|
- a ``global_addr`` instruction,
|
|
- a ``ref_element_addr`` instruction, or
|
|
- another ``begin_access`` instruction.
|
|
|
|
It will eventually become a basic structural rule of SIL that no memory
|
|
access instructions can be directly applied to the result of one of these
|
|
instructions; they can only be applied to the result of a ``begin_access``
|
|
on them. For now, this rule will be conditional based on compiler settings
|
|
and the SIL stage.
|
|
|
|
An access is ended with a corresponding ``end_access``. Accesses must be
|
|
uniquely ended on every control flow path which leads to either a function
|
|
exit or back to the ``begin_access`` instruction. The set of active
|
|
accesses must be the same on every edge into a basic block.
|
|
|
|
An ``init`` access takes uninitialized memory and initializes it.
|
|
It must always use ``static`` enforcement.
|
|
|
|
An ``deinit`` access takes initialized memory and leaves it uninitialized.
|
|
It must always use ``static`` enforcement.
|
|
|
|
``read`` and ``modify`` accesses take initialized memory and leave it
|
|
initialized. They may use ``unknown`` enforcement only in the ``raw``
|
|
SIL stage.
|
|
|
|
end_access
|
|
``````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'end_access' ( '[' 'abort' ']' )? sil-operand
|
|
|
|
Ends an access. The operand must be a ``begin_access`` instruction.
|
|
|
|
If the ``begin_access`` is ``init`` or ``deinit``, the ``end_access``
|
|
may be an ``abort``, indicating that the described transition did not
|
|
in fact take place.
|
|
|
|
Reference Counting
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
These instructions handle reference counting of heap objects. Values of
|
|
strong reference type have ownership semantics for the referenced heap
|
|
object. Retain and release operations, however,
|
|
are never implicit in SIL and always must be explicitly performed where needed.
|
|
Retains and releases on the value may be freely moved, and balancing
|
|
retains and releases may deleted, so long as an owning retain count is
|
|
maintained for the uses of the value.
|
|
|
|
All reference-counting operations are defined to work correctly on
|
|
null references (whether strong, unowned, or weak). A non-null
|
|
reference must actually refer to a valid object of the indicated type
|
|
(or a subtype). Address operands are required to be valid and non-null.
|
|
|
|
While SIL makes reference-counting operations explicit, the SIL type
|
|
system also fully represents strength of reference. This is useful
|
|
for several reasons:
|
|
|
|
1. Type-safety: it is impossible to erroneously emit SIL that naively
|
|
uses a ``@weak`` or ``@unowned`` reference as if it were a strong
|
|
reference.
|
|
|
|
2. Consistency: when a reference is kept in memory, instructions like
|
|
``copy_addr`` and ``destroy_addr`` implicitly carry the right
|
|
semantics in the type of the address, rather than needing special
|
|
variants or flags.
|
|
|
|
3. Ease of tooling: SIL directly stores the user's intended strength
|
|
of reference, making it straightforward to generate instrumentation
|
|
that would convey this to a memory profiler. In principle, with
|
|
only a modest number of additions and restrictions on SIL, it would
|
|
even be possible to drop all reference-counting instructions and
|
|
use the type information to feed a garbage collector.
|
|
|
|
strong_retain
|
|
`````````````
|
|
::
|
|
|
|
sil-instruction ::= 'strong_retain' sil-operand
|
|
|
|
strong_retain %0 : $T
|
|
// $T must be a reference type
|
|
|
|
Increases the strong retain count of the heap object referenced by ``%0``.
|
|
|
|
strong_release
|
|
``````````````
|
|
::
|
|
|
|
strong_release %0 : $T
|
|
// $T must be a reference type.
|
|
|
|
Decrements the strong reference count of the heap object referenced by ``%0``.
|
|
If the release operation brings the strong reference count of the object to
|
|
zero, the object is destroyed and ``@weak`` references are cleared. When both
|
|
its strong and unowned reference counts reach zero, the object's memory is
|
|
deallocated.
|
|
|
|
set_deallocating
|
|
````````````````
|
|
::
|
|
|
|
set_deallocating %0 : $T
|
|
// $T must be a reference type.
|
|
|
|
Explicitly sets the state of the object referenced by ``%0`` to deallocated.
|
|
This is the same operation what's done by a strong_release immediately before
|
|
it calls the deallocator of the object.
|
|
|
|
It is expected that the strong reference count of the object is one.
|
|
Furthermore, no other thread may increment the strong reference count during
|
|
execution of this instruction.
|
|
|
|
strong_retain_unowned
|
|
`````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'strong_retain_unowned' sil-operand
|
|
|
|
strong_retain_unowned %0 : $@unowned T
|
|
// $T must be a reference type
|
|
|
|
Asserts that the strong reference count of the heap object referenced by ``%0``
|
|
is still positive, then increases it by one.
|
|
|
|
unowned_retain
|
|
``````````````
|
|
::
|
|
|
|
sil-instruction ::= 'unowned_retain' sil-operand
|
|
|
|
unowned_retain %0 : $@unowned T
|
|
// $T must be a reference type
|
|
|
|
Increments the unowned reference count of the heap object underlying ``%0``.
|
|
|
|
unowned_release
|
|
```````````````
|
|
::
|
|
|
|
sil-instruction ::= 'unowned_release' sil-operand
|
|
|
|
unowned_release %0 : $@unowned T
|
|
// $T must be a reference type
|
|
|
|
Decrements the unowned reference count of the heap object referenced by
|
|
``%0``. When both its strong and unowned reference counts reach zero,
|
|
the object's memory is deallocated.
|
|
|
|
load_weak
|
|
`````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'load_weak' '[take]'? sil-operand
|
|
|
|
load_weak [take] %0 : $*@sil_weak Optional<T>
|
|
// $T must be an optional wrapping a reference type
|
|
|
|
Increments the strong reference count of the heap object held in the operand,
|
|
which must be an initialized weak reference. The result is value of type
|
|
``$Optional<T>``, except that it is ``null`` if the heap object has begun
|
|
deallocation.
|
|
|
|
This operation must be atomic with respect to the final ``strong_release`` on
|
|
the operand heap object. It need not be atomic with respect to ``store_weak``
|
|
operations on the same address.
|
|
|
|
store_weak
|
|
``````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'store_weak' sil-value 'to' '[initialization]'? sil-operand
|
|
|
|
store_weak %0 to [initialization] %1 : $*@sil_weak Optional<T>
|
|
// $T must be an optional wrapping a reference type
|
|
|
|
Initializes or reassigns a weak reference. The operand may be ``nil``.
|
|
|
|
If ``[initialization]`` is given, the weak reference must currently either be
|
|
uninitialized or destroyed. If it is not given, the weak reference must
|
|
currently be initialized.
|
|
|
|
This operation must be atomic with respect to the final ``strong_release`` on
|
|
the operand (source) heap object. It need not be atomic with respect to
|
|
``store_weak`` or ``load_weak`` operations on the same address.
|
|
|
|
load_unowned
|
|
````````````
|
|
|
|
TODO: Fill this in
|
|
|
|
store_unowned
|
|
`````````````
|
|
|
|
TODO: Fill this in
|
|
|
|
fix_lifetime
|
|
````````````
|
|
|
|
::
|
|
|
|
sil-instruction :: 'fix_lifetime' sil-operand
|
|
|
|
fix_lifetime %0 : $T
|
|
// Fix the lifetime of a value %0
|
|
fix_lifetime %1 : $*T
|
|
// Fix the lifetime of the memory object referenced by %1
|
|
|
|
Acts as a use of a value operand, or of the value in memory referenced by an
|
|
address operand. Optimizations may not move operations that would destroy the
|
|
value, such as ``release_value``, ``strong_release``, ``copy_addr [take]``, or
|
|
``destroy_addr``, past this instruction.
|
|
|
|
mark_dependence
|
|
```````````````
|
|
|
|
::
|
|
|
|
sil-instruction :: 'mark_dependence' sil-operand 'on' sil-operand
|
|
|
|
%2 = mark_dependence %0 : $*T on %1 : $Builtin.NativeObject
|
|
|
|
Indicates that the validity of the first operand depends on the value
|
|
of the second operand. Operations that would destroy the second value
|
|
must not be moved before any instructions which depend on the result
|
|
of this instruction, exactly as if the address had been obviously
|
|
derived from that operand (e.g. using ``ref_element_addr``).
|
|
|
|
The result is always equal to the first operand. The first operand
|
|
will typically be an address, but it could be an address in a
|
|
non-obvious form, such as a Builtin.RawPointer or a struct containing
|
|
the same. Transformations should be somewhat forgiving here.
|
|
|
|
The second operand may have either object or address type. In the
|
|
latter case, the dependency is on the current value stored in the
|
|
address.
|
|
|
|
strong_pin
|
|
``````````
|
|
|
|
TODO: Fill me in!
|
|
|
|
strong_unpin
|
|
````````````
|
|
|
|
TODO: Fill me in!
|
|
|
|
is_unique
|
|
`````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'is_unique' sil-operand
|
|
|
|
%1 = is_unique %0 : $*T
|
|
// $T must be a reference-counted type
|
|
// %1 will be of type Builtin.Int1
|
|
|
|
Checks whether %0 is the address of a unique reference to a memory
|
|
object. Returns 1 if the strong reference count is 1, and 0 if the
|
|
strong reference count is greater than 1.
|
|
|
|
A discussion of the semantics can be found here:
|
|
:ref:`arcopts.is_unique`.
|
|
|
|
is_unique_or_pinned
|
|
```````````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'is_unique_or_pinned' sil-operand
|
|
|
|
%1 = is_unique_or_pinned %0 : $*T
|
|
// $T must be a reference-counted type
|
|
// %1 will be of type Builtin.Int1
|
|
|
|
Checks whether %0 is the address of either a unique reference to a
|
|
memory object or a reference to a pinned object. Returns 1 if the
|
|
strong reference count is 1 or the object has been marked pinned by
|
|
strong_pin.
|
|
|
|
is_escaping_closure
|
|
```````````````````
|
|
|
|
::
|
|
sil-instruction ::= 'is_escaping_closure' sil-operand
|
|
|
|
%1 = is_escaping_closure %0 : $@callee_guaranteed () -> ()
|
|
// %0 must be an escaping swift closure.
|
|
// %1 will be of type Builtin.Int1
|
|
|
|
Checks whether the context reference is not nil and bigger than one and returns
|
|
true if it is.
|
|
|
|
copy_block
|
|
``````````
|
|
::
|
|
|
|
sil-instruction :: 'copy_block' sil-operand
|
|
|
|
%1 = copy_block %0 : $@convention(block) T -> U
|
|
|
|
Performs a copy of an Objective-C block. Unlike retains of other
|
|
reference-counted types, this can produce a different value from the operand
|
|
if the block is copied from the stack to the heap.
|
|
|
|
builtin "unsafeGuaranteed"
|
|
``````````````````````````
|
|
|
|
::
|
|
|
|
sil-instruction := 'builtin' '"unsafeGuaranteed"' '<' sil-type '>' '(' sil-operand')' ':' sil-type
|
|
|
|
%1 = builtin "unsafeGuaranteed"<T>(%0 : $T) : ($T, Builtin.Int1)
|
|
// $T must be of AnyObject type.
|
|
|
|
Asserts that there exists another reference of the value ``%0`` for the scope
|
|
delineated by the call of this builtin up to the first call of a ``builtin
|
|
"unsafeGuaranteedEnd"`` instruction that uses the second element ``%1.1`` of the
|
|
returned value. If no such instruction can be found nothing can be assumed. This
|
|
assertions holds for uses of the first tuple element of the returned value
|
|
``%1.0`` within this scope. The returned reference value equals the input
|
|
``%0``.
|
|
|
|
builtin "unsafeGuaranteedEnd"
|
|
`````````````````````````````
|
|
|
|
::
|
|
|
|
sil-instruction := 'builtin' '"unsafeGuaranteedEnd"' '(' sil-operand')'
|
|
|
|
%1 = builtin "unsafeGuaranteedEnd"(%0 : $Builtin.Int1)
|
|
// $T must be of AnyObject type.
|
|
|
|
Ends the scope for the ``builtin "unsafeGuaranteed"`` instruction.
|
|
|
|
Literals
|
|
~~~~~~~~
|
|
|
|
These instructions bind SIL values to literal constants or to global entities.
|
|
|
|
function_ref
|
|
````````````
|
|
::
|
|
|
|
sil-instruction ::= 'function_ref' sil-function-name ':' sil-type
|
|
|
|
%1 = function_ref @function : $@convention(thin) T -> U
|
|
// $@convention(thin) T -> U must be a thin function type
|
|
// %1 has type $T -> U
|
|
|
|
Creates a reference to a SIL function.
|
|
|
|
global_addr
|
|
```````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'global_addr' sil-global-name ':' sil-type
|
|
|
|
%1 = global_addr @foo : $*Builtin.Word
|
|
|
|
Creates a reference to the address of a global variable which has been
|
|
previously initialized by ``alloc_global``. It is undefined behavior to
|
|
perform this operation on a global variable which has not been
|
|
initialized, except the global variable has a static initializer.
|
|
|
|
global_value
|
|
`````````````
|
|
::
|
|
|
|
sil-instruction ::= 'global_value' sil-global-name ':' sil-type
|
|
|
|
%1 = global_value @v : $T
|
|
|
|
Returns the value of a global variable which has been previously initialized
|
|
by ``alloc_global``. It is undefined behavior to perform this operation on a
|
|
global variable which has not been initialized, except the global variable
|
|
has a static initializer.
|
|
|
|
integer_literal
|
|
```````````````
|
|
::
|
|
|
|
sil-instruction ::= 'integer_literal' sil-type ',' int-literal
|
|
|
|
%1 = integer_literal $Builtin.Int<n>, 123
|
|
// $Builtin.Int<n> must be a builtin integer type
|
|
// %1 has type $Builtin.Int<n>
|
|
|
|
Creates an integer literal value. The result will be of type
|
|
``Builtin.Int<n>``, which must be a builtin integer type. The literal value
|
|
is specified using Swift's integer literal syntax.
|
|
|
|
float_literal
|
|
`````````````
|
|
::
|
|
|
|
sil-instruction ::= 'float_literal' sil-type ',' int-literal
|
|
|
|
%1 = float_literal $Builtin.FP<n>, 0x3F800000
|
|
// $Builtin.FP<n> must be a builtin floating-point type
|
|
// %1 has type $Builtin.FP<n>
|
|
|
|
Creates a floating-point literal value. The result will be of type ``
|
|
``Builtin.FP<n>``, which must be a builtin floating-point type. The literal
|
|
value is specified as the bitwise representation of the floating point value,
|
|
using Swift's hexadecimal integer literal syntax.
|
|
|
|
string_literal
|
|
``````````````
|
|
::
|
|
|
|
sil-instruction ::= 'string_literal' encoding string-literal
|
|
encoding ::= 'utf8'
|
|
encoding ::= 'utf16'
|
|
encoding ::= 'objc_selector'
|
|
|
|
%1 = string_literal "asdf"
|
|
// %1 has type $Builtin.RawPointer
|
|
|
|
Creates a reference to a string in the global string table. The result
|
|
is a pointer to the data. The referenced string is always null-terminated. The
|
|
string literal value is specified using Swift's string
|
|
literal syntax (though ``\()`` interpolations are not allowed). When
|
|
the encoding is ``objc_selector``, the string literal produces a
|
|
reference to a UTF-8-encoded Objective-C selector in the Objective-C
|
|
method name segment.
|
|
|
|
Dynamic Dispatch
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
These instructions perform dynamic lookup of class and generic methods.
|
|
|
|
The ``class_method`` and ``super_method`` instructions must reference
|
|
Swift native methods and always use vtable dispatch.
|
|
|
|
The ``objc_method`` and ``objc_super_method`` instructions must reference
|
|
Objective-C methods (indicated by the ``foreign`` marker on a method
|
|
reference, as in ``#NSObject.description!1.foreign``).
|
|
|
|
Note that ``objc_msgSend`` invocations can only be used as the callee
|
|
of an ``apply`` instruction or ``partial_apply`` instruction. They cannot
|
|
be stored or used as ``apply`` or ``partial_apply`` arguments.
|
|
|
|
class_method
|
|
````````````
|
|
::
|
|
|
|
sil-instruction ::= 'class_method' sil-method-attributes?
|
|
sil-operand ',' sil-decl-ref ':' sil-type
|
|
|
|
%1 = class_method %0 : $T, #T.method!1 : $@convention(class_method) U -> V
|
|
// %0 must be of a class type or class metatype $T
|
|
// #T.method!1 must be a reference to a Swift native method of T or
|
|
// of one of its superclasses, at uncurry level == 1
|
|
// %1 will be of type $U -> V
|
|
|
|
Looks up a method based on the dynamic type of a class or class metatype
|
|
instance. It is undefined behavior if the class value is null.
|
|
|
|
If the static type of the class instance is known, or the method is known
|
|
to be final, then the instruction is a candidate for devirtualization
|
|
optimization. A devirtualization pass can consult the module's `VTables`_
|
|
to find the SIL function that implements the method and promote the
|
|
instruction to a static `function_ref`_.
|
|
|
|
objc_method
|
|
```````````
|
|
::
|
|
|
|
sil-instruction ::= 'objc_method' sil-method-attributes?
|
|
sil-operand ',' sil-decl-ref ':' sil-type
|
|
|
|
%1 = objc_method %0 : $T, #T.method!1.foreign : $@convention(objc_method) U -> V
|
|
// %0 must be of a class type or class metatype $T
|
|
// #T.method!1 must be a reference to an Objective-C method of T or
|
|
// of one of its superclasses, at uncurry level == 1
|
|
// %1 will be of type $U -> V
|
|
|
|
Performs Objective-C method dispatch using ``objc_msgSend()``.
|
|
|
|
Objective-C method calls are never candidates for devirtualization.
|
|
|
|
super_method
|
|
````````````
|
|
::
|
|
|
|
sil-instruction ::= 'super_method' sil-method-attributes?
|
|
sil-operand ',' sil-decl-ref ':' sil-type
|
|
|
|
%1 = super_method %0 : $T, #Super.method!1 : $@convention(thin) U -> V
|
|
// %0 must be of a non-root class type or class metatype $T
|
|
// #Super.method!1 must be a reference to a native Swift method of T's
|
|
// superclass or of one of its ancestor classes, at uncurry level >= 1
|
|
// %1 will be of type $@convention(thin) U -> V
|
|
|
|
Looks up a method in the superclass of a class or class metatype instance.
|
|
|
|
objc_super_method
|
|
`````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'super_method' sil-method-attributes?
|
|
sil-operand ',' sil-decl-ref ':' sil-type
|
|
|
|
%1 = super_method %0 : $T, #Super.method!1.foreign : $@convention(thin) U -> V
|
|
// %0 must be of a non-root class type or class metatype $T
|
|
// #Super.method!1.foreign must be a reference to an ObjC method of T's
|
|
// superclass or of one of its ancestor classes, at uncurry level >= 1
|
|
// %1 will be of type $@convention(thin) U -> V
|
|
|
|
This instruction performs an Objective-C message send using
|
|
``objc_msgSuper()``.
|
|
|
|
witness_method
|
|
``````````````
|
|
::
|
|
|
|
sil-instruction ::= 'witness_method' sil-method-attributes?
|
|
sil-type ',' sil-decl-ref ':' sil-type
|
|
|
|
%1 = witness_method $T, #Proto.method!1 \
|
|
: $@convention(witness_method) <Self: Proto> U -> V
|
|
// $T must be an archetype
|
|
// #Proto.method!1 must be a reference to a method of one of the protocol
|
|
// constraints on T
|
|
// <Self: Proto> U -> V must be the type of the referenced method,
|
|
// generic on Self
|
|
// %1 will be of type $@convention(thin) <Self: Proto> U -> V
|
|
|
|
Looks up the implementation of a protocol method for a generic type variable
|
|
constrained by that protocol. The result will be generic on the ``Self``
|
|
archetype of the original protocol and have the ``witness_method`` calling
|
|
convention. If the referenced protocol is an ``@objc`` protocol, the
|
|
resulting type has the ``objc`` calling convention.
|
|
|
|
Function Application
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
These instructions call functions or wrap them in partial application or
|
|
specialization thunks.
|
|
|
|
apply
|
|
`````
|
|
::
|
|
|
|
sil-instruction ::= 'apply' '[nothrow]'? sil-value
|
|
sil-apply-substitution-list?
|
|
'(' (sil-value (',' sil-value)*)? ')'
|
|
':' sil-type
|
|
|
|
sil-apply-substitution-list ::= '<' sil-substitution
|
|
(',' sil-substitution)* '>'
|
|
sil-substitution ::= type '=' type
|
|
|
|
%r = apply %0(%1, %2, ...) : $(A, B, ...) -> R
|
|
// Note that the type of the callee '%0' is specified *after* the arguments
|
|
// %0 must be of a concrete function type $(A, B, ...) -> R
|
|
// %1, %2, etc. must be of the argument types $A, $B, etc.
|
|
// %r will be of the return type $R
|
|
|
|
%r = apply %0<A, B>(%1, %2, ...) : $<T, U>(T, U, ...) -> R
|
|
// %0 must be of a polymorphic function type $<T, U>(T, U, ...) -> R
|
|
// %1, %2, etc. must be of the argument types after substitution $A, $B, etc.
|
|
// %r will be of the substituted return type $R'
|
|
|
|
Transfers control to function ``%0``, passing it the given arguments. In
|
|
the instruction syntax, the type of the callee is specified after the argument
|
|
list; the types of the argument and of the defined value are derived from the
|
|
function type of the callee. The input argument tuple type is destructured,
|
|
and each element is passed as an individual argument. The ``apply``
|
|
instruction does no retaining or releasing of its arguments by itself; the
|
|
`calling convention`_'s retain/release policy must be handled by separate
|
|
explicit ``retain`` and ``release`` instructions. The return value will
|
|
likewise not be implicitly retained or released.
|
|
|
|
The callee value must have function type. That function type may not
|
|
have an error result, except the instruction has the ``nothrow`` attribute set.
|
|
The ``nothrow`` attribute specifies that the callee has an error result but
|
|
does not actually throw.
|
|
For the regular case of calling a function with error result, use ``try_apply``.
|
|
|
|
NB: If the callee value is of a thick function type, ``apply`` currently
|
|
consumes the callee value at +1 strong retain count.
|
|
|
|
If the callee is generic, all of its generic parameters must be bound by the
|
|
given substitution list. The arguments and return value is
|
|
given with these generic substitutions applied.
|
|
|
|
begin_apply
|
|
```````````
|
|
::
|
|
|
|
sil-instruction ::= 'begin_apply' '[nothrow]'? sil-value
|
|
sil-apply-substitution-list?
|
|
'(' (sil-value (',' sil-value)*)? ')'
|
|
':' sil-type
|
|
|
|
(%anyAddr, %float, %token) = begin_apply %0() : $@yield_once () -> (@yields @inout %Any, @yields Float)
|
|
// %anyAddr : $*Any
|
|
// %float : $Float
|
|
// %token is a token
|
|
|
|
Transfers control to coroutine ``%0``, passing it the given arguments.
|
|
The rules for the application generally follow the rules for ``apply``,
|
|
except:
|
|
|
|
- the callee value must have a ``yield_once`` coroutine type,
|
|
|
|
- control returns to this function not when the coroutine performs a
|
|
``return``, but when it performs a ``yield``, and
|
|
|
|
- the instruction results are derived from the yields of the coroutine
|
|
instead of its normal results.
|
|
|
|
The final result of a ``begin_apply`` is a "token", a special value which
|
|
can only be used as the operand of an ``end_apply`` or ``abort_apply``
|
|
instruction. Before this second instruction is executed, the coroutine
|
|
is said to be "suspended", and the token represents a reference to its
|
|
suspended activation record.
|
|
|
|
The other results of the instruction correspond to the yields in the
|
|
coroutine type. In general, the rules of a yield are similar to the rules
|
|
for a parameter, interpreted as if the coroutine caller (the one
|
|
executing the ``begin_apply``) were being "called" by the ``yield``:
|
|
|
|
- If a yield has an indirect convention, the corresponding result will
|
|
have an address type; otherwise it has an object type. For example,
|
|
a result corresponding to an ``@in Any`` yield will have type ``$Any``.
|
|
|
|
- The convention attributes are the same as the parameter convention
|
|
attributes, interpreted as if the ``yield`` were the "call" and the
|
|
``begin_apply`` marked the entry to the "callee". For example,
|
|
an ``@in Any`` yield transfers ownership of the ``Any`` value
|
|
reference from the coroutine to the caller, which must destroy
|
|
or move the value from that position before ending or aborting the
|
|
coroutine.
|
|
|
|
A ``begin_apply`` must be uniquely either ended or aborted before
|
|
exiting the function or looping to an earlier portion of the function.
|
|
|
|
When throwing coroutines are supported, there will need to be a
|
|
``try_begin_apply`` instruction.
|
|
|
|
abort_apply
|
|
```````````
|
|
::
|
|
|
|
sil-instruction ::= 'abort_apply' sil-value
|
|
|
|
abort_apply %token
|
|
|
|
Aborts the given coroutine activation, which is currently suspended at
|
|
a ``yield`` instruction. Transfers control to the coroutine and takes
|
|
the ``unwind`` path from the ``yield``. Control is transferred back
|
|
when the coroutine reaches an ``unwind`` instruction.
|
|
|
|
The operand must always be the token result of a ``begin_apply``
|
|
instruction, which is why it need not specify a type.
|
|
|
|
Throwing coroutines will not require a new instruction for aborting
|
|
a coroutine; a coroutine is not allowed to throw when it is being aborted.
|
|
|
|
end_apply
|
|
`````````
|
|
::
|
|
|
|
sil-instruction ::= 'end_apply' sil-value
|
|
|
|
end_apply %token
|
|
|
|
Ends the given coroutine activation, which is currently suspended at
|
|
a ``yield`` instruction. Transfers control to the coroutine and takes
|
|
the ``resume`` path from the ``yield``. Control is transferred back
|
|
when the coroutine reaches a ``return`` instruction.
|
|
|
|
The operand must always be the token result of a ``begin_apply``
|
|
instruction, which is why it need not specify a type.
|
|
|
|
``end_apply`` currently has no instruction results. If coroutines were
|
|
allowed to have normal results, they would be producted by ``end_apply``.
|
|
|
|
When throwing coroutines are supported, there will need to be a
|
|
``try_end_apply`` instruction.
|
|
|
|
partial_apply
|
|
`````````````
|
|
::
|
|
|
|
sil-instruction ::= 'partial_apply' callee-ownership-attr? sil-value
|
|
sil-apply-substitution-list?
|
|
'(' (sil-value (',' sil-value)*)? ')'
|
|
':' sil-type
|
|
callee-ownership-attr ::= '[callee_guaranteed]'
|
|
|
|
%c = partial_apply %0(%1, %2, ...) : $(Z..., A, B, ...) -> R
|
|
// Note that the type of the callee '%0' is specified *after* the arguments
|
|
// %0 must be of a concrete function type $(Z..., A, B, ...) -> R
|
|
// %1, %2, etc. must be of the argument types $A, $B, etc.,
|
|
// of the tail part of the argument tuple of %0
|
|
// %c will be of the partially-applied thick function type (Z...) -> R
|
|
|
|
%c = partial_apply %0<A, B>(%1, %2, ...) : $(Z..., T, U, ...) -> R
|
|
// %0 must be of a polymorphic function type $<T, U>(T, U, ...) -> R
|
|
// %1, %2, etc. must be of the argument types after substitution $A, $B, etc.
|
|
// of the tail part of the argument tuple of %0
|
|
// %r will be of the substituted thick function type $(Z'...) -> R'
|
|
|
|
Creates a closure by partially applying the function ``%0`` to a partial
|
|
sequence of its arguments. In the instruction syntax, the type of the callee is
|
|
specified after the argument list; the types of the argument and of the defined
|
|
value are derived from the function type of the callee. The closure context will
|
|
be allocated with retain count 1 and initialized to contain the values ``%1``,
|
|
``%2``, etc. The closed-over values will not be retained; that must be done
|
|
separately before the ``partial_apply``. The closure does however take
|
|
ownership of the partially applied arguments; when the closure reference
|
|
count reaches zero, the contained values will be destroyed.
|
|
|
|
If the callee is generic, all of its generic parameters must be bound by the
|
|
given substitution list. The arguments are given with these generic
|
|
substitutions applied, and the resulting closure is of concrete function
|
|
type with the given substitutions applied. The generic parameters themselves
|
|
cannot be partially applied; all of them must be bound. The result is always
|
|
a concrete function.
|
|
|
|
TODO: The instruction, when applied to a generic function,
|
|
currently implicitly performs abstraction difference transformations enabled
|
|
by the given substitutions, such as promoting address-only arguments and returns
|
|
to register arguments. This should be fixed.
|
|
|
|
By default, ``partial_apply`` creates a closure whose invocation takes ownership
|
|
of the context, meaning that a call implicitly releases the closure. The
|
|
``[callee_guaranteed]`` change this to a caller-guaranteed model, where the
|
|
caller promises not to release the closure while the function is being called.
|
|
|
|
This instruction is used to implement both curry thunks and closures. A
|
|
curried function in Swift::
|
|
|
|
func foo(_ a:A)(b:B)(c:C)(d:D) -> E { /* body of foo */ }
|
|
|
|
emits curry thunks in SIL as follows (retains and releases omitted for
|
|
clarity)::
|
|
|
|
func @foo : $@convention(thin) A -> B -> C -> D -> E {
|
|
entry(%a : $A):
|
|
%foo_1 = function_ref @foo_1 : $@convention(thin) (B, A) -> C -> D -> E
|
|
%thunk = partial_apply %foo_1(%a) : $@convention(thin) (B, A) -> C -> D -> E
|
|
return %thunk : $B -> C -> D -> E
|
|
}
|
|
|
|
func @foo_1 : $@convention(thin) (B, A) -> C -> D -> E {
|
|
entry(%b : $B, %a : $A):
|
|
%foo_2 = function_ref @foo_2 : $@convention(thin) (C, B, A) -> D -> E
|
|
%thunk = partial_apply %foo_2(%b, %a) \
|
|
: $@convention(thin) (C, B, A) -> D -> E
|
|
return %thunk : $(B, A) -> C -> D -> E
|
|
}
|
|
|
|
func @foo_2 : $@convention(thin) (C, B, A) -> D -> E {
|
|
entry(%c : $C, %b : $B, %a : $A):
|
|
%foo_3 = function_ref @foo_3 : $@convention(thin) (D, C, B, A) -> E
|
|
%thunk = partial_apply %foo_3(%c, %b, %a) \
|
|
: $@convention(thin) (D, C, B, A) -> E
|
|
return %thunk : $(C, B, A) -> D -> E
|
|
}
|
|
|
|
func @foo_3 : $@convention(thin) (D, C, B, A) -> E {
|
|
entry(%d : $D, %c : $C, %b : $B, %a : $A):
|
|
// ... body of foo ...
|
|
}
|
|
|
|
A local function in Swift that captures context, such as ``bar`` in the
|
|
following example::
|
|
|
|
func foo(_ x:Int) -> Int {
|
|
func bar(_ y:Int) -> Int {
|
|
return x + y
|
|
}
|
|
return bar(1)
|
|
}
|
|
|
|
lowers to an uncurried entry point and is curried in the enclosing function::
|
|
|
|
func @bar : $@convention(thin) (Int, @box Int, *Int) -> Int {
|
|
entry(%y : $Int, %x_box : $@box Int, %x_address : $*Int):
|
|
// ... body of bar ...
|
|
}
|
|
|
|
func @foo : $@convention(thin) Int -> Int {
|
|
entry(%x : $Int):
|
|
// Create a box for the 'x' variable
|
|
%x_box = alloc_box $Int
|
|
%x_addr = project_box %x_box : $@box Int
|
|
store %x to %x_addr : $*Int
|
|
|
|
// Create the bar closure
|
|
%bar_uncurried = function_ref @bar : $(Int, Int) -> Int
|
|
%bar = partial_apply %bar_uncurried(%x_box, %x_addr) \
|
|
: $(Int, Builtin.NativeObject, *Int) -> Int
|
|
|
|
// Apply it
|
|
%1 = integer_literal $Int, 1
|
|
%ret = apply %bar(%1) : $(Int) -> Int
|
|
|
|
// Clean up
|
|
release %bar : $(Int) -> Int
|
|
return %ret : $Int
|
|
}
|
|
|
|
builtin
|
|
```````
|
|
::
|
|
|
|
sil-instruction ::= 'builtin' string-literal
|
|
sil-apply-substitution-list?
|
|
'(' (sil-operand (',' sil-operand)*)? ')'
|
|
':' sil-type
|
|
|
|
%1 = builtin "foo"(%1 : $T, %2 : $U) : $V
|
|
// "foo" must name a function in the Builtin module
|
|
|
|
Invokes functionality built into the backend code generator, such as LLVM-
|
|
level instructions and intrinsics.
|
|
|
|
Metatypes
|
|
~~~~~~~~~
|
|
|
|
These instructions access metatypes, either statically by type name or
|
|
dynamically by introspecting class or generic values.
|
|
|
|
metatype
|
|
````````
|
|
::
|
|
|
|
sil-instruction ::= 'metatype' sil-type
|
|
|
|
%1 = metatype $T.Type
|
|
// %1 has type $T.Type
|
|
|
|
Creates a reference to the metatype object for type ``T``.
|
|
|
|
value_metatype
|
|
``````````````
|
|
::
|
|
|
|
sil-instruction ::= 'value_metatype' sil-type ',' sil-operand
|
|
|
|
%1 = value_metatype $T.Type, %0 : $T
|
|
// %0 must be a value or address of type $T
|
|
// %1 will be of type $T.Type
|
|
|
|
Obtains a reference to the dynamic metatype of the value ``%0``.
|
|
|
|
existential_metatype
|
|
````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'existential_metatype' sil-type ',' sil-operand
|
|
|
|
%1 = existential_metatype $P.Type, %0 : $P
|
|
// %0 must be a value of class protocol or protocol composition
|
|
// type $P, or an address of address-only protocol type $*P
|
|
// %1 will be a $P.Type value referencing the metatype of the
|
|
// concrete value inside %0
|
|
|
|
Obtains the metatype of the concrete value
|
|
referenced by the existential container referenced by ``%0``.
|
|
|
|
objc_protocol
|
|
`````````````
|
|
::
|
|
|
|
sil-instruction ::= 'objc_protocol' protocol-decl : sil-type
|
|
|
|
%0 = objc_protocol #ObjCProto : $Protocol
|
|
|
|
*TODO* Fill this in.
|
|
|
|
Aggregate Types
|
|
~~~~~~~~~~~~~~~
|
|
|
|
These instructions construct and project elements from structs, tuples, and
|
|
class instances.
|
|
|
|
retain_value
|
|
````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'retain_value' sil-operand
|
|
|
|
retain_value %0 : $A
|
|
|
|
Retains a loadable value, which simply retains any references it holds.
|
|
|
|
For trivial types, this is a no-op. For reference types, this is equivalent to
|
|
a ``strong_retain``. For ``@unowned`` types, this is equivalent to an
|
|
``unowned_retain``. In each of these cases, those are the preferred forms.
|
|
|
|
For aggregate types, especially enums, it is typically both easier
|
|
and more efficient to reason about aggregate copies than it is to
|
|
reason about copies of the subobjects.
|
|
|
|
retain_value_addr
|
|
`````````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'retain_value_addr' sil-operand
|
|
|
|
retain_value_addr %0 : $*A
|
|
|
|
Retains a loadable value inside given address,
|
|
which simply retains any references it holds.
|
|
|
|
unmanaged_retain_value
|
|
``````````````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'unmanaged_retain_value' sil-value
|
|
|
|
unmanaged_retain_value %0 : $A
|
|
|
|
This instruction has the same local semantics as ``retain_value`` but:
|
|
|
|
* Is valid in ownership qualified SIL.
|
|
* Is not intended to be statically paired at compile time by the compiler.
|
|
|
|
The intention is that this instruction is used to implement unmanaged
|
|
constructs.
|
|
|
|
copy_value
|
|
``````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'copy_value' sil-operand
|
|
|
|
%1 = copy_value %0 : $A
|
|
|
|
Performs a copy of a loadable value as if by the value's type lowering and
|
|
returns the copy. The returned copy semantically is a value that is completely
|
|
independent of the operand. In terms of specific types:
|
|
|
|
1. For trivial types, this is equivalent to just propagating through the trivial
|
|
value.
|
|
2. For reference types, this is equivalent to performing a ``strong_retain``
|
|
operation and returning the reference.
|
|
3. For ``@unowned`` types, this is equivalent to performing an
|
|
``unowned_retain`` and returning the operand.
|
|
4. For aggregate types, this is equivalent to recursively performing a
|
|
``copy_value`` on its components, forming a new aggregate from the copied
|
|
components, and then returning the new aggregate.
|
|
|
|
In ownership qualified functions, a ``copy_value`` produces a +1 value that must
|
|
be consumed at most once along any path through the program.
|
|
|
|
release_value
|
|
`````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'release_value' sil-operand
|
|
|
|
release_value %0 : $A
|
|
|
|
Destroys a loadable value, by releasing any retainable pointers within it.
|
|
|
|
This is defined to be equivalent to storing the operand into a stack
|
|
allocation and using 'destroy_addr' to destroy the object there.
|
|
|
|
For trivial types, this is a no-op. For reference types, this is
|
|
equivalent to a ``strong_release``. For ``@unowned`` types, this is
|
|
equivalent to an ``unowned_release``. In each of these cases, those
|
|
are the preferred forms.
|
|
|
|
For aggregate types, especially enums, it is typically both easier
|
|
and more efficient to reason about aggregate destroys than it is to
|
|
reason about destroys of the subobjects.
|
|
|
|
release_value_addr
|
|
``````````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'release_value_addr' sil-operand
|
|
|
|
release_value_addr %0 : $*A
|
|
|
|
Destroys a loadable value inside given address,
|
|
by releasing any retainable pointers within it.
|
|
|
|
unmanaged_release_value
|
|
```````````````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'unmanaged_release_value' sil-value
|
|
|
|
unmanaged_release_value %0 : $A
|
|
|
|
This instruction has the same local semantics as ``release_value`` but:
|
|
|
|
* Is valid in ownership qualified SIL.
|
|
* Is not intended to be statically paired at compile time by the compiler.
|
|
|
|
The intention is that this instruction is used to implement unmanaged
|
|
constructs.
|
|
|
|
destroy_value
|
|
`````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'destroy_value' sil-operand
|
|
|
|
destroy_value %0 : $A
|
|
|
|
Destroys a loadable value, by releasing any retainable pointers within it.
|
|
|
|
This is defined to be equivalent to storing the operand into a stack
|
|
allocation and using 'destroy_addr' to destroy the object there.
|
|
|
|
For trivial types, this is a no-op. For reference types, this is
|
|
equivalent to a ``strong_release``. For ``@unowned`` types, this is
|
|
equivalent to an ``unowned_release``. In each of these cases, those
|
|
are the preferred forms.
|
|
|
|
For aggregate types, especially enums, it is typically both easier
|
|
and more efficient to reason about aggregate destroys than it is to
|
|
reason about destroys of the subobjects.
|
|
|
|
autorelease_value
|
|
`````````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'autorelease_value' sil-operand
|
|
|
|
autorelease_value %0 : $A
|
|
|
|
*TODO* Complete this section.
|
|
|
|
tuple
|
|
`````
|
|
::
|
|
|
|
sil-instruction ::= 'tuple' sil-tuple-elements
|
|
sil-tuple-elements ::= '(' (sil-operand (',' sil-operand)*)? ')'
|
|
sil-tuple-elements ::= sil-type '(' (sil-value (',' sil-value)*)? ')'
|
|
|
|
%1 = tuple (%a : $A, %b : $B, ...)
|
|
// $A, $B, etc. must be loadable non-address types
|
|
// %1 will be of the "simple" tuple type $(A, B, ...)
|
|
|
|
%1 = tuple $(a:A, b:B, ...) (%a, %b, ...)
|
|
// (a:A, b:B, ...) must be a loadable tuple type
|
|
// %1 will be of the type $(a:A, b:B, ...)
|
|
|
|
Creates a loadable tuple value by aggregating multiple loadable values.
|
|
|
|
If the destination type is a "simple" tuple type, that is, it has no keyword
|
|
argument labels or variadic arguments, then the first notation can be used,
|
|
which interleaves the element values and types. If keyword names or variadic
|
|
fields are specified, then the second notation must be used, which spells out
|
|
the tuple type before the fields.
|
|
|
|
tuple_extract
|
|
`````````````
|
|
::
|
|
|
|
sil-instruction ::= 'tuple_extract' sil-operand ',' int-literal
|
|
|
|
%1 = tuple_extract %0 : $(T...), 123
|
|
// %0 must be of a loadable tuple type $(T...)
|
|
// %1 will be of the type of the selected element of %0
|
|
|
|
Extracts an element from a loadable tuple value.
|
|
|
|
tuple_element_addr
|
|
``````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'tuple_element_addr' sil-operand ',' int-literal
|
|
|
|
%1 = tuple_element_addr %0 : $*(T...), 123
|
|
// %0 must of a $*(T...) address-of-tuple type
|
|
// %1 will be of address type $*U where U is the type of the 123rd
|
|
// element of T
|
|
|
|
Given the address of a tuple in memory, derives the
|
|
address of an element within that value.
|
|
|
|
destructure_tuple
|
|
`````````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'destructure_tuple' sil-operand
|
|
|
|
(%elt1, ..., %eltn) = destructure_tuple %0 : $(Elt1Ty, ..., EltNTy)
|
|
// %0 must be a tuple of type $(Elt1Ty, ..., EltNTy)
|
|
// %eltN must have the type $EltNTy
|
|
|
|
Given a tuple value, split the value into its constituent elements.
|
|
|
|
struct
|
|
``````
|
|
::
|
|
|
|
sil-instruction ::= 'struct' sil-type '(' (sil-operand (',' sil-operand)*)? ')'
|
|
|
|
%1 = struct $S (%a : $A, %b : $B, ...)
|
|
// $S must be a loadable struct type
|
|
// $A, $B, ... must be the types of the physical 'var' fields of $S in order
|
|
// %1 will be of type $S
|
|
|
|
Creates a value of a loadable struct type by aggregating multiple loadable
|
|
values.
|
|
|
|
struct_extract
|
|
``````````````
|
|
::
|
|
|
|
sil-instruction ::= 'struct_extract' sil-operand ',' sil-decl-ref
|
|
|
|
%1 = struct_extract %0 : $S, #S.field
|
|
// %0 must be of a loadable struct type $S
|
|
// #S.field must be a physical 'var' field of $S
|
|
// %1 will be of the type of the selected field of %0
|
|
|
|
Extracts a physical field from a loadable struct value.
|
|
|
|
struct_element_addr
|
|
```````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'struct_element_addr' sil-operand ',' sil-decl-ref
|
|
|
|
%1 = struct_element_addr %0 : $*S, #S.field
|
|
// %0 must be of a struct type $S
|
|
// #S.field must be a physical 'var' field of $S
|
|
// %1 will be the address of the selected field of %0
|
|
|
|
Given the address of a struct value in memory, derives the address of a
|
|
physical field within the value.
|
|
|
|
destructure_struct
|
|
``````````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'destructure_struct' sil-operand
|
|
|
|
(%elt1, ..., %eltn) = destructure_struct %0 : $S
|
|
// %0 must be a struct of type $S
|
|
// %eltN must have the same type as the Nth field of $S
|
|
|
|
Given a struct, split the struct into its constituent fields.
|
|
|
|
object
|
|
``````
|
|
::
|
|
|
|
sil-instruction ::= 'object' sil-type '(' (sil-operand (',' sil-operand)*)? ')'
|
|
|
|
object $T (%a : $A, %b : $B, ...)
|
|
// $T must be a non-generic or bound generic reference type
|
|
// The first operands must match the stored properties of T
|
|
// Optionally there may be more elements, which are tail-allocated to T
|
|
|
|
Constructs a statically initialized object. This instruction can only appear
|
|
as final instruction in a global variable static initializer list.
|
|
|
|
ref_element_addr
|
|
````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'ref_element_addr' sil-operand ',' sil-decl-ref
|
|
|
|
%1 = ref_element_addr %0 : $C, #C.field
|
|
// %0 must be a value of class type $C
|
|
// #C.field must be a non-static physical field of $C
|
|
// %1 will be of type $*U where U is the type of the selected field
|
|
// of C
|
|
|
|
Given an instance of a class, derives the address of a physical instance
|
|
variable inside the instance. It is undefined behavior if the class value
|
|
is null.
|
|
|
|
ref_tail_addr
|
|
`````````````
|
|
::
|
|
|
|
sil-instruction ::= 'ref_tail_addr' sil-operand ',' sil-type
|
|
|
|
%1 = ref_tail_addr %0 : $C, $E
|
|
// %0 must be a value of class type $C with tail-allocated elements $E
|
|
// %1 will be of type $*E
|
|
|
|
Given an instance of a class, which is created with tail-allocated array(s),
|
|
derives the address of the first element of the first tail-allocated array.
|
|
This instruction is used to project the first tail-allocated element from an
|
|
object which is created by an ``alloc_ref`` with ``tail_elems``.
|
|
It is undefined behavior if the class instance does not have tail-allocated
|
|
arrays or if the element-types do not match.
|
|
|
|
Enums
|
|
~~~~~
|
|
|
|
These instructions construct values of enum type. Loadable enum values are
|
|
created with the `enum`_ instruction. Address-only enums require two-step
|
|
initialization. First, if the case requires data, that data is stored into
|
|
the enum at the address projected by `init_enum_data_addr`_. This step is
|
|
skipped for cases without data. Finally, the tag for
|
|
the enum is injected with an `inject_enum_addr`_ instruction::
|
|
|
|
enum AddressOnlyEnum {
|
|
case HasData(AddressOnlyType)
|
|
case NoData
|
|
}
|
|
|
|
sil @init_with_data : $(AddressOnlyType) -> AddressOnlyEnum {
|
|
entry(%0 : $*AddressOnlyEnum, %1 : $*AddressOnlyType):
|
|
// Store the data argument for the case.
|
|
%2 = init_enum_data_addr %0 : $*AddressOnlyEnum, #AddressOnlyEnum.HasData!enumelt.1
|
|
copy_addr [take] %2 to [initialization] %1 : $*AddressOnlyType
|
|
// Inject the tag.
|
|
inject_enum_addr %0 : $*AddressOnlyEnum, #AddressOnlyEnum.HasData!enumelt.1
|
|
return
|
|
}
|
|
|
|
sil @init_without_data : $() -> AddressOnlyEnum {
|
|
// No data. We only need to inject the tag.
|
|
inject_enum_addr %0 : $*AddressOnlyEnum, #AddressOnlyEnum.NoData!enumelt
|
|
return
|
|
}
|
|
|
|
Accessing the value of a loadable enum is inseparable from dispatching on its
|
|
discriminator and is done with the `switch_enum`_ terminator::
|
|
|
|
enum Foo { case A(Int), B(String) }
|
|
|
|
sil @switch_foo : $(Foo) -> () {
|
|
entry(%foo : $Foo):
|
|
switch_enum %foo : $Foo, case #Foo.A!enumelt.1: a_dest, case #Foo.B!enumelt.1: b_dest
|
|
|
|
a_dest(%a : $Int):
|
|
/* use %a */
|
|
|
|
b_dest(%b : $String):
|
|
/* use %b */
|
|
}
|
|
|
|
An address-only enum can be tested by branching on it using the
|
|
`switch_enum_addr`_ terminator. Its value can then be taken by destructively
|
|
projecting the enum value with `unchecked_take_enum_data_addr`_::
|
|
|
|
enum Foo<T> { case A(T), B(String) }
|
|
|
|
sil @switch_foo : $<T> (Foo<T>) -> () {
|
|
entry(%foo : $*Foo<T>):
|
|
switch_enum_addr %foo : $*Foo<T>, case #Foo.A!enumelt.1: a_dest, \
|
|
case #Foo.B!enumelt.1: b_dest
|
|
|
|
a_dest:
|
|
%a = unchecked_take_enum_data_addr %foo : $*Foo<T>, #Foo.A!enumelt.1
|
|
/* use %a */
|
|
|
|
b_dest:
|
|
%b = unchecked_take_enum_data_addr %foo : $*Foo<T>, #Foo.B!enumelt.1
|
|
/* use %b */
|
|
}
|
|
|
|
enum
|
|
````
|
|
::
|
|
|
|
sil-instruction ::= 'enum' sil-type ',' sil-decl-ref (',' sil-operand)?
|
|
|
|
%1 = enum $U, #U.EmptyCase!enumelt
|
|
%1 = enum $U, #U.DataCase!enumelt.1, %0 : $T
|
|
// $U must be an enum type
|
|
// #U.DataCase or #U.EmptyCase must be a case of enum $U
|
|
// If #U.Case has a data type $T, %0 must be a value of type $T
|
|
// If #U.Case has no data type, the operand must be omitted
|
|
// %1 will be of type $U
|
|
|
|
Creates a loadable enum value in the given ``case``. If the ``case`` has a
|
|
data type, the enum value will contain the operand value.
|
|
|
|
unchecked_enum_data
|
|
```````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'unchecked_enum_data' sil-operand ',' sil-decl-ref
|
|
|
|
%1 = unchecked_enum_data %0 : $U, #U.DataCase!enumelt.1
|
|
// $U must be an enum type
|
|
// #U.DataCase must be a case of enum $U with data
|
|
// %1 will be of object type $T for the data type of case U.DataCase
|
|
|
|
Unsafely extracts the payload data for an enum ``case`` from an enum value.
|
|
It is undefined behavior if the enum does not contain a value of the given
|
|
case.
|
|
|
|
init_enum_data_addr
|
|
```````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'init_enum_data_addr' sil-operand ',' sil-decl-ref
|
|
|
|
%1 = init_enum_data_addr %0 : $*U, #U.DataCase!enumelt.1
|
|
// $U must be an enum type
|
|
// #U.DataCase must be a case of enum $U with data
|
|
// %1 will be of address type $*T for the data type of case U.DataCase
|
|
|
|
Projects the address of the data for an enum ``case`` inside an enum. This
|
|
does not modify the enum or check its value. It is intended to be used as
|
|
part of the initialization sequence for an address-only enum. Storing to
|
|
the ``init_enum_data_addr`` for a case followed by ``inject_enum_addr`` with that
|
|
same case is guaranteed to result in a fully-initialized enum value of that
|
|
case being stored. Loading from the ``init_enum_data_addr`` of an initialized
|
|
enum value or injecting a mismatched case tag is undefined behavior.
|
|
|
|
The address is invalidated as soon as the operand enum is fully initialized by
|
|
an ``inject_enum_addr``.
|
|
|
|
inject_enum_addr
|
|
````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'inject_enum_addr' sil-operand ',' sil-decl-ref
|
|
|
|
inject_enum_addr %0 : $*U, #U.Case!enumelt
|
|
// $U must be an enum type
|
|
// #U.Case must be a case of enum $U
|
|
// %0 will be overlaid with the tag for #U.Case
|
|
|
|
Initializes the enum value referenced by the given address by overlaying the
|
|
tag for the given case. If the case has no data, this instruction is sufficient
|
|
to initialize the enum value. If the case has data, the data must be stored
|
|
into the enum at the ``init_enum_data_addr`` address for the case *before*
|
|
``inject_enum_addr`` is applied. It is undefined behavior if
|
|
``inject_enum_addr`` is applied for a case with data to an uninitialized enum,
|
|
or if ``inject_enum_addr`` is applied for a case with data when data for a
|
|
mismatched case has been stored to the enum.
|
|
|
|
unchecked_take_enum_data_addr
|
|
`````````````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'unchecked_take_enum_data_addr' sil-operand ',' sil-decl-ref
|
|
|
|
%1 = unchecked_take_enum_data_addr %0 : $*U, #U.DataCase!enumelt.1
|
|
// $U must be an enum type
|
|
// #U.DataCase must be a case of enum $U with data
|
|
// %1 will be of address type $*T for the data type of case U.DataCase
|
|
|
|
Invalidates an enum value, and takes the address of the payload for the given
|
|
enum ``case`` in-place in memory. The referenced enum value is no longer valid,
|
|
but the payload value referenced by the result address is valid and must be
|
|
destroyed. It is undefined behavior if the referenced enum does not contain a
|
|
value of the given ``case``. The result shares memory with the original enum
|
|
value; the enum memory cannot be reinitialized as an enum until the payload has
|
|
also been invalidated.
|
|
|
|
(1.0 only)
|
|
|
|
For the first payloaded case of an enum, ``unchecked_take_enum_data_addr``
|
|
is guaranteed to have no side effects; the enum value will not be invalidated.
|
|
|
|
select_enum
|
|
```````````
|
|
::
|
|
|
|
sil-instruction ::= 'select_enum' sil-operand sil-select-case*
|
|
(',' 'default' sil-value)?
|
|
':' sil-type
|
|
|
|
%n = select_enum %0 : $U, \
|
|
case #U.Case1!enumelt: %1, \
|
|
case #U.Case2!enumelt: %2, /* ... */ \
|
|
default %3 : $T
|
|
|
|
// $U must be an enum type
|
|
// #U.Case1, Case2, etc. must be cases of enum $U
|
|
// %1, %2, %3, etc. must have type $T
|
|
// %n has type $T
|
|
|
|
Selects one of the "case" or "default" operands based on the case of an
|
|
enum value. This is equivalent to a trivial `switch_enum`_ branch sequence::
|
|
|
|
entry:
|
|
switch_enum %0 : $U, \
|
|
case #U.Case1!enumelt: bb1, \
|
|
case #U.Case2!enumelt: bb2, /* ... */ \
|
|
default bb_default
|
|
bb1:
|
|
br cont(%1 : $T) // value for #U.Case1
|
|
bb2:
|
|
br cont(%2 : $T) // value for #U.Case2
|
|
bb_default:
|
|
br cont(%3 : $T) // value for default
|
|
cont(%n : $T):
|
|
// use argument %n
|
|
|
|
but turns the control flow dependency into a data flow dependency.
|
|
For address-only enums, `select_enum_addr`_ offers the same functionality for
|
|
an indirectly referenced enum value in memory.
|
|
|
|
select_enum_addr
|
|
````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'select_enum_addr' sil-operand sil-select-case*
|
|
(',' 'default' sil-value)?
|
|
':' sil-type
|
|
|
|
%n = select_enum_addr %0 : $*U, \
|
|
case #U.Case1!enumelt: %1, \
|
|
case #U.Case2!enumelt: %2, /* ... */ \
|
|
default %3 : $T
|
|
|
|
// %0 must be the address of an enum type $*U
|
|
// #U.Case1, Case2, etc. must be cases of enum $U
|
|
// %1, %2, %3, etc. must have type $T
|
|
// %n has type $T
|
|
|
|
Selects one of the "case" or "default" operands based on the case of the
|
|
referenced enum value. This is the address-only counterpart to
|
|
`select_enum`_.
|
|
|
|
Protocol and Protocol Composition Types
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
These instructions create and manipulate values of protocol and protocol
|
|
composition type. From SIL's perspective, protocol and protocol composition
|
|
types consist of an *existential container*, which is a generic container for
|
|
a value of unknown runtime type, referred to as an "existential type" in type
|
|
theory. The existential container consists of a reference to the
|
|
*witness table(s)* for the protocol(s) referred to by the protocol type and a
|
|
reference to the underlying *concrete value*, which may be either stored
|
|
in-line inside the existential container for small values or allocated
|
|
separately into a buffer owned and managed by the existential container for
|
|
larger values.
|
|
|
|
Depending on the constraints applied to an existential type, an existential
|
|
container may use one of several representations:
|
|
|
|
- **Opaque existential containers**: If none of the protocols in a protocol
|
|
type are class protocols, then the existential container for that type is
|
|
address-only and referred to in the implementation as an *opaque existential
|
|
container*. The value semantics of the existential container propagate to the
|
|
contained concrete value. Applying ``copy_addr`` to an opaque existential
|
|
container copies the contained concrete value, deallocating or reallocating
|
|
the destination container's owned buffer if necessary. Applying
|
|
``destroy_addr`` to an opaque existential container destroys the concrete
|
|
value and deallocates any buffers owned by the existential container. The
|
|
following instructions manipulate opaque existential containers:
|
|
|
|
* `init_existential_addr`_
|
|
* `open_existential_addr`_
|
|
* `deinit_existential_addr`_
|
|
|
|
- **Opaque existential containers loadable types**: In the SIL Opaque Values
|
|
mode of operation, we take an opaque value as-is.
|
|
Said value might be replaced with one of the _addr instructions above
|
|
before IR generation.
|
|
The following instructions manipulate "loadable" opaque existential containers:
|
|
|
|
* `init_existential_value`_
|
|
* `open_existential_value`_
|
|
* `deinit_existential_value`_
|
|
|
|
- **Class existential containers**: If a protocol type is constrained by one or
|
|
more class protocols, then the existential container for that type is
|
|
loadable and referred to in the implementation as a *class existential
|
|
container*. Class existential containers have reference semantics and can be
|
|
``retain``-ed and ``release``-d. The following instructions manipulate class
|
|
existential containers:
|
|
|
|
* `init_existential_ref`_
|
|
* `open_existential_ref`_
|
|
|
|
- **Metatype existential containers**: Existential metatypes use a
|
|
container consisting of the type metadata for the conforming type along with
|
|
the protocol conformances. Metatype existential containers are trivial types.
|
|
The following instructions manipulate metatype existential containers:
|
|
|
|
* `init_existential_metatype`_
|
|
* `open_existential_metatype`_
|
|
|
|
- **Boxed existential containers**: The standard library ``Error`` protocol
|
|
uses a size-optimized reference-counted container, which indirectly stores
|
|
the conforming value. Boxed existential containers can be ``retain``-ed
|
|
and ``release``-d. The following instructions manipulate boxed existential
|
|
containers:
|
|
|
|
* `alloc_existential_box`_
|
|
* `project_existential_box`_
|
|
* `open_existential_box`_
|
|
* `open_existential_box_value`_
|
|
* `dealloc_existential_box`_
|
|
|
|
Some existential types may additionally support specialized representations
|
|
when they contain certain known concrete types. For example, when Objective-C
|
|
interop is available, the ``Error`` protocol existential supports
|
|
a class existential container representation for ``NSError`` objects, so it
|
|
can be initialized from one using ``init_existential_ref`` instead of the
|
|
more expensive ``alloc_existential_box``::
|
|
|
|
bb(%nserror: $NSError):
|
|
// The slow general way to form an Error, allocating a box and
|
|
// storing to its value buffer:
|
|
%error1 = alloc_existential_box $Error, $NSError
|
|
%addr = project_existential_box $NSError in %error1 : $Error
|
|
strong_retain %nserror: $NSError
|
|
store %nserror to %addr : $NSError
|
|
|
|
// The fast path supported for NSError:
|
|
strong_retain %nserror: $NSError
|
|
%error2 = init_existential_ref %nserror: $NSError, $Error
|
|
|
|
init_existential_addr
|
|
`````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'init_existential_addr' sil-operand ',' sil-type
|
|
|
|
%1 = init_existential_addr %0 : $*P, $T
|
|
// %0 must be of a $*P address type for non-class protocol or protocol
|
|
// composition type P
|
|
// $T must be an AST type that fulfills protocol(s) P
|
|
// %1 will be of type $*T', where T' is the maximally abstract lowering
|
|
// of type T
|
|
|
|
Partially initializes the memory referenced by ``%0`` with an existential
|
|
container prepared to contain a value of type ``$T``. The result of the
|
|
instruction is an address referencing the storage for the contained value, which
|
|
remains uninitialized. The contained value must be ``store``-d or
|
|
``copy_addr``-ed to in order for the existential value to be fully initialized.
|
|
If the existential container needs to be destroyed while the contained value
|
|
is uninitialized, ``deinit_existential_addr`` must be used to do so. A fully
|
|
initialized existential container can be destroyed with ``destroy_addr`` as
|
|
usual. It is undefined behavior to ``destroy_addr`` a partially-initialized
|
|
existential container.
|
|
|
|
init_existential_value
|
|
``````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'init_existential_value' sil-operand ':' sil-type ','
|
|
sil-type
|
|
|
|
%1 = init_existential_value %0 : $L' : $C, $P
|
|
// %0 must be of loadable type $L', lowered from AST type $L, conforming to
|
|
// protocol(s) $P
|
|
// %1 will be of type $P
|
|
|
|
Loadable version of the above: Inits-up the existential
|
|
container prepared to contain a value of type ``$P``.
|
|
|
|
deinit_existential_addr
|
|
```````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'deinit_existential_addr' sil-operand
|
|
|
|
deinit_existential_addr %0 : $*P
|
|
// %0 must be of a $*P address type for non-class protocol or protocol
|
|
// composition type P
|
|
|
|
Undoes the partial initialization performed by
|
|
``init_existential_addr``. ``deinit_existential_addr`` is only valid for
|
|
existential containers that have been partially initialized by
|
|
``init_existential_addr`` but haven't had their contained value initialized.
|
|
A fully initialized existential must be destroyed with ``destroy_addr``.
|
|
|
|
deinit_existential_value
|
|
````````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'deinit_existential_value' sil-operand
|
|
|
|
deinit_existential_value %0 : $P
|
|
// %0 must be of a $P opaque type for non-class protocol or protocol
|
|
// composition type P
|
|
|
|
Undoes the partial initialization performed by
|
|
``init_existential_value``. ``deinit_existential_value`` is only valid for
|
|
existential containers that have been partially initialized by
|
|
``init_existential_value`` but haven't had their contained value initialized.
|
|
A fully initialized existential must be destroyed with ``destroy_value``.
|
|
|
|
open_existential_addr
|
|
`````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'open_existential_addr' sil-allowed-access sil-operand 'to' sil-type
|
|
sil-allowed-access ::= 'immutable_access'
|
|
sil-allowed-access ::= 'mutable_access'
|
|
|
|
%1 = open_existential_addr immutable_access %0 : $*P to $*@opened P
|
|
// %0 must be of a $*P type for non-class protocol or protocol composition
|
|
// type P
|
|
// $*@opened P must be a unique archetype that refers to an opened
|
|
// existential type P.
|
|
// %1 will be of type $*P
|
|
|
|
Obtains the address of the concrete value inside the existential
|
|
container referenced by ``%0``. The protocol conformances associated
|
|
with this existential container are associated directly with the
|
|
archetype ``$*@opened P``. This pointer can be used with any operation
|
|
on archetypes, such as ``witness_method`` assuming this operation obeys the
|
|
access constraint: The returned address can either allow ``mutable_access`` or
|
|
``immutable_access``. Users of the returned address may only consume
|
|
(e.g ``destroy_addr`` or ``copy_addr [take]``) or mutate the value at the
|
|
address if they have ``mutable_access``.
|
|
|
|
open_existential_value
|
|
``````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'open_existential_value' sil-operand 'to' sil-type
|
|
|
|
%1 = open_existential_value %0 : $P to $@opened P
|
|
// %0 must be of a $P type for non-class protocol or protocol composition
|
|
// type P
|
|
// $@opened P must be a unique archetype that refers to an opened
|
|
// existential type P.
|
|
// %1 will be of type $P
|
|
|
|
Loadable version of the above: Opens-up the existential
|
|
container associated with ``%0``. The protocol conformances associated
|
|
with this existential container are associated directly with the
|
|
archetype ``$@opened P``.
|
|
|
|
init_existential_ref
|
|
````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'init_existential_ref' sil-operand ':' sil-type ','
|
|
sil-type
|
|
|
|
%1 = init_existential_ref %0 : $C' : $C, $P
|
|
// %0 must be of class type $C', lowered from AST type $C, conforming to
|
|
// protocol(s) $P
|
|
// $P must be a class protocol or protocol composition type
|
|
// %1 will be of type $P
|
|
|
|
Creates a class existential container of type ``$P`` containing a reference to
|
|
the class instance ``%0``.
|
|
|
|
open_existential_ref
|
|
````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'open_existential_ref' sil-operand 'to' sil-type
|
|
|
|
%1 = open_existential_ref %0 : $P to $@opened P
|
|
// %0 must be of a $P type for a class protocol or protocol composition
|
|
// $@opened P must be a unique archetype that refers to an opened
|
|
// existential type P
|
|
// %1 will be of type $@opened P
|
|
|
|
Extracts the class instance reference from a class existential
|
|
container. The protocol conformances associated with this existential
|
|
container are associated directly with the archetype ``@opened P``. This
|
|
pointer can be used with any operation on archetypes, such as
|
|
``witness_method``. When the operand is of metatype type, the result
|
|
will be the metatype of the opened archetype.
|
|
|
|
init_existential_metatype
|
|
`````````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'init_existential_metatype' sil-operand ',' sil-type
|
|
|
|
%1 = init_existential_metatype $0 : $@<rep> T.Type, $@<rep> P.Type
|
|
// %0 must be of a metatype type $@<rep> T.Type where T: P
|
|
// %@<rep> P.Type must be the existential metatype of a protocol or protocol
|
|
// composition, with the same metatype representation <rep>
|
|
// %1 will be of type $@<rep> P.Type
|
|
|
|
Creates a metatype existential container of type ``$P.Type`` containing the
|
|
conforming metatype of ``$T``.
|
|
|
|
open_existential_metatype
|
|
`````````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'open_existential_metatype' sil-operand 'to' sil-type
|
|
|
|
%1 = open_existential_metatype %0 : $@<rep> P.Type to $@<rep> (@opened P).Type
|
|
// %0 must be of a $P.Type existential metatype for a protocol or protocol
|
|
// composition
|
|
// $@<rep> (@opened P).Type must be the metatype of a unique archetype that
|
|
// refers to an opened existential type P, with the same metatype
|
|
// representation <rep>
|
|
// %1 will be of type $@<rep> (@opened P).Type
|
|
|
|
Extracts the metatype from an existential metatype. The protocol conformances associated with this existential
|
|
container are associated directly with the archetype ``@opened P``.
|
|
|
|
alloc_existential_box
|
|
`````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'alloc_existential_box' sil-type ',' sil-type
|
|
|
|
%1 = alloc_existential_box $P, $T
|
|
// $P must be a protocol or protocol composition type with boxed
|
|
// representation
|
|
// $T must be an AST type that conforms to P
|
|
// %1 will be of type $P
|
|
// %1#1 will be of type $*T', where T' is the most abstracted lowering of T
|
|
|
|
Allocates a boxed existential container of type ``$P`` with space to hold a
|
|
value of type ``$T'``. The box is not fully initialized until a valid value
|
|
has been stored into the box. If the box must be deallocated before it is
|
|
fully initialized, ``dealloc_existential_box`` must be used. A fully
|
|
initialized box can be ``retain``-ed and ``release``-d like any
|
|
reference-counted type. The ``project_existential_box`` instruction is used
|
|
to retrieve the address of the value inside the container.
|
|
|
|
project_existential_box
|
|
```````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'project_existential_box' sil-type 'in' sil-operand
|
|
|
|
%1 = project_existential_box $T in %0 : $P
|
|
// %0 must be a value of boxed protocol or protocol composition type $P
|
|
// $T must be the most abstracted lowering of the AST type for which the box
|
|
// was allocated
|
|
// %1 will be of type $*T
|
|
|
|
Projects the address of the value inside a boxed existential container.
|
|
The address is dependent on the lifetime of the owner reference ``%0``.
|
|
It is undefined behavior if the concrete type ``$T`` is not the same type for
|
|
which the box was allocated with ``alloc_existential_box``.
|
|
|
|
open_existential_box
|
|
````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'open_existential_box' sil-operand 'to' sil-type
|
|
|
|
%1 = open_existential_box %0 : $P to $*@opened P
|
|
// %0 must be a value of boxed protocol or protocol composition type $P
|
|
// %@opened P must be the address type of a unique archetype that refers to
|
|
/// an opened existential type P
|
|
// %1 will be of type $*@opened P
|
|
|
|
Projects the address of the value inside a boxed existential container, and
|
|
uses the enclosed type and protocol conformance metadata to bind the
|
|
opened archetype ``$@opened P``. The result address is dependent on both
|
|
the owning box and the enclosing function; in order to "open" a boxed
|
|
existential that has directly adopted a class reference, temporary scratch
|
|
space may need to have been allocated.
|
|
|
|
open_existential_box_value
|
|
``````````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'open_existential_box_value' sil-operand 'to' sil-type
|
|
|
|
%1 = open_existential_box_value %0 : $P to $@opened P
|
|
// %0 must be a value of boxed protocol or protocol composition type $P
|
|
// %@opened P must be a unique archetype that refers to an opened
|
|
// existential type P
|
|
// %1 will be of type $@opened P
|
|
|
|
Projects the value inside a boxed existential container, and uses the enclosed
|
|
type and protocol conformance metadata to bind the opened archetype ``$@opened
|
|
P``.
|
|
|
|
dealloc_existential_box
|
|
```````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'dealloc_existential_box' sil-operand, sil-type
|
|
|
|
dealloc_existential_box %0 : $P, $T
|
|
// %0 must be an uninitialized box of boxed existential container type $P
|
|
// $T must be the AST type for which the box was allocated
|
|
|
|
Deallocates a boxed existential container. The value inside the existential
|
|
buffer is not destroyed; either the box must be uninitialized, or the value
|
|
must have been projected out and destroyed beforehand. It is undefined behavior
|
|
if the concrete type ``$T`` is not the same type for which the box was
|
|
allocated with ``alloc_existential_box``.
|
|
|
|
Blocks
|
|
~~~~~~
|
|
|
|
project_block_storage
|
|
`````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'project_block_storage' sil-operand ':' sil-type
|
|
|
|
init_block_storage_header
|
|
`````````````````````````
|
|
|
|
*TODO* Fill this in. The printing of this instruction looks incomplete on trunk currently.
|
|
|
|
|
|
Unchecked Conversions
|
|
~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
These instructions implement type conversions which are not checked. These are
|
|
either user-level conversions that are always safe and do not need to be
|
|
checked, or implementation detail conversions that are unchecked for
|
|
performance or flexibility.
|
|
|
|
upcast
|
|
``````
|
|
::
|
|
|
|
sil-instruction ::= 'upcast' sil-operand 'to' sil-type
|
|
|
|
%1 = upcast %0 : $D to $B
|
|
// $D and $B must be class types or metatypes, with B a superclass of D
|
|
// %1 will have type $B
|
|
|
|
Represents a conversion from a derived class instance or metatype to a
|
|
superclass, or from a base-class-constrained archetype to its base class.
|
|
|
|
address_to_pointer
|
|
``````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'address_to_pointer' sil-operand 'to' sil-type
|
|
|
|
%1 = address_to_pointer %0 : $*T to $Builtin.RawPointer
|
|
// %0 must be of an address type $*T
|
|
// %1 will be of type Builtin.RawPointer
|
|
|
|
Creates a ``Builtin.RawPointer`` value corresponding to the address ``%0``.
|
|
Converting the result pointer back to an address of the same type will give
|
|
an address equivalent to ``%0``. It is undefined behavior to cast the
|
|
``RawPointer`` to any address type other than its original address type or
|
|
any `layout compatible types`_.
|
|
|
|
pointer_to_address
|
|
``````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'pointer_to_address' sil-operand 'to' ('[' 'strict' ']')? sil-type
|
|
|
|
%1 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*T
|
|
// %1 will be of type $*T
|
|
|
|
Creates an address value corresponding to the ``Builtin.RawPointer`` value
|
|
``%0``. Converting a ``RawPointer`` back to an address of the same type as
|
|
its originating ``address_to_pointer`` instruction gives back an equivalent
|
|
address. It is undefined behavior to cast the ``RawPointer`` back to any type
|
|
other than its original address type or `layout compatible types`_. It is
|
|
also undefined behavior to cast a ``RawPointer`` from a heap object to any
|
|
address type.
|
|
|
|
The ``strict`` flag indicates whether the returned address adheres to
|
|
strict aliasing. If true, then the type of each memory access
|
|
dependent on this address must be consistent with the memory's bound
|
|
type. A memory access from an address that is not strict cannot have
|
|
its address substituted with a strict address, even if other nearby
|
|
memory accesses at the same location are strict.
|
|
|
|
|
|
unchecked_ref_cast
|
|
``````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'unchecked_ref_cast' sil-operand 'to' sil-type
|
|
|
|
%1 = unchecked_ref_cast %0 : $A to $B
|
|
// %0 must be an object of type $A
|
|
// $A must be a type with retainable pointer representation
|
|
// %1 will be of type $B
|
|
// $B must be a type with retainable pointer representation
|
|
|
|
Converts a heap object reference to another heap object reference
|
|
type. This conversion is unchecked, and it is undefined behavior if
|
|
the destination type is not a valid type for the heap object. The heap
|
|
object reference on either side of the cast may be a class
|
|
existential, and may be wrapped in one level of Optional.
|
|
|
|
unchecked_ref_cast_addr
|
|
```````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'unchecked_ref_cast_addr'
|
|
sil-type 'in' sil-operand 'to'
|
|
sil-type 'in' sil-operand
|
|
|
|
unchecked_ref_cast_addr $A in %0 : $*A to $B in %1 : $*B
|
|
// %0 must be the address of an object of type $A
|
|
// $A must be a type with retainable pointer representation
|
|
// %1 must be the address of storage for an object of type $B
|
|
// $B must be a retainable pointer representation
|
|
|
|
Loads a heap object reference from an address and stores it at the
|
|
address of another uninitialized heap object reference. The loaded
|
|
reference is always taken, and the stored reference is
|
|
initialized. This conversion is unchecked, and it is undefined
|
|
behavior if the destination type is not a valid type for the heap
|
|
object. The heap object reference on either side of the cast may be a
|
|
class existential, and may be wrapped in one level of Optional.
|
|
|
|
unchecked_addr_cast
|
|
```````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'unchecked_addr_cast' sil-operand 'to' sil-type
|
|
|
|
%1 = unchecked_addr_cast %0 : $*A to $*B
|
|
// %0 must be an address
|
|
// %1 will be of type $*B
|
|
|
|
Converts an address to a different address type. Using the resulting
|
|
address is undefined unless ``B`` is layout compatible with ``A``. The
|
|
layout of ``B`` may be smaller than that of ``A`` as long as the lower
|
|
order bytes have identical layout.
|
|
|
|
unchecked_trivial_bit_cast
|
|
``````````````````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'unchecked_trivial_bit_cast' sil-operand 'to' sil-type
|
|
|
|
%1 = unchecked_trivial_bit_cast %0 : $Builtin.NativeObject to $Builtin.Word
|
|
// %0 must be an object.
|
|
// %1 must be an object with trivial type.
|
|
|
|
Bitcasts an object of type ``A`` to be of same sized or smaller type
|
|
``B`` with the constraint that ``B`` must be trivial. This can be used
|
|
for bitcasting among trivial types, but more importantly is a one way
|
|
bitcast from non-trivial types to trivial types.
|
|
|
|
unchecked_bitwise_cast
|
|
``````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'unchecked_bitwise_cast' sil-operand 'to' sil-type
|
|
|
|
%1 = unchecked_bitwise_cast %0 : $A to $B
|
|
|
|
Bitwise copies an object of type ``A`` into a new object of type ``B``
|
|
of the same size or smaller.
|
|
|
|
ref_to_raw_pointer
|
|
``````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'ref_to_raw_pointer' sil-operand 'to' sil-type
|
|
|
|
%1 = ref_to_raw_pointer %0 : $C to $Builtin.RawPointer
|
|
// $C must be a class type, or Builtin.NativeObject, or AnyObject
|
|
// %1 will be of type $Builtin.RawPointer
|
|
|
|
Converts a heap object reference to a ``Builtin.RawPointer``. The ``RawPointer``
|
|
result can be cast back to the originating class type but does not have
|
|
ownership semantics. It is undefined behavior to cast a ``RawPointer`` from a
|
|
heap object reference to an address using ``pointer_to_address``.
|
|
|
|
raw_pointer_to_ref
|
|
``````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'raw_pointer_to_ref' sil-operand 'to' sil-type
|
|
|
|
%1 = raw_pointer_to_ref %0 : $Builtin.RawPointer to $C
|
|
// $C must be a class type, or Builtin.NativeObject, or AnyObject
|
|
// %1 will be of type $C
|
|
|
|
Converts a ``Builtin.RawPointer`` back to a heap object reference. Casting
|
|
a heap object reference to ``Builtin.RawPointer`` back to the same type gives
|
|
an equivalent heap object reference (though the raw pointer has no ownership
|
|
semantics for the object on its own). It is undefined behavior to cast a
|
|
``RawPointer`` to a type unrelated to the dynamic type of the heap object.
|
|
It is also undefined behavior to cast a ``RawPointer`` from an address to any
|
|
heap object type.
|
|
|
|
ref_to_unowned
|
|
``````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'ref_to_unowned' sil-operand
|
|
|
|
%1 = unowned_to_ref %0 : T
|
|
// $T must be a reference type
|
|
// %1 will have type $@unowned T
|
|
|
|
Adds the ``@unowned`` qualifier to the type of a reference to a heap
|
|
object. No runtime effect.
|
|
|
|
unowned_to_ref
|
|
``````````````
|
|
|
|
::
|
|
|
|
sil-instruction ::= 'unowned_to_ref' sil-operand
|
|
|
|
%1 = unowned_to_ref %0 : $@unowned T
|
|
// $T must be a reference type
|
|
// %1 will have type $T
|
|
|
|
Strips the ``@unowned`` qualifier off the type of a reference to a
|
|
heap object. No runtime effect.
|
|
|
|
ref_to_unmanaged
|
|
````````````````
|
|
|
|
TODO
|
|
|
|
unmanaged_to_ref
|
|
````````````````
|
|
|
|
TODO
|
|
|
|
|
|
convert_function
|
|
````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'convert_function' sil-operand 'to' sil-type
|
|
|
|
%1 = convert_function %0 : $T -> U to $T' -> U'
|
|
// %0 must be of a function type $T -> U ABI-compatible with $T' -> U'
|
|
// (see below)
|
|
// %1 will be of type $T' -> U'
|
|
|
|
Performs a conversion of the function ``%0`` to type ``T``, which must be ABI-
|
|
compatible with the type of ``%0``. Function types are ABI-compatible if their
|
|
input and result types are tuple types that, after destructuring, differ only
|
|
in the following ways:
|
|
|
|
- Corresponding tuple elements may add, remove, or change keyword names.
|
|
``(a:Int, b:Float, UnicodeScalar) -> ()`` and ``(x:Int, Float, z:UnicodeScalar) -> ()`` are
|
|
ABI compatible.
|
|
|
|
- A class tuple element of the destination type may be a superclass or
|
|
subclass of the source type's corresponding tuple element.
|
|
|
|
The function types may also differ in attributes, except that the
|
|
``convention`` attribute cannot be changed and the ``@noescape`` attribute must
|
|
not change for functions with context.
|
|
|
|
A ``convert_function`` cannot be used to change a thick type's ``@noescape``
|
|
attribute (``@noescape`` function types with context are not ABI compatible with
|
|
escaping function types with context) -- however, thin function types with and
|
|
without ``@noescape`` are ABI compatible because they have no context. To
|
|
convert from an escaping to a ``@noescape`` thick function type use
|
|
``convert_escape_to_noescape``.
|
|
|
|
convert_escape_to_noescape
|
|
```````````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'convert_escape_to_noescape' sil-operand 'to' sil-type
|
|
%1 = convert_escape_to_noescape %0 : $T -> U to $@noescape T' -> U'
|
|
// %0 must be of a function type $T -> U ABI-compatible with $T' -> U'
|
|
// (see convert_function)
|
|
// %1 will be of the trivial type $@noescape T -> U
|
|
|
|
Converts an escaping (non-trivial) function type to an ``@noescape`` trivial
|
|
function type. Something must guarantee the lifetime of the input ``%0`` for the
|
|
duration of the use ``%1``.
|
|
|
|
A ``convert_escape_to_noescape [not_guaranteed] %opd`` indicates that the
|
|
lifetime of its operand was not guaranteed by SILGen and a mandatory pass must
|
|
be run to ensure the lifetime of ``%opd``` for the conversion's uses.
|
|
|
|
A ``convert_escape_to_noescape [escaped]`` indiciates that the result was
|
|
passed to a function (materializeForSet) which escapes the closure in a way not
|
|
expressed by the convert's users. The mandatory pass must ensure the lifetime
|
|
in a conservative way.
|
|
|
|
thin_function_to_pointer
|
|
````````````````````````
|
|
|
|
TODO
|
|
|
|
pointer_to_thin_function
|
|
````````````````````````
|
|
|
|
TODO
|
|
|
|
classify_bridge_object
|
|
``````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'classify_bridge_object' sil-operand
|
|
|
|
%1 = classify_bridge_object %0 : $Builtin.BridgeObject
|
|
// %1 will be of type (Builtin.Int1, Builtin.Int1)
|
|
|
|
Decodes the bit representation of the specified ``Builtin.BridgeObject`` value,
|
|
returning two bits: the first indicates whether the object is an Objective-C
|
|
object, the second indicates whether it is an Objective-C tagged pointer value.
|
|
|
|
ref_to_bridge_object
|
|
````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'ref_to_bridge_object' sil-operand, sil-operand
|
|
|
|
%2 = ref_to_bridge_object %0 : $C, %1 : $Builtin.Word
|
|
// %1 must be of reference type $C
|
|
// %2 will be of type Builtin.BridgeObject
|
|
|
|
Creates a ``Builtin.BridgeObject`` that references ``%0``, with spare bits
|
|
in the pointer representation populated by bitwise-OR-ing in the value of
|
|
``%1``. It is undefined behavior if this bitwise OR operation affects the
|
|
reference identity of ``%0``; in other words, after the following instruction
|
|
sequence::
|
|
|
|
%b = ref_to_bridge_object %r : $C, %w : $Builtin.Word
|
|
%r2 = bridge_object_to_ref %b : $Builtin.BridgeObject to $C
|
|
|
|
``%r`` and ``%r2`` must be equivalent. In particular, it is assumed that
|
|
retaining or releasing the ``BridgeObject`` is equivalent to retaining or
|
|
releasing the original reference, and that the above ``ref_to_bridge_object``
|
|
/ ``bridge_object_to_ref`` round-trip can be folded away to a no-op.
|
|
|
|
On platforms with ObjC interop, there is additionally a platform-specific
|
|
bit in the pointer representation of a ``BridgeObject`` that is reserved to
|
|
indicate whether the referenced object has native Swift refcounting. It is
|
|
undefined behavior to set this bit when the first operand references an
|
|
Objective-C object.
|
|
|
|
bridge_object_to_ref
|
|
````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'bridge_object_to_ref' sil-operand 'to' sil-type
|
|
|
|
%1 = bridge_object_to_ref %0 : $Builtin.BridgeObject to $C
|
|
// $C must be a reference type
|
|
// %1 will be of type $C
|
|
|
|
Extracts the object reference from a ``Builtin.BridgeObject``, masking out any
|
|
spare bits.
|
|
|
|
bridge_object_to_word
|
|
`````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'bridge_object_to_word' sil-operand 'to' sil-type
|
|
|
|
%1 = bridge_object_to_word %0 : $Builtin.BridgeObject to $Builtin.Word
|
|
// %1 will be of type $Builtin.Word
|
|
|
|
Provides the bit pattern of a ``Builtin.BridgeObject`` as an integer.
|
|
|
|
thin_to_thick_function
|
|
``````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'thin_to_thick_function' sil-operand 'to' sil-type
|
|
|
|
%1 = thin_to_thick_function %0 : $@convention(thin) T -> U to $T -> U
|
|
// %0 must be of a thin function type $@convention(thin) T -> U
|
|
// The destination type must be the corresponding thick function type
|
|
// %1 will be of type $T -> U
|
|
|
|
Converts a thin function value, that is, a bare function pointer with no
|
|
context information, into a thick function value with ignored context.
|
|
Applying the resulting thick function value is equivalent to applying the
|
|
original thin value. The ``thin_to_thick_function`` conversion may be
|
|
eliminated if the context is proven not to be needed.
|
|
|
|
thick_to_objc_metatype
|
|
``````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'thick_to_objc_metatype' sil-operand 'to' sil-type
|
|
|
|
%1 = thick_to_objc_metatype %0 : $@thick T.Type to $@objc_metatype T.Type
|
|
// %0 must be of a thick metatype type $@thick T.Type
|
|
// The destination type must be the corresponding Objective-C metatype type
|
|
// %1 will be of type $@objc_metatype T.Type
|
|
|
|
Converts a thick metatype to an Objective-C class metatype. ``T`` must
|
|
be of class, class protocol, or class protocol composition type.
|
|
|
|
objc_to_thick_metatype
|
|
``````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'objc_to_thick_metatype' sil-operand 'to' sil-type
|
|
|
|
%1 = objc_to_thick_metatype %0 : $@objc_metatype T.Type to $@thick T.Type
|
|
// %0 must be of an Objective-C metatype type $@objc_metatype T.Type
|
|
// The destination type must be the corresponding thick metatype type
|
|
// %1 will be of type $@thick T.Type
|
|
|
|
Converts an Objective-C class metatype to a thick metatype. ``T`` must
|
|
be of class, class protocol, or class protocol composition type.
|
|
|
|
objc_metatype_to_object
|
|
```````````````````````
|
|
|
|
TODO
|
|
|
|
objc_existential_metatype_to_object
|
|
```````````````````````````````````
|
|
|
|
TODO
|
|
|
|
Checked Conversions
|
|
~~~~~~~~~~~~~~~~~~~
|
|
|
|
Some user-level cast operations can fail and thus require runtime checking.
|
|
|
|
The `unconditional_checked_cast_addr`_, `unconditional_checked_cast_value`_ and `unconditional_checked_cast`_
|
|
instructions performs an unconditional checked cast; it is a runtime failure
|
|
if the cast fails. The `checked_cast_addr_br`_, `checked_cast_value_br`_ and `checked_cast_br`_
|
|
terminator instruction performs a conditional checked cast; it branches to one
|
|
of two destinations based on whether the cast succeeds or not.
|
|
|
|
unconditional_checked_cast
|
|
``````````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'unconditional_checked_cast' sil-operand 'to' sil-type
|
|
|
|
%1 = unconditional_checked_cast %0 : $A to $B
|
|
%1 = unconditional_checked_cast %0 : $*A to $*B
|
|
// $A and $B must be both objects or both addresses
|
|
// %1 will be of type $B or $*B
|
|
|
|
Performs a checked scalar conversion, causing a runtime failure if the
|
|
conversion fails. Casts that require changing representation or ownership are
|
|
unsupported.
|
|
|
|
unconditional_checked_cast_addr
|
|
```````````````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'unconditional_checked_cast_addr'
|
|
sil-type 'in' sil-operand 'to'
|
|
sil-type 'in' sil-operand
|
|
|
|
unconditional_checked_cast_addr $A in %0 : $*@thick A to $B in $*@thick B
|
|
// $A and $B must be both addresses
|
|
// %1 will be of type $*B
|
|
// $A is destroyed during the conversion. There is no implicit copy.
|
|
|
|
Performs a checked indirect conversion, causing a runtime failure if the
|
|
conversion fails.
|
|
|
|
unconditional_checked_cast_value
|
|
````````````````````````````````
|
|
::
|
|
|
|
sil-instruction ::= 'unconditional_checked_cast_value'
|
|
sil-operand 'to' sil-type
|
|
|
|
%1 = unconditional_checked_cast_value %0 : $A to $B
|
|
// $A must not be an address
|
|
// $B must not be an address
|
|
// %1 will be of type $B
|
|
// $A is destroyed during the conversion. There is no implicit copy.
|
|
|
|
Performs a checked conversion, causing a runtime failure if the conversion
|
|
fails. Unlike `unconditional_checked_cast`, this destroys its operand and
|
|
creates a new value. Consequently, this supports bridging objects to values, as
|
|
well as casting to a different ownership classification such as `$AnyObject` to
|
|
`$T.Type`.
|
|
|
|
Runtime Failures
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
cond_fail
|
|
`````````
|
|
::
|
|
|
|
sil-instruction ::= 'cond_fail' sil-operand
|
|
|
|
cond_fail %0 : $Builtin.Int1
|
|
// %0 must be of type $Builtin.Int1
|
|
|
|
This instruction produces a `runtime failure`_ if the operand is one.
|
|
Execution proceeds normally if the operand is zero.
|
|
|
|
Terminators
|
|
~~~~~~~~~~~
|
|
|
|
These instructions terminate a basic block. Every basic block must end
|
|
with a terminator. Terminators may only appear as the final instruction of
|
|
a basic block.
|
|
|
|
unreachable
|
|
```````````
|
|
::
|
|
|
|
sil-terminator ::= 'unreachable'
|
|
|
|
unreachable
|
|
|
|
Indicates that control flow must not reach the end of the current basic block.
|
|
It is a dataflow error if an unreachable terminator is reachable from the entry
|
|
point of a function and is not immediately preceded by an ``apply`` of a
|
|
no-return function.
|
|
|
|
return
|
|
``````
|
|
::
|
|
|
|
sil-terminator ::= 'return' sil-operand
|
|
|
|
return %0 : $T
|
|
// $T must be the return type of the current function
|
|
|
|
Exits the current function and returns control to the calling function. If
|
|
the current function was invoked with an ``apply`` instruction, the result
|
|
of that function will be the operand of this ``return`` instruction. If
|
|
the current function was invoked with a ``try_apply` instruction, control
|
|
resumes at the normal destination, and the value of the basic block argument
|
|
will be the operand of this ``return`` instruction.
|
|
|
|
If the current function is a ``yield_once`` coroutine, there must not be
|
|
a path from the entry block to a ``return`` which does not pass through
|
|
a ``yield`` instruction. This rule does not apply in the ``raw`` SIL stage.
|
|
|
|
``return`` does not retain or release its operand or any other values.
|
|
|
|
A function must not contain more than one ``return`` instruction.
|
|
|
|
throw
|
|
`````
|
|
::
|
|
|
|
sil-terminator ::= 'throw' sil-operand
|
|
|
|
throw %0 : $T
|
|
// $T must be the error result type of the current function
|
|
|
|
Exits the current function and returns control to the calling
|
|
function. The current function must have an error result, and so the
|
|
function must have been invoked with a ``try_apply`` instruction.
|
|
Control will resume in the error destination of that instruction, and
|
|
the basic block argument will be the operand of the ``throw``.
|
|
|
|
``throw`` does not retain or release its operand or any other values.
|
|
|
|
A function must not contain more than one ``throw`` instruction.
|
|
|
|
yield
|
|
`````
|
|
::
|
|
|
|
sil-terminator ::= 'yield' sil-yield-values
|
|
',' 'resume' sil-identifier
|
|
',' 'unwind' sil-identifier
|
|
sil-yield-values ::= sil-operand
|
|
sil-yield-values ::= '(' (sil-operand (',' sil-operand)*)? ')'
|
|
|
|
Temporarily suspends the current function and provides the given
|
|
values to the calling function. The current function must be a coroutine,
|
|
and the yield values must match the yield types of the coroutine.
|
|
If the calling function resumes the coroutine normally, control passes to
|
|
the ``resume`` destination. If the calling function aborts the coroutine,
|
|
control passes to the ``unwind`` destination.
|
|
|
|
The ``resume`` and ``unwind`` destination blocks must be uniquely
|
|
referenced by the ``yield`` instruction. This prevents them from becoming
|
|
critical edges.
|
|
|
|
In a ``yield_once`` coroutine, there must not be a control flow path leading
|
|
from the ``resume`` edge to another ``yield`` instruction in this function.
|
|
This rule does not apply in the ``raw`` SIL stage.
|
|
|
|
There must not be a control flow path leading from the ``unwind`` edge to
|
|
a ``return`` instruction, to a ``throw`` instruction, or to any block
|
|
reachable from the entry block via a path that does not pass through
|
|
an ``unwind`` edge. That is, the blocks reachable from ``unwind`` edges
|
|
must jointly form a disjoint subfunction of the coroutine.
|
|
|
|
unwind
|
|
``````
|
|
::
|
|
|
|
sil-terminator ::= 'unwind'
|
|
|
|
Exits the current function and returns control to the calling function,
|
|
completing an unwind from a ``yield``. The current function must be a
|
|
coroutine.
|
|
|
|
``unwind`` is only permitted in blocks reachable from the ``unwind`` edges
|
|
of ``yield`` instructions.
|
|
|
|
br
|
|
``
|
|
::
|
|
|
|
sil-terminator ::= 'br' sil-identifier
|
|
'(' (sil-operand (',' sil-operand)*)? ')'
|
|
|
|
br label (%0 : $A, %1 : $B, ...)
|
|
// `label` must refer to a basic block label within the current function
|
|
// %0, %1, etc. must be of the types of `label`'s arguments
|
|
|
|
Unconditionally transfers control from the current basic block to the block
|
|
labeled ``label``, binding the given values to the arguments of the destination
|
|
basic block.
|
|
|
|
cond_br
|
|
``````````
|
|
::
|
|
|
|
sil-terminator ::= 'cond_br' sil-operand ','
|
|
sil-identifier '(' (sil-operand (',' sil-operand)*)? ')' ','
|
|
sil-identifier '(' (sil-operand (',' sil-operand)*)? ')'
|
|
|
|
cond_br %0 : $Builtin.Int1, true_label (%a : $A, %b : $B, ...), \
|
|
false_label (%x : $X, %y : $Y, ...)
|
|
// %0 must be of $Builtin.Int1 type
|
|
// `true_label` and `false_label` must refer to block labels within the
|
|
// current function and must not be identical
|
|
// %a, %b, etc. must be of the types of `true_label`'s arguments
|
|
// %x, %y, etc. must be of the types of `false_label`'s arguments
|
|
|
|
Conditionally branches to ``true_label`` if ``%0`` is equal to ``1`` or to
|
|
``false_label`` if ``%0`` is equal to ``0``, binding the corresponding set of
|
|
values to the arguments of the chosen destination block.
|
|
|
|
switch_value
|
|
````````````
|
|
::
|
|
|
|
sil-terminator ::= 'switch_value' sil-operand
|
|
(',' sil-switch-value-case)*
|
|
(',' sil-switch-default)?
|
|
sil-switch-value-case ::= 'case' sil-value ':' sil-identifier
|
|
sil-switch-default ::= 'default' sil-identifier
|
|
|
|
switch_value %0 : $Builtin.Int<n>, case %1: label1, \
|
|
case %2: label2, \
|
|
..., \
|
|
default labelN
|
|
|
|
// %0 must be a value of builtin integer type $Builtin.Int<n>
|
|
// `label1` through `labelN` must refer to block labels within the current
|
|
// function
|
|
// FIXME: All destination labels currently must take no arguments
|
|
|
|
Conditionally branches to one of several destination basic blocks based on a
|
|
value of builtin integer or function type. If the operand value matches one of the ``case``
|
|
values of the instruction, control is transferred to the corresponding basic
|
|
block. If there is a ``default`` basic block, control is transferred to it if
|
|
the value does not match any of the ``case`` values. It is undefined behavior
|
|
if the value does not match any cases and no ``default`` branch is provided.
|
|
|
|
select_value
|
|
````````````
|
|
::
|
|
|
|
sil-instruction ::= 'select_value' sil-operand sil-select-value-case*
|
|
(',' 'default' sil-value)?
|
|
':' sil-type
|
|
sil-select-value-case ::= 'case' sil-value ':' sil-value
|
|
|
|
|
|
%n = select_value %0 : $U, \
|
|
case %c1: %r1, \
|
|
case %c2: %r2, /* ... */ \
|
|
default %r3 : $T
|
|
|
|
// $U must be a builtin type. Only integers types are supported currently.
|
|
// c1, c2, etc must be of type $U
|
|
// %r1, %r2, %r3, etc. must have type $T
|
|
// %n has type $T
|
|
|
|
Selects one of the "case" or "default" operands based on the case of a
|
|
value. This is equivalent to a trivial `switch_value`_ branch sequence::
|
|
|
|
entry:
|
|
switch_value %0 : $U, \
|
|
case %c1: bb1, \
|
|
case %c2: bb2, /* ... */ \
|
|
default bb_default
|
|
bb1:
|
|
br cont(%r1 : $T) // value for %c1
|
|
bb2:
|
|
br cont(%r2 : $T) // value for %c2
|
|
bb_default:
|
|
br cont(%r3 : $T) // value for default
|
|
cont(%n : $T):
|
|
// use argument %n
|
|
|
|
but turns the control flow dependency into a data flow dependency.
|
|
|
|
switch_enum
|
|
```````````
|
|
::
|
|
|
|
sil-terminator ::= 'switch_enum' sil-operand
|
|
(',' sil-switch-enum-case)*
|
|
(',' sil-switch-default)?
|
|
sil-switch-enum-case ::= 'case' sil-decl-ref ':' sil-identifier
|
|
|
|
switch_enum %0 : $U, case #U.Foo!enumelt: label1, \
|
|
case #U.Bar!enumelt: label2, \
|
|
..., \
|
|
default labelN
|
|
|
|
// %0 must be a value of enum type $U
|
|
// #U.Foo, #U.Bar, etc. must be 'case' declarations inside $U
|
|
// `label1` through `labelN` must refer to block labels within the current
|
|
// function
|
|
// label1 must take either no basic block arguments, or a single argument
|
|
// of the type of #U.Foo's data
|
|
// label2 must take either no basic block arguments, or a single argument
|
|
// of the type of #U.Bar's data, etc.
|
|
// labelN must take no basic block arguments
|
|
|
|
Conditionally branches to one of several destination basic blocks based on the
|
|
discriminator in a loadable ``enum`` value. Unlike ``switch_int``,
|
|
``switch_enum`` requires coverage of the operand type: If the ``enum`` type
|
|
is resilient, the ``default`` branch is required; if the ``enum`` type is
|
|
fragile, the ``default`` branch is required unless a destination is assigned to
|
|
every ``case`` of the ``enum``. The destination basic block for a ``case`` may
|
|
take an argument of the corresponding ``enum`` ``case``'s data type (or of the
|
|
address type, if the operand is an address). If the branch is taken, the
|
|
destination's argument will be bound to the associated data inside the
|
|
original enum value. For example::
|
|
|
|
enum Foo {
|
|
case Nothing
|
|
case OneInt(Int)
|
|
case TwoInts(Int, Int)
|
|
}
|
|
|
|
sil @sum_of_foo : $Foo -> Int {
|
|
entry(%x : $Foo):
|
|
switch_enum %x : $Foo, \
|
|
case #Foo.Nothing!enumelt: nothing, \
|
|
case #Foo.OneInt!enumelt.1: one_int, \
|
|
case #Foo.TwoInts!enumelt.1: two_ints
|
|
|
|
nothing:
|
|
%zero = integer_literal $Int, 0
|
|
return %zero : $Int
|
|
|
|
one_int(%y : $Int):
|
|
return %y : $Int
|
|
|
|
two_ints(%ab : $(Int, Int)):
|
|
%a = tuple_extract %ab : $(Int, Int), 0
|
|
%b = tuple_extract %ab : $(Int, Int), 1
|
|
%add = function_ref @add : $(Int, Int) -> Int
|
|
%result = apply %add(%a, %b) : $(Int, Int) -> Int
|
|
return %result : $Int
|
|
}
|
|
|
|
On a path dominated by a destination block of ``switch_enum``, copying or
|
|
destroying the basic block argument has equivalent reference counting semantics
|
|
to copying or destroying the ``switch_enum`` operand::
|
|
|
|
// This retain_value...
|
|
retain_value %e1 : $Enum
|
|
switch_enum %e1, case #Enum.A: a, case #Enum.B: b
|
|
a(%a : $A):
|
|
// ...is balanced by this release_value
|
|
release_value %a
|
|
b(%b : $B):
|
|
// ...and this one
|
|
release_value %b
|
|
|
|
switch_enum_addr
|
|
````````````````
|
|
::
|
|
|
|
sil-terminator ::= 'switch_enum_addr' sil-operand
|
|
(',' sil-switch-enum-case)*
|
|
(',' sil-switch-default)?
|
|
|
|
switch_enum_addr %0 : $*U, case #U.Foo!enumelt: label1, \
|
|
case #U.Bar!enumelt: label2, \
|
|
..., \
|
|
default labelN
|
|
|
|
// %0 must be the address of an enum type $*U
|
|
// #U.Foo, #U.Bar, etc. must be cases of $U
|
|
// `label1` through `labelN` must refer to block labels within the current
|
|
// function
|
|
// The destinations must take no basic block arguments
|
|
|
|
Conditionally branches to one of several destination basic blocks based on
|
|
the discriminator in the enum value referenced by the address operand.
|
|
|
|
Unlike ``switch_int``, ``switch_enum`` requires coverage of the operand type:
|
|
If the ``enum`` type is resilient, the ``default`` branch is required; if the
|
|
``enum`` type is fragile, the ``default`` branch is required unless a
|
|
destination is assigned to every ``case`` of the ``enum``.
|
|
Unlike ``switch_enum``, the payload value is not passed to the destination
|
|
basic blocks; it must be projected out separately with `unchecked_take_enum_data_addr`_.
|
|
|
|
dynamic_method_br
|
|
`````````````````
|
|
::
|
|
|
|
sil-terminator ::= 'dynamic_method_br' sil-operand ',' sil-decl-ref
|
|
',' sil-identifier ',' sil-identifier
|
|
|
|
dynamic_method_br %0 : $P, #X.method!1, bb1, bb2
|
|
// %0 must be of protocol type
|
|
// #X.method!1 must be a reference to an @objc method of any class
|
|
// or protocol type
|
|
|
|
Looks up the implementation of an Objective-C method with the same
|
|
selector as the named method for the dynamic type of the value inside
|
|
an existential container. The "self" operand of the result function
|
|
value is represented using an opaque type, the value for which must be
|
|
projected out as a value of type ``Builtin.ObjCPointer``.
|
|
|
|
If the operand is determined to have the named method, this
|
|
instruction branches to ``bb1``, passing it the uncurried function
|
|
corresponding to the method found. If the operand does not have the
|
|
named method, this instruction branches to ``bb2``.
|
|
|
|
checked_cast_br
|
|
```````````````
|
|
::
|
|
|
|
sil-terminator ::= 'checked_cast_br' sil-checked-cast-exact?
|
|
sil-operand 'to' sil-type ','
|
|
sil-identifier ',' sil-identifier
|
|
sil-checked-cast-exact ::= '[' 'exact' ']'
|
|
|
|
checked_cast_br %0 : $A to $B, bb1, bb2
|
|
checked_cast_br %0 : $*A to $*B, bb1, bb2
|
|
checked_cast_br [exact] %0 : $A to $A, bb1, bb2
|
|
// $A and $B must be both object types or both address types
|
|
// bb1 must take a single argument of type $B or $*B
|
|
// bb2 must take no arguments
|
|
|
|
Performs a checked scalar conversion from ``$A`` to ``$B``. If the conversion
|
|
succeeds, control is transferred to ``bb1``, and the result of the cast is
|
|
passed into ``bb1`` as an argument. If the conversion fails, control is
|
|
transferred to ``bb2``.
|
|
|
|
An exact cast checks whether the dynamic type is exactly the target
|
|
type, not any possible subtype of it. The source and target types
|
|
must be class types.
|
|
|
|
checked_cast_value_br
|
|
`````````````````````
|
|
::
|
|
|
|
sil-terminator ::= 'checked_cast_value_br'
|
|
sil-operand 'to' sil-type ','
|
|
sil-identifier ',' sil-identifier
|
|
sil-checked-cast-exact ::= '[' 'exact' ']'
|
|
|
|
checked_cast_value_br %0 : $A to $B, bb1, bb2
|
|
// $A must be not be an address
|
|
// $B must be an opaque value
|
|
// bb1 must take a single argument of type $B
|
|
// bb2 must take no arguments
|
|
|
|
Performs a checked opaque conversion from ``$A`` to ``$B``. If the conversion
|
|
succeeds, control is transferred to ``bb1``, and the result of the cast is
|
|
passed into ``bb1`` as an argument. If the conversion fails, control is
|
|
transferred to ``bb2``.
|
|
|
|
checked_cast_addr_br
|
|
````````````````````
|
|
::
|
|
|
|
sil-terminator ::= 'checked_cast_addr_br'
|
|
sil-cast-consumption-kind
|
|
sil-type 'in' sil-operand 'to'
|
|
sil-stype 'in' sil-operand ','
|
|
sil-identifier ',' sil-identifier
|
|
sil-cast-consumption-kind ::= 'take_always'
|
|
sil-cast-consumption-kind ::= 'take_on_success'
|
|
sil-cast-consumption-kind ::= 'copy_on_success'
|
|
|
|
checked_cast_addr_br take_always $A in %0 : $*@thick A to $B in %2 : $*@thick B, bb1, bb2
|
|
// $A and $B must be both address types
|
|
// bb1 must take a single argument of type $*B
|
|
// bb2 must take no arguments
|
|
|
|
Performs a checked indirect conversion from ``$A`` to ``$B``. If the
|
|
conversion succeeds, control is transferred to ``bb1``, and the result of the
|
|
cast is left in the destination. If the conversion fails, control is
|
|
transferred to ``bb2``.
|
|
|
|
try_apply
|
|
`````````
|
|
::
|
|
|
|
sil-terminator ::= 'try_apply' sil-value
|
|
sil-apply-substitution-list?
|
|
'(' (sil-value (',' sil-value)*)? ')'
|
|
':' sil-type
|
|
'normal' sil-identifier, 'error' sil-identifier
|
|
|
|
try_apply %0(%1, %2, ...) : $(A, B, ...) -> (R, @error E),
|
|
normal bb1, error bb2
|
|
bb1(%3 : R):
|
|
bb2(%4 : E):
|
|
|
|
// Note that the type of the callee '%0' is specified *after* the arguments
|
|
// %0 must be of a concrete function type $(A, B, ...) -> (R, @error E)
|
|
// %1, %2, etc. must be of the argument types $A, $B, etc.
|
|
|
|
Transfers control to the function specified by ``%0``, passing it the
|
|
given arguments. When ``%0`` returns, control resumes in either the
|
|
normal destination (if it returns with ``return``) or the error
|
|
destination (if it returns with ``throw``).
|
|
|
|
``%0`` must have a function type with an error result.
|
|
|
|
The rules on generic substitutions are identical to those of ``apply``.
|
|
|
|
Assertion configuration
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
To be able to support disabling assertions at compile time there is a builtin
|
|
``assertion_configuration`` function. A call to this function can be replaced at
|
|
compile time by a constant or can stay opaque.
|
|
|
|
All calls to the ``assert_configuration`` function are replaced by the constant
|
|
propagation pass to the appropriate constant depending on compile time settings.
|
|
Subsequent passes remove dependent unwanted control flow. Using this mechanism
|
|
we support conditionally enabling/disabling of code in SIL libraries depending
|
|
on the assertion configuration selected when the library is linked into user
|
|
code.
|
|
|
|
There are three assertion configurations: Debug (0), Release (1) and
|
|
DisableReplacement (-1).
|
|
|
|
The optimization flag or a special assert configuration flag determines the
|
|
value. Depending on the configuration value assertions in the standard library
|
|
will be executed or not.
|
|
|
|
The standard library uses this builtin to define an assert that can be
|
|
disabled at compile time.
|
|
|
|
::
|
|
|
|
func assert(...) {
|
|
if (Int32(Builtin.assert_configuration()) == 0) {
|
|
_fatal_error_message(message, ...)
|
|
}
|
|
}
|
|
|
|
The ``assert_configuration`` function application is serialized when we build
|
|
the standard library (we recognize the ``-parse-stdlib`` option and don't do the
|
|
constant replacement but leave the function application to be serialized to
|
|
sil).
|
|
|
|
The compiler flag that influences the value of the ``assert_configuration``
|
|
function application is the optimization flag: at ``-Onone` the application will
|
|
be replaced by ``Debug`` at higher optimization levels the instruction will be
|
|
replaced by ``Release``. Optionally, the value to use for replacement can be
|
|
specified with the ``-assert-config`` flag which overwrites the value selected by
|
|
the optimization flag (possible values are ``Debug``, ``Release``,
|
|
``DisableReplacement``).
|
|
|
|
If the call to the ``assert_configuration`` function stays opaque until IRGen,
|
|
IRGen will replace the application by the constant representing Debug mode (0).
|
|
This happens we can build the standard library .dylib. The generate sil will
|
|
retain the function call but the generated .dylib will contain code with
|
|
assertions enabled.
|