<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">This seems fine to me… at a high level!<div class="">-Andy</div><div class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Oct 14, 2016, at 2:44 PM, Michael Gottesman via swift-dev &lt;<a href="mailto:swift-dev@swift.org" class="">swift-dev@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=us-ascii" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Attached below is a final version of the proposal. I am going to commit it to the repo if there are no further questions/changes/etc.<div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div class=""><a href="https://gottesmm.github.io/proposals/high-level-arc-memory-operations.html" class="">https://gottesmm.github.io/proposals/high-level-arc-memory-operations.html</a></div></div></blockquote><div class=""><br class=""></div>Michael</div><div class=""><br class=""></div><div class="">----</div><div class=""><br class=""></div><div class=""><div class=""># Summary</div><div class=""><br class=""></div><div class="">This document proposes:</div><div class=""><br class=""></div><div class="">1. adding the following ownership qualifiers to `load`: `[take]`, `[copy]`,</div><div class="">&nbsp; &nbsp;`[trivial]`.</div><div class="">2. adding the following ownership qualifiers to `store`: `[init]`, `[assign]`,</div><div class="">&nbsp; &nbsp;`[trivial]`.</div><div class="">3. adding the `load_borrow` instruction and the `end_borrow` instruction.</div><div class="">3. requiring all `load` and `store` operations to have ownership qualifiers.</div><div class="">4. banning the use of `load [trivial]`, `store [trivial]` on memory locations of</div><div class="">&nbsp; &nbsp;`non-trivial` type.</div><div class=""><br class=""></div><div class="">This will allow for:</div><div class=""><br class=""></div><div class="">1. eliminating optimizer miscompiles that occur due to releases being moved into</div><div class="">&nbsp; &nbsp;the region in between a `load`/`retain`, `load`/`release`,</div><div class="">&nbsp; &nbsp;`store`/`release`. (For a specific example, see the appendix).</div><div class="">2. explicitly modeling `load [trivial]`/`store [trivial]` as having `unsafe</div><div class="">&nbsp; &nbsp;unowned` ownership semantics. This will be enforced via the verifier.</div><div class="">3. more aggressive ARC code motion.</div><div class=""><br class=""></div><div class=""># Definitions</div><div class=""><br class=""></div><div class="">## ownership qualified load</div><div class=""><br class=""></div><div class="">We propose three different ownership qualifiers for load. Define `load [trivial]`</div><div class="">as:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %x = load [trivial] %x_ptr : $*Int</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; =&gt;</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %x = load %x_ptr : $*Int</div><div class=""><br class=""></div><div class="">A `load [trivial]` can not be used to load values of non-trivial type. Define</div><div class="">`load [copy]` as:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %x = load [copy] %x_ptr : $*C</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; =&gt;</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %x = load %x_ptr : $*C</div><div class="">&nbsp; &nbsp; retain_value %x : $C</div><div class=""><br class=""></div><div class="">Then define `load [take]` as:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %x = load [take] %x_ptr : $*Builtin.NativeObject</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; =&gt;</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %x = load %x_ptr : $*Builtin.NativeObject</div><div class=""><br class=""></div><div class="">**NOTE** `load [take]` implies that the loaded from memory location no longer</div><div class="">owns the result object (i.e. a take is a move). Loading from the memory location</div><div class="">again without reinitialization is illegal.</div><div class=""><br class=""></div><div class="">## load_borrow and end_borrow</div><div class=""><br class=""></div><div class="">Next we provide `load_borrow` and `end_borrow`:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %x = load_borrow %x_ptr : $*Builtin.NativeObject</div><div class="">&nbsp; &nbsp; ...</div><div class="">&nbsp; &nbsp; end_borrow %x, %x_ptr : $*Builtin.NativeObject</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; =&gt;</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %x = load %x_ptr : $*Builtin.NativeObject</div><div class="">&nbsp; &nbsp; ...</div><div class="">&nbsp; &nbsp; endLifetime %x : $Builtin.NativeObject</div><div class="">&nbsp; &nbsp; fixLifetime %x_ptr : $*Builtin.NativeObject</div><div class=""><br class=""></div><div class="">`load [borrow]` implies that in the region between the `load` and the</div><div class="">`end_borrow`, the loaded object must semantically remain alive. The `end_borrow`</div><div class="">communicates to the optimizer:</div><div class=""><br class=""></div><div class="">1. that the value in `%x_ptr` should not be destroyed before endBorrow.</div><div class="">2. uses of `%x` should not be sunk past endBorrow since `%x` is only a shallow</div><div class="">&nbsp; &nbsp;copy of the value in `%x_ptr` and past that point `%x_ptr` may not remain</div><div class="">&nbsp; &nbsp;alive.</div><div class=""><br class=""></div><div class="">An example of where this construct is useful is when one has a let binding to a</div><div class="">class instance `c` that contains a let field `f`. In that case `c`'s lifetime</div><div class="">guarantees `f`'s lifetime meaning that returning `f` over the function call</div><div class="">boundary is safe.</div><div class=""><br class=""></div><div class="">*NOTE* since the SILOwnershipModelEliminator will not process these</div><div class="">instructions, endLifetime is just a strawman instruction that will not be</div><div class="">implemented. In practice though, IRGen will need to create a suitable barrier to</div><div class="">ensure that LLVM does not move any uses of `%x` past the `fixLifetime`</div><div class="">instruction of `%x_ptr` once we begin creating such instructions as a result of</div><div class="">ARC optimization.</div><div class=""><br class=""></div><div class="">## ownership qualified store</div><div class=""><br class=""></div><div class="">First define a `store [trivial]` as:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; store %x to [trivial] %x_ptr : $*Int</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; =&gt;</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; store %x to %x_ptr : $*Int</div><div class=""><br class=""></div><div class="">The verifier will prevent this instruction from being used on types with</div><div class="">non-trivial ownership. Define a `store [assign]` as follows:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; store %x to [assign] %x_ptr : $*C</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; &nbsp;=&gt;</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %old_x = load %x_ptr : $*C</div><div class="">&nbsp; &nbsp; store %new_x to %x_ptr : $*C</div><div class="">&nbsp; &nbsp; release_value %old_x : $C</div><div class=""><br class=""></div><div class="">*NOTE* `store` is defined as a consuming operation. We also provide</div><div class="">`store [init]` in the case where we know statically that there is no</div><div class="">previous value in the memory location:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; store %x to [init] %x_ptr : $*C</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; &nbsp;=&gt;</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; store %new_x to %x_ptr : $*C</div><div class=""><br class=""></div><div class=""># Implementation</div><div class=""><br class=""></div><div class="">## Goals</div><div class=""><br class=""></div><div class="">Our implementation strategy goals are:</div><div class=""><br class=""></div><div class="">1. zero impact on other compiler developers until the feature is fully</div><div class="">&nbsp; &nbsp;developed. This implies all work will be done behind a flag.</div><div class="">2. separation of feature implementation from updating passes.</div><div class=""><br class=""></div><div class="">Goal 2 will be implemented via a pass that transforms ownership qualified</div><div class="">`load`/`store` instructions into unqualified `load`/`store` right after SILGen.</div><div class=""><br class=""></div><div class="">## Plan</div><div class=""><br class=""></div><div class="">We begin by adding initial infrastructure for our development. This means:</div><div class=""><br class=""></div><div class="">1. Adding to SILOptions a disabled by default flag called</div><div class="">&nbsp;"EnableSILOwnershipModel". This flag will be set by a false by default frontend</div><div class="">&nbsp;option called "-enable-sil-ownership-mode".</div><div class=""><br class=""></div><div class="">2. Bots will be brought up to test the compiler with</div><div class="">&nbsp; &nbsp;"-enable-sil-ownership-model" set to true. The specific bots are:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp;* RA-OSX+simulators</div><div class="">&nbsp; &nbsp;* RA-Device</div><div class="">&nbsp; &nbsp;* RA-Linux.</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp;The bots will run once a day until the feature is close to completion. Then a</div><div class="">&nbsp; &nbsp;polling model will be followed.</div><div class=""><br class=""></div><div class="">Now that change isolation is borrow, we develop building blocks for the</div><div class="">optimization:</div><div class=""><br class=""></div><div class="">1. Two enums will be defined: `LoadInstOwnershipQualifier`,</div><div class="">&nbsp; &nbsp;`StoreInstOwnershipQualifier`. The exact definition of these enums are as</div><div class="">&nbsp; &nbsp;follows:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; &nbsp;enum class LoadOwnershipQualifier {</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Unqualified, Take, Copy, Trivial</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp;};</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp;enum class StoreOwnershipQualifier {</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Unqualified, Init, Assign, Trivial</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp;};</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; *NOTE* `LoadOwnershipQualifier::Unqualified` and</div><div class="">&nbsp; &nbsp; `StoreOwnershipQualifier::Unqualified` are only needed for staging purposes.</div><div class=""><br class=""></div><div class="">2. Creating a `LoadInst`, `StoreInst` will be changed to require an ownership</div><div class="">qualifier. At this stage, this argument will default to `Unqualified`. "Bare"</div><div class="">`load`, `store` when parsed via textual SIL will be considered to be</div><div class="">unqualified. This implies that the rest of the compiler will not have to be</div><div class="">changed as a result of this step.</div><div class=""><br class=""></div><div class="">3. Support will be added to SIL, IRGen, Serialization, SIL Printing, and SIL</div><div class="">Parsing for the rest of the qualifiers. SILGen will not be modified at this</div><div class="">stage.</div><div class=""><br class=""></div><div class="">4. The `load_borrow` and `end_borrow` instructions will be implemented in SIL,</div><div class="">&nbsp; &nbsp;IRGen, Serialization, SIL Printing, and SIL Parsing. They will not be used</div><div class="">&nbsp; &nbsp;immediately.</div><div class=""><br class=""></div><div class="">4. A pass called the "OwnershipModelEliminator" will be implemented. It will</div><div class="">&nbsp; &nbsp;blow up all `load`, `store` instructions with non `*::Unqualified` ownership</div><div class="">&nbsp; &nbsp;into their constituant ARC operations and `*::Unqualified` `load`, `store`</div><div class="">&nbsp; &nbsp;insts. It will not process `load_borrow` and `end_borrow` since currently it</div><div class="">&nbsp; &nbsp;is not expected for SILGen to emit such instructions.</div><div class=""><br class=""></div><div class="">5. An option called "EnforceSILOwnershipMode" will be added to the verifier. If</div><div class="">the option is set, the verifier will assert if:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp;a. `load`, `store` operations with trivial ownership are applied to memory</div><div class="">&nbsp; &nbsp; &nbsp; locations with non-trivial type.</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp;b. `load`, `store` operation with unqualified ownership type are present in</div><div class="">&nbsp; &nbsp;the IR.</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp;c. `load_borrow` or `end_borrow` are present in the IR. This is because</div><div class="">&nbsp; &nbsp;currently we do not support SIL containing such instructions in SIL</div><div class="">&nbsp; &nbsp;Ownership Mode. Once we have the ability to verify borrowing scopes, this</div><div class="">&nbsp; &nbsp;will no longer be the case, but this is a different proposal.</div><div class=""><br class=""></div><div class="">Finally, we wire up the building blocks:</div><div class=""><br class=""></div><div class="">1. If SILOption.EnableSILOwnershipModel is true, then the after SILGen SIL</div><div class="">&nbsp; &nbsp;verification will be performed with EnforceSILOwnershipModel set to true.</div><div class="">2. If SILOption.EnableSILOwnershipModel is true, then the pass manager will run</div><div class="">&nbsp; &nbsp;the OwnershipModelEliminator pass right after SILGen before the normal pass</div><div class="">&nbsp; &nbsp;pipeline starts.</div><div class="">3. SILGen will be changed to emit non-unqualified ownership qualifiers on load,</div><div class="">&nbsp; &nbsp;store instructions when the EnableSILOwnershipModel flag is set. We will use</div><div class="">&nbsp; &nbsp;the verifier throwing to guarantee that we are not missing any specific</div><div class="">&nbsp; &nbsp;cases.</div><div class=""><br class=""></div><div class="">Then once all of the bots are green, we change SILOption.EnableSILOwnershipModel</div><div class="">to be true by default. After a cooling off period, we move all of the code</div><div class="">behind the SILOwnershipModel flag in front of the flag. We do this so we can</div><div class="">reuse that flag for further SILOwnershipModel changes.</div><div class=""><br class=""></div><div class="">## Optimizer Changes</div><div class=""><br class=""></div><div class="">Since the SILOwnershipModel eliminator will eliminate the ownership qualifiers</div><div class="">on load, store instructions right after ownership verification, there will be no</div><div class="">immediate effects on the optimizer and thus the optimizer changes can be done in</div><div class="">parallel with the rest of the ARC optimization work.</div><div class=""><br class=""></div><div class="">But, in the long run, we want to enforce these ownership invariants all</div><div class="">throughout the SIL pipeline implying these ownership qualified `load`, `store`</div><div class="">instructions must be processed by IRGen, not eliminated by the SILOwnershipModel</div><div class="">eliminator. Thus we will need to update passes to handle these new instructions</div><div class="">and also will need to implement the `load_borrow`, `end_borrow` instruction.</div><div class=""><br class=""></div><div class="">The main optimizer changes can be separated into the following areas: memory</div><div class="">forwarding, dead stores, ARC optimization. In all of these cases, the necessary</div><div class="">changes are relatively trivial to respond to. We give a quick taste of two of</div><div class="">them: store-&gt;load forwarding and ARC Code Motion.</div><div class=""><br class=""></div><div class="">### store-&gt;load forwarding</div><div class=""><br class=""></div><div class="">Currently we perform store-&gt;load forwarding as follows:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; store %x to %x_ptr : $C</div><div class="">&nbsp; &nbsp; ... NO SIDE EFFECTS THAT TOUCH X_PTR ...</div><div class="">&nbsp; &nbsp; %y = load %x_ptr : $C</div><div class="">&nbsp; &nbsp; use(%y)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; =&gt;</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; store %x to %x_ptr : $C</div><div class="">&nbsp; &nbsp; ... NO SIDE EFFECTS THAT TOUCH X_PTR ...</div><div class="">&nbsp; &nbsp; use(%x)</div><div class=""><br class=""></div><div class="">In a world, where we are using ownership qualified load, store, we have to also</div><div class="">consider the ownership implications. *NOTE* Since we are not modifying the</div><div class="">store, `store [assign]` and `store [init]` are treated the same. Thus without</div><div class="">any loss of generality, lets consider solely `store`.</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; store %x to [assign] %x_ptr : $C</div><div class="">&nbsp; &nbsp; ... NO SIDE EFFECTS THAT TOUCH X_PTR ...</div><div class="">&nbsp; &nbsp; %y = load [copy] %x_ptr : $C</div><div class="">&nbsp; &nbsp; use(%y)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; =&gt;</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; store %x to [assign] %x_ptr : $C</div><div class="">&nbsp; &nbsp; ... NO SIDE EFFECTS THAT TOUCH X_PTR ...</div><div class="">&nbsp; &nbsp; strong_retain %x</div><div class="">&nbsp; &nbsp; use(%x)</div><div class=""><br class=""></div><div class="">### ARC Code Motion</div><div class=""><br class=""></div><div class="">If ARC Code Motion wishes to move the ARC semantics of ownership qualified</div><div class="">`load`, `store` instructions, it must now consider read/write effects. On the</div><div class="">other hand, we can perform more aggressive ARC code motion of ownership</div><div class="">qualified loads and stores in the face of deinits. This is because we no longer</div><div class="">need to worry about our code motion causing a deinit to fire in between (without</div><div class="">any loss of generality) the load/retain.</div><div class=""><br class=""></div><div class="">### Normal Code Motion</div><div class=""><br class=""></div><div class="">Normal code motion will lose some effectiveness since many of the load/store</div><div class="">operations that it used to be able to move now must consider ARC information. We</div><div class="">may need to consider running ARC code motion earlier in the pipeline where we</div><div class="">normally run Normal Code Motion to ensure that we are able to handle these</div><div class="">cases.</div><div class=""><br class=""></div><div class="">### ARC Optimization</div><div class=""><br class=""></div><div class="">The main implication for ARC optimization is that instead of eliminating just</div><div class="">retains, releases, it must be able to recognize ownership qualified `load`,</div><div class="">`store` and set their flags as appropriate. Also in general ARC optimization and</div><div class="">memory behavior will need to recognize the `end_borrow` instruction as a code</div><div class="">motion barrier.</div><div class=""><br class=""></div><div class="">### Function Signature Optimization</div><div class=""><br class=""></div><div class="">Semantic ARC affects function signature optimization in the context of the owned</div><div class="">to borrow optimization. Specifically:</div><div class=""><br class=""></div><div class="">1. A `store [assign]` must be recognized as a release of the old value that is</div><div class="">&nbsp; &nbsp;being overridden. In such a case, we can move the `release` of the old value</div><div class="">&nbsp; &nbsp;into the caller and change the `store [assign]` into a `store [init]`.</div><div class="">2. A `load [copy]` must be recognized as a retain in the callee. Then function</div><div class="">&nbsp; &nbsp;signature optimization will transform the `load [copy]` into a</div><div class="">&nbsp; &nbsp;`load_borrow`. This would require the addition of a new `@borrow` return</div><div class="">&nbsp; &nbsp;value convention.</div><div class=""><br class=""></div><div class=""># Appendix</div><div class=""><br class=""></div><div class="">## Partial Initialization of Loadable References in SIL</div><div class=""><br class=""></div><div class="">In SIL, a value of non-trivial loadable type is loaded from a memory location as</div><div class="">follows:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %x = load %x_ptr : $*S</div><div class="">&nbsp; &nbsp; ...</div><div class="">&nbsp; &nbsp; retain_value %x_ptr : $S</div><div class=""><br class=""></div><div class="">At first glance, this looks reasonable, but in truth there is a hidden drawback:</div><div class="">the partially initialized zone in between the load and the retain</div><div class="">operation. This zone creates a period of time when an "evil optimizer" could</div><div class="">insert an instruction that causes x to be deallocated before the copy is</div><div class="">finished being initialized. Similar issues come up when trying to perform a</div><div class="">store of a non-trival value into a memory location.</div><div class=""><br class=""></div><div class="">Since this sort of partial initialization is allowed in SIL, the optimizer is</div><div class="">forced to be overly conservative when attempting to move releases passed retains</div><div class="">lest the release triggers a deinit that destroys a value like `%x`. Lets look at</div><div class="">two concrete examples that show how semantically providing ownership qualified</div><div class="">load, store instructions eliminate this problem.</div><div class=""><br class=""></div><div class="">**NOTE** Without any loss of generality, we will speak of values with reference</div><div class="">semantics instead of non-trivial values.</div><div class=""><br class=""></div><div class="">## Case Study: Partial Initialization and load [copy]</div><div class=""><br class=""></div><div class="">### The Problem</div><div class=""><br class=""></div><div class="">Consider the following swift program:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; func opaque_call()</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; final class C {</div><div class="">&nbsp; &nbsp; &nbsp; var int: Int = 0</div><div class="">&nbsp; &nbsp; &nbsp; deinit {</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; opaque_call()</div><div class="">&nbsp; &nbsp; &nbsp; }</div><div class="">&nbsp; &nbsp; }</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; final class D {</div><div class="">&nbsp; &nbsp; &nbsp; var int: Int = 0</div><div class="">&nbsp; &nbsp; }</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; var GLOBAL_C : C? = nil</div><div class="">&nbsp; &nbsp; var GLOBAL_D : D? = nil</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; func useC(_ c: C)</div><div class="">&nbsp; &nbsp; func useD(_ d: D)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; func run() {</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; let c = C()</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; GLOBAL_C = c</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; let d = D()</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; GLOBAL_D = d</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; useC(c)</div><div class="">&nbsp; &nbsp; &nbsp; &nbsp; useD(d)</div><div class="">&nbsp; &nbsp; }</div><div class=""><br class=""></div><div class="">Notice that both `C` and `D` have fixed layouts and separate class hierarchies,</div><div class="">but `C`'s deinit has a call to the function `opaque_call` which may write to</div><div class="">`GLOBAL_D` or `GLOBAL_C`. Additionally assume that both `useC` and `useD` are</div><div class="">known to the compiler to not have any affects on instances of type `D`, `C`</div><div class="">respectively and useC assigns `nil` to `GLOBAL_C`. Now consider the following</div><div class="">valid SIL lowering for `run`:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; sil_global GLOBAL_D : $D</div><div class="">&nbsp; &nbsp; sil_global GLOBAL_C : $C</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; final class C {</div><div class="">&nbsp; &nbsp; &nbsp; var x: Int</div><div class="">&nbsp; &nbsp; &nbsp; deinit</div><div class="">&nbsp; &nbsp; }</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; final class D {</div><div class="">&nbsp; &nbsp; &nbsp; var x: Int</div><div class="">&nbsp; &nbsp; }</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; sil @useC : $@convention(thin) () -&gt; ()</div><div class="">&nbsp; &nbsp; sil @useD : $@convention(thin) () -&gt; ()</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; sil @run : $@convention(thin) () -&gt; () {</div><div class="">&nbsp; &nbsp; bb0:</div><div class="">&nbsp; &nbsp; &nbsp; %c = alloc_ref $C</div><div class="">&nbsp; &nbsp; &nbsp; %global_c = global_addr @GLOBAL_C : $*C</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %c : $C</div><div class="">&nbsp; &nbsp; &nbsp; store %c to %global_c : $*C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(1)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %d = alloc_ref $D</div><div class="">&nbsp; &nbsp; &nbsp; %global_d = global_addr @GLOBAL_D : $*D</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %d : $D</div><div class="">&nbsp; &nbsp; &nbsp; store %d to %global_d : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(2)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %c2 = load %global_c : $*C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (3)</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %c2 : $C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (4)</div><div class="">&nbsp; &nbsp; &nbsp; %d2 = load %global_d : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (5)</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %d2 : $D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (6)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %useC_func = function_ref @useC : $@convention(thin) (@owned C) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useC_func(%c2) : $@convention(thin) (@owned C) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(7)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %useD_func = function_ref @useD : $@convention(thin) (@owned D) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useD_func(%d2) : $@convention(thin) (@owned D) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(8)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; strong_release %d : $D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (9)</div><div class="">&nbsp; &nbsp; &nbsp; strong_release %c : $C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (10)</div><div class="">&nbsp; &nbsp; }</div><div class=""><br class=""></div><div class="">Lets optimize this function! First we perform the following operations:</div><div class=""><br class=""></div><div class="">1. Since `(2)` is storing to an identified object that can not be `GLOBAL_C`, we</div><div class="">&nbsp; &nbsp;can store to load forward `(1)` to `(3)`.</div><div class="">2. Since a retain does not block store to load forwarding, we can forward `(2)`</div><div class="">&nbsp; &nbsp;to `(5)`. But lets for the sake of argument, assume that the optimizer keeps</div><div class="">&nbsp; &nbsp;such information as an analysis and does not perform the actual load-&gt;store</div><div class="">&nbsp; &nbsp;forwarding.</div><div class="">3. Even though we do not foward `(2)` to `(5)`, we can still move `(4)` over</div><div class="">&nbsp; &nbsp;`(6)` so that `(4)` is right before `(7)`.</div><div class=""><br class=""></div><div class="">This yields (using the ' marker to designate that a register has had load-store</div><div class="">forwarding applied to it):</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; sil @run : $@convention(thin) () -&gt; () {</div><div class="">&nbsp; &nbsp; bb0:</div><div class="">&nbsp; &nbsp; &nbsp; %c = alloc_ref $C</div><div class="">&nbsp; &nbsp; &nbsp; %global_c = global_addr @GLOBAL_C : $*C</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %c : $C</div><div class="">&nbsp; &nbsp; &nbsp; store %c to %global_c : $*C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(1)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %d = alloc_ref $D</div><div class="">&nbsp; &nbsp; &nbsp; %global_d = global_addr @GLOBAL_D : $*D</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %d : $D</div><div class="">&nbsp; &nbsp; &nbsp; store %d to %global_d : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(2)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %c : $C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(4')</div><div class="">&nbsp; &nbsp; &nbsp; %d2 = load %global_d : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (5)</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %d2 : $D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (6)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %useC_func = function_ref @useC : $@convention(thin) (@owned C) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useC_func(%c) : $@convention(thin) (@owned C) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (7')</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %useD_func = function_ref @useD : $@convention(thin) (@owned D) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useD_func(%d2) : $@convention(thin) (@owned D) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(8)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; strong_release %d : $D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (9)</div><div class="">&nbsp; &nbsp; &nbsp; strong_release %c : $C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (10)</div><div class="">&nbsp; &nbsp; }</div><div class=""><br class=""></div><div class="">Then by assumption, we know that `%useC` does not perform any releases of any</div><div class="">instances of class `D`. Thus `(6)` can be moved past `(7')` and we can then pair</div><div class="">and eliminate `(6)` and `(9)` via the rules of ARC optimization using the</div><div class="">analysis information that `%d2` and `%d` are th same due to the possibility of</div><div class="">performing store-&gt;load forwarding. After performing such transformations, `run`</div><div class="">looks as follows:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; sil @run : $@convention(thin) () -&gt; () {</div><div class="">&nbsp; &nbsp; bb0:</div><div class="">&nbsp; &nbsp; &nbsp; %c = alloc_ref $C</div><div class="">&nbsp; &nbsp; &nbsp; %global_c = global_addr @GLOBAL_C : $*C</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %c : $C</div><div class="">&nbsp; &nbsp; &nbsp; store %c to %global_c : $*C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(1)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %d = alloc_ref $D</div><div class="">&nbsp; &nbsp; &nbsp; %global_d = global_addr @GLOBAL_D : $*D</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %d : $D</div><div class="">&nbsp; &nbsp; &nbsp; store %d to %global_d : $*D</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %d2 = load %global_d : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (5)</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %c : $C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(4')</div><div class="">&nbsp; &nbsp; &nbsp; %useC_func = function_ref @useC : $@convention(thin) (@owned C) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useC_func(%c) : $@convention(thin) (@owned C) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (7')</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %useD_func = function_ref @useD : $@convention(thin) (@owned D) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useD_func(%d2) : $@convention(thin) (@owned D) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(8)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; strong_release %c : $C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (10)</div><div class="">&nbsp; &nbsp; }</div><div class=""><br class=""></div><div class="">Now by assumption, we know that `%useD_func` does not touch any instances of</div><div class="">class `C` and `%c` does not contain any ivars of type `D` and is final so none</div><div class="">can be added. At first glance, this seems to suggest that we can move `(10)`</div><div class="">before `(8')` and then pair/eliminate `(4')` and `(10)`. But is this a safe</div><div class="">optimization perform? &nbsp;Absolutely Not! Why? Remember that since `useC_func`</div><div class="">assigns `nil` to `GLOBAL_C`, after `(7')`, `%c` could have a reference count</div><div class="">of 1. &nbsp;Thus `(10)` _may_ invoke the destructor of `C`. Since this destructor</div><div class="">calls an opaque function that _could_ potentially write to `GLOBAL_D`, we may be</div><div class="">be passing `%d2`, an already deallocated object to `%useD_func`, an illegal</div><div class="">optimization!</div><div class=""><br class=""></div><div class="">Lets think a bit more about this example and consider this example at the</div><div class="">language level. Remember that while Swift's deinit are not asychronous, we do</div><div class="">not allow for user level code to create dependencies from the body of the</div><div class="">destructor into the normal control flow that has called it. This means that</div><div class="">there are two valid results of this code:</div><div class=""><br class=""></div><div class="">- Operation Sequence 1: No optimization is performed and `%d2` is passed to</div><div class="">&nbsp; `%useD_func`.</div><div class="">- Operation Sequence 2: We shorten the lifetime of `%c` before `%useD_func` and</div><div class="">&nbsp; &nbsp;a different instance of `$D` is passed into `%useD_func`.</div><div class=""><br class=""></div><div class="">The fact that 1 occurs without optimization is just as a result of an</div><div class="">implementation detail of SILGen. 2 is also a valid sequence of operations.</div><div class=""><br class=""></div><div class="">Given that:</div><div class=""><br class=""></div><div class="">1. As a principle, the optimizer does not consider such dependencies to avoid</div><div class="">&nbsp; &nbsp;being overly conservative.</div><div class="">2. We provide constructs to ensure appropriate lifetimes via the usage of</div><div class="">&nbsp; &nbsp;constructs such as fix_lifetime.</div><div class=""><br class=""></div><div class="">We need to figure out how to express our optimization such that 2</div><div class="">happens. Remember that one of the optimizations that we performed at the</div><div class="">beginning was to move `(6)` over `(7')`, i.e., transform this:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %d = alloc_ref $D</div><div class="">&nbsp; &nbsp; &nbsp; %global_d_addr = global_addr GLOBAL_D : $D</div><div class="">&nbsp; &nbsp; &nbsp; %d = load %global_d_addr : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (5)</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %d : $D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(6)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; // Call the user functions passing in the instances that we loaded from the globals.</div><div class="">&nbsp; &nbsp; &nbsp; %useC_func = function_ref @useC : $@convention(thin) (@owned C) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useC_func(%c) : $@convention(thin) (@owned C) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(7')</div><div class=""><br class=""></div><div class="">into:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %global_d_addr = global_addr GLOBAL_D : $D</div><div class="">&nbsp; &nbsp; &nbsp; %d2 = load %global_d_addr : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (5)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; // Call the user functions passing in the instances that we loaded from the globals.</div><div class="">&nbsp; &nbsp; &nbsp; %useC_func = function_ref @useC : $@convention(thin) (@owned C) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useC_func(%c) : $@convention(thin) (@owned C) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(7')</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %d2 : $D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(6)</div><div class=""><br class=""></div><div class="">This transformation in Swift corresponds to transforming:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; let d = GLOBAL_D</div><div class="">&nbsp; &nbsp; &nbsp; useC(c)</div><div class=""><br class=""></div><div class="">to:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; let d_raw = load_d_value(GLOBAL_D)</div><div class="">&nbsp; &nbsp; &nbsp; useC(c)</div><div class="">&nbsp; &nbsp; &nbsp; let d = take_ownership_of_d(d_raw)</div><div class=""><br class=""></div><div class="">This is clearly an instance where we have moved a side-effect in between the</div><div class="">loading of the data and the taking ownership of such data, that is before the</div><div class="">`let` is fully initialized. What if instead of just moving the retain, we moved</div><div class="">the entire let statement? This would then result in the following swift code:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; useC(c)</div><div class="">&nbsp; &nbsp; &nbsp; let d = GLOBAL_D</div><div class=""><br class=""></div><div class="">and would correspond to the following SIL snippet:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %global_d_addr = global_addr GLOBAL_D : $D</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; // Call the user functions passing in the instances that we loaded from the globals.</div><div class="">&nbsp; &nbsp; &nbsp; %useC_func = function_ref @useC : $@convention(thin) (@owned C) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useC_func(%c) : $@convention(thin) (@owned C) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(7')</div><div class="">&nbsp; &nbsp; &nbsp; %d2 = load %global_d_addr : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (5)</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %d2 : $D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(6)</div><div class=""><br class=""></div><div class="">Moving the load with the strong_retain to ensure that the full initialization is</div><div class="">performed even after code motion causes our SIL to look as follows:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; sil @run : $@convention(thin) () -&gt; () {</div><div class="">&nbsp; &nbsp; bb0:</div><div class="">&nbsp; &nbsp; &nbsp; %c = alloc_ref $C</div><div class="">&nbsp; &nbsp; &nbsp; %global_c = global_addr @GLOBAL_C : $*C</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %c : $C</div><div class="">&nbsp; &nbsp; &nbsp; store %c to %global_c : $*C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(1)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %d = alloc_ref $D</div><div class="">&nbsp; &nbsp; &nbsp; %global_d = global_addr @GLOBAL_D : $*D</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %d : $D</div><div class="">&nbsp; &nbsp; &nbsp; store %d to %global_d : $*D</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %c : $C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(4')</div><div class="">&nbsp; &nbsp; &nbsp; %useC_func = function_ref @useC : $@convention(thin) (@owned C) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useC_func(%c) : $@convention(thin) (@owned C) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (7')</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %d2 = load %global_d : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (5)</div><div class="">&nbsp; &nbsp; &nbsp; %useD_func = function_ref @useD : $@convention(thin) (@owned D) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useD_func(%d2) : $@convention(thin) (@owned D) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(8)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; strong_release %c : $C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (10)</div><div class="">&nbsp; &nbsp; }</div><div class=""><br class=""></div><div class="">Giving us the exact result that we want: Operation Sequence 2!</div><div class=""><br class=""></div><div class="">### Defining load [copy]</div><div class=""><br class=""></div><div class="">Given that we wish the load, store to be tightly coupled together, it is natural</div><div class="">to express this operation as a `load [copy]` instruction. Lets define the `load</div><div class="">[copy]` instruction as follows:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %1 = load [copy] %0 : $*C</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; =&gt;</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %1 = load %0 : $*C</div><div class="">&nbsp; &nbsp; retain_value %1 : $C</div><div class=""><br class=""></div><div class="">Now lets transform our initial example to use this instruction:</div><div class=""><br class=""></div><div class="">Notice how now if we move `(7)` over `(3)` and `(6)` now, we get the following SIL:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; sil @run : $@convention(thin) () -&gt; () {</div><div class="">&nbsp; &nbsp; bb0:</div><div class="">&nbsp; &nbsp; &nbsp; %c = alloc_ref $C</div><div class="">&nbsp; &nbsp; &nbsp; %global_c = global_addr @GLOBAL_C : $*C</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %c : $C</div><div class="">&nbsp; &nbsp; &nbsp; store %c to %global_c : $*C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(1)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %d = alloc_ref $D</div><div class="">&nbsp; &nbsp; &nbsp; %global_d = global_addr @GLOBAL_D : $*D</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %d : $D</div><div class="">&nbsp; &nbsp; &nbsp; store %d to %global_d : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(2)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %c2 = load [copy] %global_c : $*C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(3)</div><div class="">&nbsp; &nbsp; &nbsp; %d2 = load [copy] %global_d : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(5)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %useC_func = function_ref @useC : $@convention(thin) (@owned C) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useC_func(%c2) : $@convention(thin) (@owned C) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(7)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %useD_func = function_ref @useD : $@convention(thin) (@owned D) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useD_func(%d2) : $@convention(thin) (@owned D) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(8)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; strong_release %d : $D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (9)</div><div class="">&nbsp; &nbsp; &nbsp; strong_release %c : $C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (10)</div><div class="">&nbsp; &nbsp; }</div><div class=""><br class=""></div><div class="">We then perform the previous code motion:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; sil @run : $@convention(thin) () -&gt; () {</div><div class="">&nbsp; &nbsp; bb0:</div><div class="">&nbsp; &nbsp; &nbsp; %c = alloc_ref $C</div><div class="">&nbsp; &nbsp; &nbsp; %global_c = global_addr @GLOBAL_C : $*C</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %c : $C</div><div class="">&nbsp; &nbsp; &nbsp; store %c to %global_c : $*C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(1)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %d = alloc_ref $D</div><div class="">&nbsp; &nbsp; &nbsp; %global_d = global_addr @GLOBAL_D : $*D</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %d : $D</div><div class="">&nbsp; &nbsp; &nbsp; store %d to %global_d : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(2)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %c2 = load [copy] %global_c : $*C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(3)</div><div class="">&nbsp; &nbsp; &nbsp; %useC_func = function_ref @useC : $@convention(thin) (@owned C) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useC_func(%c2) : $@convention(thin) (@owned C) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(7)</div><div class="">&nbsp; &nbsp; &nbsp; strong_release %d : $D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (9)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %d2 = load [copy] %global_d : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(5)</div><div class="">&nbsp; &nbsp; &nbsp; %useD_func = function_ref @useD : $@convention(thin) (@owned D) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useD_func(%d2) : $@convention(thin) (@owned D) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(8)</div><div class="">&nbsp; &nbsp; &nbsp; strong_release %c : $C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (10)</div><div class="">&nbsp; &nbsp; }</div><div class=""><br class=""></div><div class="">We then would like to eliminate `(9)` and `(10)` by pairing them with `(3)` and</div><div class="">`(8)`. Can we still do so? One way we could do this is by introducing the</div><div class="">`[take]` flag. The `[take]` flag on a `load [take]` says that one is</div><div class="">semantically loading a value from a memory location and are taking ownership of</div><div class="">the value thus eliding the retain. In terms of SIL this flag is defined as:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %x = load [take] %x_ptr : $*C</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; =&gt;</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; %x = load %x_ptr : $*C</div><div class=""><br class=""></div><div class="">Why do we care about having such a `load [take]` instruction when we could just</div><div class="">use a `load`? The reason why is that a normal `load` has unsafe unowned</div><div class="">ownership (i.e. it has no implications on ownership). We would like for memory</div><div class="">that has non-trivial type to only be able to be loaded via instructions that</div><div class="">maintain said ownership. We will allow for casting to trivial types as usual to</div><div class="">provide such access if it is required.</div><div class=""><br class=""></div><div class="">Thus we have achieved the desired result:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; sil @run : $@convention(thin) () -&gt; () {</div><div class="">&nbsp; &nbsp; bb0:</div><div class="">&nbsp; &nbsp; &nbsp; %c = alloc_ref $C</div><div class="">&nbsp; &nbsp; &nbsp; %global_c = global_addr @GLOBAL_C : $*C</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %c : $C</div><div class="">&nbsp; &nbsp; &nbsp; store %c to %global_c : $*C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(1)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %d = alloc_ref $D</div><div class="">&nbsp; &nbsp; &nbsp; %global_d = global_addr @GLOBAL_D : $*D</div><div class="">&nbsp; &nbsp; &nbsp; strong_retain %d : $D</div><div class="">&nbsp; &nbsp; &nbsp; store %d to %global_d : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(2)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %c2 = load [take] %global_c : $*C &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(3)</div><div class="">&nbsp; &nbsp; &nbsp; %useC_func = function_ref @useC : $@convention(thin) (@owned C) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useC_func(%c2) : $@convention(thin) (@owned C) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(7)</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; &nbsp; %d2 = load [take] %global_d : $*D &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(5)</div><div class="">&nbsp; &nbsp; &nbsp; %useD_func = function_ref @useD : $@convention(thin) (@owned D) -&gt; ()</div><div class="">&nbsp; &nbsp; &nbsp; apply %useD_func(%d2) : $@convention(thin) (@owned D) -&gt; () &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(8)</div><div class="">&nbsp; &nbsp; }</div></div><div class=""><br class=""></div><div class="">----</div><div class=""><br class=""></div></div>_______________________________________________<br class="">swift-dev mailing list<br class=""><a href="mailto:swift-dev@swift.org" class="">swift-dev@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-dev<br class=""></div></blockquote></div><br class=""></div></body></html>