Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-14 Thread Andrew Trick via swift-dev
This seems fine to me… at a high level!
-Andy

> On Oct 14, 2016, at 2:44 PM, Michael Gottesman via swift-dev 
>  wrote:
> 
> 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.
> 
>> https://gottesmm.github.io/proposals/high-level-arc-memory-operations.html 
>> 
> Michael
> 
> 
> 
> # Summary
> 
> This document proposes:
> 
> 1. adding the following ownership qualifiers to `load`: `[take]`, `[copy]`,
>`[trivial]`.
> 2. adding the following ownership qualifiers to `store`: `[init]`, `[assign]`,
>`[trivial]`.
> 3. adding the `load_borrow` instruction and the `end_borrow` instruction.
> 3. requiring all `load` and `store` operations to have ownership qualifiers.
> 4. banning the use of `load [trivial]`, `store [trivial]` on memory locations 
> of
>`non-trivial` type.
> 
> This will allow for:
> 
> 1. eliminating optimizer miscompiles that occur due to releases being moved 
> into
>the region in between a `load`/`retain`, `load`/`release`,
>`store`/`release`. (For a specific example, see the appendix).
> 2. explicitly modeling `load [trivial]`/`store [trivial]` as having `unsafe
>unowned` ownership semantics. This will be enforced via the verifier.
> 3. more aggressive ARC code motion.
> 
> # Definitions
> 
> ## ownership qualified load
> 
> We propose three different ownership qualifiers for load. Define `load 
> [trivial]`
> as:
> 
> %x = load [trivial] %x_ptr : $*Int
> 
>   =>
> 
> %x = load %x_ptr : $*Int
> 
> A `load [trivial]` can not be used to load values of non-trivial type. Define
> `load [copy]` as:
> 
> %x = load [copy] %x_ptr : $*C
> 
>   =>
> 
> %x = load %x_ptr : $*C
> retain_value %x : $C
> 
> Then define `load [take]` as:
> 
> %x = load [take] %x_ptr : $*Builtin.NativeObject
> 
>   =>
> 
> %x = load %x_ptr : $*Builtin.NativeObject
> 
> **NOTE** `load [take]` implies that the loaded from memory location no longer
> owns the result object (i.e. a take is a move). Loading from the memory 
> location
> again without reinitialization is illegal.
> 
> ## load_borrow and end_borrow
> 
> Next we provide `load_borrow` and `end_borrow`:
> 
> %x = load_borrow %x_ptr : $*Builtin.NativeObject
> ...
> end_borrow %x, %x_ptr : $*Builtin.NativeObject
> 
>   =>
> 
> %x = load %x_ptr : $*Builtin.NativeObject
> ...
> endLifetime %x : $Builtin.NativeObject
> fixLifetime %x_ptr : $*Builtin.NativeObject
> 
> `load [borrow]` implies that in the region between the `load` and the
> `end_borrow`, the loaded object must semantically remain alive. The 
> `end_borrow`
> communicates to the optimizer:
> 
> 1. that the value in `%x_ptr` should not be destroyed before endBorrow.
> 2. uses of `%x` should not be sunk past endBorrow since `%x` is only a shallow
>copy of the value in `%x_ptr` and past that point `%x_ptr` may not remain
>alive.
> 
> An example of where this construct is useful is when one has a let binding to 
> a
> class instance `c` that contains a let field `f`. In that case `c`'s lifetime
> guarantees `f`'s lifetime meaning that returning `f` over the function call
> boundary is safe.
> 
> *NOTE* since the SILOwnershipModelEliminator will not process these
> instructions, endLifetime is just a strawman instruction that will not be
> implemented. In practice though, IRGen will need to create a suitable barrier 
> to
> ensure that LLVM does not move any uses of `%x` past the `fixLifetime`
> instruction of `%x_ptr` once we begin creating such instructions as a result 
> of
> ARC optimization.
> 
> ## ownership qualified store
> 
> First define a `store [trivial]` as:
> 
> store %x to [trivial] %x_ptr : $*Int
> 
>   =>
> 
> store %x to %x_ptr : $*Int
> 
> The verifier will prevent this instruction from being used on types with
> non-trivial ownership. Define a `store [assign]` as follows:
> 
> store %x to [assign] %x_ptr : $*C
> 
>=>
> 
> %old_x = load %x_ptr : $*C
> store %new_x to %x_ptr : $*C
> release_value %old_x : $C
> 
> *NOTE* `store` is defined as a consuming operation. We also provide
> `store [init]` in the case where we know statically that there is no
> previous value in the memory location:
> 
> store %x to [init] %x_ptr : $*C
> 
>=>
> 
> store %new_x to %x_ptr : $*C
> 
> # Implementation
> 
> ## Goals
> 
> Our implementation strategy goals are:
> 
> 1. zero impact on other compiler developers until the feature is fully
>developed. This implies all work will be done behind a flag.
> 2. separation of feature implementation from updating passes.
> 
> Goal 2 will be implemented via a pass that transforms ownership qualified
> `load`/`store` instructions into unqualified `load`/`store` right after 
> SILGen.
> 
> ## Plan
> 
> We begin by adding initial 

Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-14 Thread Michael Gottesman via swift-dev
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.

> https://gottesmm.github.io/proposals/high-level-arc-memory-operations.html 
> 
Michael



# Summary

This document proposes:

1. adding the following ownership qualifiers to `load`: `[take]`, `[copy]`,
   `[trivial]`.
2. adding the following ownership qualifiers to `store`: `[init]`, `[assign]`,
   `[trivial]`.
3. adding the `load_borrow` instruction and the `end_borrow` instruction.
3. requiring all `load` and `store` operations to have ownership qualifiers.
4. banning the use of `load [trivial]`, `store [trivial]` on memory locations of
   `non-trivial` type.

This will allow for:

1. eliminating optimizer miscompiles that occur due to releases being moved into
   the region in between a `load`/`retain`, `load`/`release`,
   `store`/`release`. (For a specific example, see the appendix).
2. explicitly modeling `load [trivial]`/`store [trivial]` as having `unsafe
   unowned` ownership semantics. This will be enforced via the verifier.
3. more aggressive ARC code motion.

# Definitions

## ownership qualified load

We propose three different ownership qualifiers for load. Define `load 
[trivial]`
as:

%x = load [trivial] %x_ptr : $*Int

  =>

%x = load %x_ptr : $*Int

A `load [trivial]` can not be used to load values of non-trivial type. Define
`load [copy]` as:

%x = load [copy] %x_ptr : $*C

  =>

%x = load %x_ptr : $*C
retain_value %x : $C

Then define `load [take]` as:

%x = load [take] %x_ptr : $*Builtin.NativeObject

  =>

%x = load %x_ptr : $*Builtin.NativeObject

**NOTE** `load [take]` implies that the loaded from memory location no longer
owns the result object (i.e. a take is a move). Loading from the memory location
again without reinitialization is illegal.

## load_borrow and end_borrow

Next we provide `load_borrow` and `end_borrow`:

%x = load_borrow %x_ptr : $*Builtin.NativeObject
...
end_borrow %x, %x_ptr : $*Builtin.NativeObject

  =>

%x = load %x_ptr : $*Builtin.NativeObject
...
endLifetime %x : $Builtin.NativeObject
fixLifetime %x_ptr : $*Builtin.NativeObject

`load [borrow]` implies that in the region between the `load` and the
`end_borrow`, the loaded object must semantically remain alive. The `end_borrow`
communicates to the optimizer:

1. that the value in `%x_ptr` should not be destroyed before endBorrow.
2. uses of `%x` should not be sunk past endBorrow since `%x` is only a shallow
   copy of the value in `%x_ptr` and past that point `%x_ptr` may not remain
   alive.

An example of where this construct is useful is when one has a let binding to a
class instance `c` that contains a let field `f`. In that case `c`'s lifetime
guarantees `f`'s lifetime meaning that returning `f` over the function call
boundary is safe.

*NOTE* since the SILOwnershipModelEliminator will not process these
instructions, endLifetime is just a strawman instruction that will not be
implemented. In practice though, IRGen will need to create a suitable barrier to
ensure that LLVM does not move any uses of `%x` past the `fixLifetime`
instruction of `%x_ptr` once we begin creating such instructions as a result of
ARC optimization.

## ownership qualified store

First define a `store [trivial]` as:

store %x to [trivial] %x_ptr : $*Int

  =>

store %x to %x_ptr : $*Int

The verifier will prevent this instruction from being used on types with
non-trivial ownership. Define a `store [assign]` as follows:

store %x to [assign] %x_ptr : $*C

   =>

%old_x = load %x_ptr : $*C
store %new_x to %x_ptr : $*C
release_value %old_x : $C

*NOTE* `store` is defined as a consuming operation. We also provide
`store [init]` in the case where we know statically that there is no
previous value in the memory location:

store %x to [init] %x_ptr : $*C

   =>

store %new_x to %x_ptr : $*C

# Implementation

## Goals

Our implementation strategy goals are:

1. zero impact on other compiler developers until the feature is fully
   developed. This implies all work will be done behind a flag.
2. separation of feature implementation from updating passes.

Goal 2 will be implemented via a pass that transforms ownership qualified
`load`/`store` instructions into unqualified `load`/`store` right after SILGen.

## Plan

We begin by adding initial infrastructure for our development. This means:

1. Adding to SILOptions a disabled by default flag called
 "EnableSILOwnershipModel". This flag will be set by a false by default frontend
 option called "-enable-sil-ownership-mode".

2. Bots will be brought up to test the compiler with
   "-enable-sil-ownership-model" set to true. The specific bots are:

   * RA-OSX+simulators
   * RA-Device
   * RA-Linux.

   The bots will run once a day until the feature is close to 

Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-11 Thread Chris Lattner via swift-dev
On Oct 11, 2016, at 10:10 AM, Michael Gottesman  wrote:
>> >If ARC Code Motion wishes to move the ARC semantics of ownership qualified 
>> >load, store instructions, it must now consider read/write effects. On the 
>> >other hand, it will be able to now not consider the side-effects of 
>> >destructors when moving retain/release operations.
>> 
>> Can you elaborate on what you mean by this?  Does this mean the ARC 
>> optimizer has additional freedom somehow to ignore side effects in deinits?  
>> What grants that ability?
> 
> Sorry for not being clear.
> 
> deinits in ARC are not sequenced with respect to memory operations in normal 
> control flow, but if ARC performs any code motion, we must ensure that memory 
> safety is respected. Such semantics are not new.
> 
> The main change here is that previously there were situations where we would 
> split up the load/retain in a load [copy] operation. Then if the right things 
> occured, we could cause the object to be deallocated before we use the value 
> or took full ownership of the value.

Ah, I see what you mean.  Got it.

> In this case, we would be passing off to useD a deallocated instance of class 
> D1 which would be undefined behavior.
> 
> So as long as for these load [copy] operations, we move the load/retain fused 
> together, we can guarantee that an object is produced instantaneously in a 
> safe way without any worry.
> 
> Was this helpful?

Yep, totally.  The new approach is much more clear, thanks Michael!

-Chris


___
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev


Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-11 Thread Michael Gottesman via swift-dev

> On Oct 10, 2016, at 8:05 PM, Chris Lattner  wrote:
> 
> 
>> On Oct 7, 2016, at 2:38 PM, Michael Gottesman via swift-dev 
>> > wrote:
>> 
>> Attached below is an updated version of the proposal. Again a rendered 
>> version is located at:
>> 
>> https://gottesmm.github.io/proposals/high-level-arc-memory-operations.html 
>> 
>> 
>> Michael
> 
> This looks great Michael, a couple of comments/questions:
> 
> typo: affects -> effects.
> 
> 
> >If ARC Code Motion wishes to move the ARC semantics of ownership qualified 
> >load, store instructions, it must now consider read/write effects. On the 
> >other hand, it will be able to now not consider the side-effects of 
> >destructors when moving retain/release operations.
> 
> Can you elaborate on what you mean by this?  Does this mean the ARC optimizer 
> has additional freedom somehow to ignore side effects in deinits?  What 
> grants that ability?

Sorry for not being clear.

deinits in ARC are not sequenced with respect to memory operations in normal 
control flow, but if ARC performs any code motion, we must ensure that memory 
safety is respected. Such semantics are not new.

The main change here is that previously there were situations where we would 
split up the load/retain in a load [copy] operation. Then if the right things 
occured, we could cause the object to be deallocated before we use the value or 
took full ownership of the value.

Consider the following example:


class D {}
class D1 : D {}
class D2 : D {}

var GLOBAL_D : D = D1()

class C { deinit { GLOBAL_D = D2 } }

func main() {
  let c = C()
  let d = GLOBAL_D
  useC(c)
  useD(d)
}

main()


Assume that useC does not directly in any way touch an instance of class D 
except via the destructor .

Since memory operations are not sequenced with respect to deinits, there are 
two correct programs here that the optimizer can produce: the original and the 
one where useC(c) and GLOBAL_D are swapped, i.e.:


func main() {
  let c = C()
  useC(c)
  let d = GLOBAL_D
  useD(d)
}


In the first program, d would be an instance of class D1. In the second, it 
would be an instance of class D2. Notice how in both programs though, no 
deinitialized object is accessed. On the other hand, imagine if we had split 
main like so:


func main() {
  let c = C()
  let d = unsafe_unowned_load(GLOBAL_D)
  useC(c)
  let owned_d = retain(d)
  useD(owned_d)
}


In this case, we would be passing off to useD a deallocated instance of class 
D1 which would be undefined behavior.

So as long as for these load [copy] operations, we move the load/retain fused 
together, we can guarantee that an object is produced instantaneously in a safe 
way without any worry.

Was this helpful?
Michael

> 
> -Chris

___
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev


Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-07 Thread John McCall via swift-dev
> On Oct 7, 2016, at 10:36 PM, Michael Gottesman  wrote:
>> On Oct 7, 2016, at 10:26 PM, Andrew Trick > > wrote:
>> 
>> 
>>> On Oct 7, 2016, at 10:08 PM, Michael Gottesman >> > wrote:
>>> 
 
 On Oct 7, 2016, at 9:25 PM, Andrew Trick > wrote:
 
 
> On Oct 7, 2016, at 6:04 PM, Michael Gottesman via swift-dev 
> > wrote:
> 
>> I wonder whether it might make more sense for load [borrow] to be a 
>> different instruction.
>> There's a couple reasons for that first.  The first is that it's the 
>> only load which introduces
>> a scope, which is a really big difference structurally.  The second is 
>> that it's the only load
>> which returns a non-owned value, which will be a typing difference when 
>> we record
>> ownership in the type system.
> 
> I am fine with a load_borrow. If this is the only change left that you 
> want can I just send out a proposal with that small change and start 
> implementing. I am nervous about perfection being the enemy of the good 
> (and I want to start implementing this weekend if possible *evil smile*).
 
 There’s a lot in the proposal that makes sense to discuss for completeness 
 but isn’t motivated by a particular need. Please separate functionality. 
 We only need load [copy] at first right? When do those need to be promoted 
 to load_borrow?
>>> 
>>> These are needed for the ARC optimizer to eliminate retain, release 
>>> operations, i.e. a:
>>> 
>>> %0 = load [copy] %x_ptr
>>> 
>>> destroy_value %1
>>> 
>>> =>
>>> 
>>> %0 = load [borrow] %x_ptr
>>> 
>>> borrow_end(%0, %x_ptr)
>>> 
>>> These constructs will be needed by engineers to update passes like ARC. By 
>>> implementing such modifiers now, we can begin to implement support in the 
>>> various passes for these new instructions via sil-opt/etc in parallel to 
>>> other semantic ARC work.
>>> 
 load [trivial] is an optimization, so that should follow a functionally 
 complete implementation. 
>>> 
>>> Yes you are correct that given that we are exploding the load [copy] in the 
>>> eliminator, the trivial load is not *strictly* needed. But as soon as we 
>>> start upgrading passes, we are going to want this. Again assuming that 
>>> parallel work can be done, it makes sense to set the stage for optimizer 
>>> work that will occur in parallel to further semantic ARC work.
>>> 
  load [take] should definitely not exist until there’s some motivation.
>>> 
>>> If you look at the frontend, there are places where the frontend wants to 
>>> emit a take. Unless we are willing to use unqualified loads for those cases 
>>> (which we can not if we are trying to prove that no unqualified loads are 
>>> emitted by the frontend), then we must have a load [take].
>>> 
>>> Did I provide the motivation that you requested?
>> 
>> Yes. My general request is for each commit to be easy to review and the 
>> functionality obvious to test. I’m convinced we’ll eventually want the 
>> variants. Although I still want to understand better when we need to [take] 
>> values out of memory.
> 
> Just as a quick example, the API for emitLoad in SILGenFunction:
> 
>   ManagedValue emitLoad(SILLocation loc, SILValue addr,
> const TypeLowering ,
> SGFContext C, IsTake_t isTake,
> bool isGuaranteedValid = false);
> 
> Notice the IsTake_t parameter. I see that code path used in several locations 
> in SILGenFunction.
> 
>> 
>> I also want to prove that my understanding of the model is accurate by 
>> seeing everything work with load [copy].
> 
> I am fine doing everything initially with load [copy] (and when SILGen 
> requires load [take]). The other things can wait until we need them. I just 
> don't want to have to do another proposal at that point ; ).

Well, you might as well *implement* load [trivial] now.  It's literally going 
to be, like, five lines in various switch statements plus an obvious case in 
the verifier.  You can incrementally move things over to actually start *using* 
load [trivial] whenever you like.

John.___
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev


Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-07 Thread Andrew Trick via swift-dev

> On Oct 7, 2016, at 10:36 PM, Michael Gottesman  wrote:
> 
>> 
>> On Oct 7, 2016, at 10:26 PM, Andrew Trick > > wrote:
>> 
>> 
>>> On Oct 7, 2016, at 10:08 PM, Michael Gottesman >> > wrote:
>>> 
 
 On Oct 7, 2016, at 9:25 PM, Andrew Trick > wrote:
 
 
> On Oct 7, 2016, at 6:04 PM, Michael Gottesman via swift-dev 
> > wrote:
> 
>> I wonder whether it might make more sense for load [borrow] to be a 
>> different instruction.
>> There's a couple reasons for that first.  The first is that it's the 
>> only load which introduces
>> a scope, which is a really big difference structurally.  The second is 
>> that it's the only load
>> which returns a non-owned value, which will be a typing difference when 
>> we record
>> ownership in the type system.
> 
> I am fine with a load_borrow. If this is the only change left that you 
> want can I just send out a proposal with that small change and start 
> implementing. I am nervous about perfection being the enemy of the good 
> (and I want to start implementing this weekend if possible *evil smile*).
 
 There’s a lot in the proposal that makes sense to discuss for completeness 
 but isn’t motivated by a particular need. Please separate functionality. 
 We only need load [copy] at first right? When do those need to be promoted 
 to load_borrow?
>>> 
>>> These are needed for the ARC optimizer to eliminate retain, release 
>>> operations, i.e. a:
>>> 
>>> %0 = load [copy] %x_ptr
>>> 
>>> destroy_value %1
>>> 
>>> =>
>>> 
>>> %0 = load [borrow] %x_ptr
>>> 
>>> borrow_end(%0, %x_ptr)
>>> 
>>> These constructs will be needed by engineers to update passes like ARC. By 
>>> implementing such modifiers now, we can begin to implement support in the 
>>> various passes for these new instructions via sil-opt/etc in parallel to 
>>> other semantic ARC work.
>>> 
 load [trivial] is an optimization, so that should follow a functionally 
 complete implementation. 
>>> 
>>> Yes you are correct that given that we are exploding the load [copy] in the 
>>> eliminator, the trivial load is not *strictly* needed. But as soon as we 
>>> start upgrading passes, we are going to want this. Again assuming that 
>>> parallel work can be done, it makes sense to set the stage for optimizer 
>>> work that will occur in parallel to further semantic ARC work.
>>> 
  load [take] should definitely not exist until there’s some motivation.
>>> 
>>> If you look at the frontend, there are places where the frontend wants to 
>>> emit a take. Unless we are willing to use unqualified loads for those cases 
>>> (which we can not if we are trying to prove that no unqualified loads are 
>>> emitted by the frontend), then we must have a load [take].
>>> 
>>> Did I provide the motivation that you requested?
>> 
>> Yes. My general request is for each commit to be easy to review and the 
>> functionality obvious to test. I’m convinced we’ll eventually want the 
>> variants. Although I still want to understand better when we need to [take] 
>> values out of memory.
> 
> Just as a quick example, the API for emitLoad in SILGenFunction:
> 
>   ManagedValue emitLoad(SILLocation loc, SILValue addr,
> const TypeLowering ,
> SGFContext C, IsTake_t isTake,
> bool isGuaranteedValid = false);
> 
> Notice the IsTake_t parameter. I see that code path used in several locations 
> in SILGenFunction.

I guess it’s doing this to forward locals variables at their last use, avoiding 
a copy. Although probably not theoretically necessary, I guess it would be 
silly not to do this.
-Andy

>> 
>> I also want to prove that my understanding of the model is accurate by 
>> seeing everything work with load [copy].
> 
> I am fine doing everything initially with load [copy] (and when SILGen 
> requires load [take]). The other things can wait until we need them. I just 
> don't want to have to do another proposal at that point ; ).
> 
>> 
>> -Andy

___
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev


Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-07 Thread Michael Gottesman via swift-dev

> On Oct 7, 2016, at 10:26 PM, Andrew Trick  wrote:
> 
> 
>> On Oct 7, 2016, at 10:08 PM, Michael Gottesman > > wrote:
>> 
>>> 
>>> On Oct 7, 2016, at 9:25 PM, Andrew Trick >> > wrote:
>>> 
>>> 
 On Oct 7, 2016, at 6:04 PM, Michael Gottesman via swift-dev 
 > wrote:
 
> I wonder whether it might make more sense for load [borrow] to be a 
> different instruction.
> There's a couple reasons for that first.  The first is that it's the only 
> load which introduces
> a scope, which is a really big difference structurally.  The second is 
> that it's the only load
> which returns a non-owned value, which will be a typing difference when 
> we record
> ownership in the type system.
 
 I am fine with a load_borrow. If this is the only change left that you 
 want can I just send out a proposal with that small change and start 
 implementing. I am nervous about perfection being the enemy of the good 
 (and I want to start implementing this weekend if possible *evil smile*).
>>> 
>>> There’s a lot in the proposal that makes sense to discuss for completeness 
>>> but isn’t motivated by a particular need. Please separate functionality. We 
>>> only need load [copy] at first right? When do those need to be promoted to 
>>> load_borrow?
>> 
>> These are needed for the ARC optimizer to eliminate retain, release 
>> operations, i.e. a:
>> 
>> %0 = load [copy] %x_ptr
>> 
>> destroy_value %1
>> 
>> =>
>> 
>> %0 = load [borrow] %x_ptr
>> 
>> borrow_end(%0, %x_ptr)
>> 
>> These constructs will be needed by engineers to update passes like ARC. By 
>> implementing such modifiers now, we can begin to implement support in the 
>> various passes for these new instructions via sil-opt/etc in parallel to 
>> other semantic ARC work.
>> 
>>> load [trivial] is an optimization, so that should follow a functionally 
>>> complete implementation. 
>> 
>> Yes you are correct that given that we are exploding the load [copy] in the 
>> eliminator, the trivial load is not *strictly* needed. But as soon as we 
>> start upgrading passes, we are going to want this. Again assuming that 
>> parallel work can be done, it makes sense to set the stage for optimizer 
>> work that will occur in parallel to further semantic ARC work.
>> 
>>>  load [take] should definitely not exist until there’s some motivation.
>> 
>> If you look at the frontend, there are places where the frontend wants to 
>> emit a take. Unless we are willing to use unqualified loads for those cases 
>> (which we can not if we are trying to prove that no unqualified loads are 
>> emitted by the frontend), then we must have a load [take].
>> 
>> Did I provide the motivation that you requested?
> 
> Yes. My general request is for each commit to be easy to review and the 
> functionality obvious to test. I’m convinced we’ll eventually want the 
> variants. Although I still want to understand better when we need to [take] 
> values out of memory.

Just as a quick example, the API for emitLoad in SILGenFunction:

  ManagedValue emitLoad(SILLocation loc, SILValue addr,
const TypeLowering ,
SGFContext C, IsTake_t isTake,
bool isGuaranteedValid = false);

Notice the IsTake_t parameter. I see that code path used in several locations 
in SILGenFunction.

> 
> I also want to prove that my understanding of the model is accurate by seeing 
> everything work with load [copy].

I am fine doing everything initially with load [copy] (and when SILGen requires 
load [take]). The other things can wait until we need them. I just don't want 
to have to do another proposal at that point ; ).

> 
> -Andy

___
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev


Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-07 Thread Andrew Trick via swift-dev

> On Oct 7, 2016, at 10:08 PM, Michael Gottesman  wrote:
> 
>> 
>> On Oct 7, 2016, at 9:25 PM, Andrew Trick > > wrote:
>> 
>> 
>>> On Oct 7, 2016, at 6:04 PM, Michael Gottesman via swift-dev 
>>> > wrote:
>>> 
 I wonder whether it might make more sense for load [borrow] to be a 
 different instruction.
 There's a couple reasons for that first.  The first is that it's the only 
 load which introduces
 a scope, which is a really big difference structurally.  The second is 
 that it's the only load
 which returns a non-owned value, which will be a typing difference when we 
 record
 ownership in the type system.
>>> 
>>> I am fine with a load_borrow. If this is the only change left that you want 
>>> can I just send out a proposal with that small change and start 
>>> implementing. I am nervous about perfection being the enemy of the good 
>>> (and I want to start implementing this weekend if possible *evil smile*).
>> 
>> There’s a lot in the proposal that makes sense to discuss for completeness 
>> but isn’t motivated by a particular need. Please separate functionality. We 
>> only need load [copy] at first right? When do those need to be promoted to 
>> load_borrow?
> 
> These are needed for the ARC optimizer to eliminate retain, release 
> operations, i.e. a:
> 
> %0 = load [copy] %x_ptr
> 
> destroy_value %1
> 
> =>
> 
> %0 = load [borrow] %x_ptr
> 
> borrow_end(%0, %x_ptr)
> 
> These constructs will be needed by engineers to update passes like ARC. By 
> implementing such modifiers now, we can begin to implement support in the 
> various passes for these new instructions via sil-opt/etc in parallel to 
> other semantic ARC work.
> 
>> load [trivial] is an optimization, so that should follow a functionally 
>> complete implementation. 
> 
> Yes you are correct that given that we are exploding the load [copy] in the 
> eliminator, the trivial load is not *strictly* needed. But as soon as we 
> start upgrading passes, we are going to want this. Again assuming that 
> parallel work can be done, it makes sense to set the stage for optimizer work 
> that will occur in parallel to further semantic ARC work.
> 
>>  load [take] should definitely not exist until there’s some motivation.
> 
> If you look at the frontend, there are places where the frontend wants to 
> emit a take. Unless we are willing to use unqualified loads for those cases 
> (which we can not if we are trying to prove that no unqualified loads are 
> emitted by the frontend), then we must have a load [take].
> 
> Did I provide the motivation that you requested?

Yes. My general request is for each commit to be easy to review and the 
functionality obvious to test. I’m convinced we’ll eventually want the 
variants. Although I still want to understand better when we need to [take] 
values out of memory.

I also want to prove that my understanding of the model is accurate by seeing 
everything work with load [copy].

-Andy___
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev


Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-07 Thread Andrew Trick via swift-dev

> On Oct 7, 2016, at 6:04 PM, Michael Gottesman via swift-dev 
>  wrote:
> 
>> I wonder whether it might make more sense for load [borrow] to be a 
>> different instruction.
>> There's a couple reasons for that first.  The first is that it's the only 
>> load which introduces
>> a scope, which is a really big difference structurally.  The second is that 
>> it's the only load
>> which returns a non-owned value, which will be a typing difference when we 
>> record
>> ownership in the type system.
> 
> I am fine with a load_borrow. If this is the only change left that you want 
> can I just send out a proposal with that small change and start implementing. 
> I am nervous about perfection being the enemy of the good (and I want to 
> start implementing this weekend if possible *evil smile*).

There’s a lot in the proposal that makes sense to discuss for completeness but 
isn’t motivated by a particular need. Please separate functionality. We only 
need load [copy] at first right? When do those need to be promoted to 
load_borrow? load [trivial] is an optimization, so that should follow a 
functionally complete implementation.  load [take] should definitely not exist 
until there’s some motivation.

-Andy___
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev


Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-07 Thread John McCall via swift-dev
> On Oct 7, 2016, at 6:04 PM, Michael Gottesman  wrote:
>> On Oct 7, 2016, at 5:09 PM, John McCall > > wrote:
>>> On Oct 7, 2016, at 2:38 PM, Michael Gottesman >> > wrote:
>>> 
>>> Attached below is an updated version of the proposal. Again a rendered 
>>> version is located at:
>>> 
>>> https://gottesmm.github.io/proposals/high-level-arc-memory-operations.html 
>>> 
>>> 
>>> Michael
>>> 
>>> 
>>> 
>>> # Summary
>>> 
>>> This document proposes:
>>> 
>>> 1. adding the following ownership qualifiers to `load`: `[take]`, `[copy]`,
>>>`[borrow]`, `[trivial]`.
>>> 2. adding the following ownership qualifiers to `store`: `[init]`, 
>>> `[assign]`,
>>>`[trivial]`.
>>> 3. requiring all `load` and `store` operations to have ownership qualifiers.
>>> 4. banning the use of `load [trivial]`, `store [trivial]` on memory 
>>> locations of
>>>`non-trivial` type.
>>> 
>>> This will allow for:
>>> 
>>> 1. eliminating optimizer miscompiles that occur due to releases being moved 
>>> into
>>>the region in between a `load`/`retain`, `load`/`release`,
>>>`store`/`release`. (For a specific example, see the appendix).
>>> 2. explicitly modeling `load [trivial]`/`store [trivial]` as having `unsafe
>>>unowned` ownership semantics. This will be enforced via the verifier.
>>> 3. more aggressive ARC code motion.
>>> 
>>> # Definitions
>>> 
>>> ## ownership qualified load
>>> 
>>> We propose four different ownership qualifiers for load. Define `load 
>>> [trivial]`
>>> as:
>>> 
>>> %x = load [trivial] %x_ptr : $*Int
>>> 
>>>   =>
>>> 
>>> %x = load %x_ptr : $*Int
>>> 
>>> A `load [trivial]` can not be used to load values of non-trivial type.
>> 
>> Should we mandate the reverse as well, that e.g. load [copy] cannot be used 
>> to
>> load values of trivial type?  That's a little more work for substituting 
>> cloners, but it
>> keeps everything nice and canonical.
> 
> No. I think that in the trivial case, load [copy] optimizes to load [trivial] 
> as a canonicalization. This is just like applying a retain_value to a trivial 
> type.

I guess I'm fine with that.

>>> Define
>>> `load [copy]` as:
>>> 
>>> %x = load [copy] %x_ptr : $*C
>>> 
>>>   =>
>>> 
>>> %x = load %x_ptr : $*C
>>> retain_value %x : $C
>>> 
>>> Then define `load [take]` as:
>>> 
>>> %x = load [take] %x_ptr : $*Builtin.NativeObject
>>> 
>>>   =>
>>> 
>>> %x = load %x_ptr : $*Builtin.NativeObject
>>> 
>>> **NOTE** `load [take]` implies that the loaded from memory location no 
>>> longer
>>> owns the result object (i.e. a take is a move). Loading from the memory 
>>> location
>>> again without reinitialization is illegal.
>>> 
>>> Next we provide `load [borrow]`:
>>> 
>>> %x = load [borrow] %x_ptr : $*Builtin.NativeObject
>>> ...
>>> endBorrow(%x, %x_ptr)
>>> 
>>>   =>
>>> 
>>> %x = load %x_ptr : $*Builtin.NativeObject
>>> ...
>>> endBorrow(%x, %x_ptr)
>>> 
>>> `load [borrow]` implies that in the region between the `load` and the
>>> `endBorrow`, the loaded object must semantically remain alive.
>> 
>> For consistency with other multi-word SIL instructions, this should be 
>> end_borrow.
> 
> Sure.
> 
>> 
>> I wonder whether it might make more sense for load [borrow] to be a 
>> different instruction.
>> There's a couple reasons for that first.  The first is that it's the only 
>> load which introduces
>> a scope, which is a really big difference structurally.  The second is that 
>> it's the only load
>> which returns a non-owned value, which will be a typing difference when we 
>> record
>> ownership in the type system.
> 
> I am fine with a load_borrow. If this is the only change left that you want 
> can I just send out a proposal with that small change and start implementing. 
> I am nervous about perfection being the enemy of the good (and I want to 
> start implementing this weekend if possible *evil smile*).

Yes, it's fine to introduce this incrementally.

We can discuss the formation rules on scopes concurrently.  I think the same 
theoretical
foundation is probably what we'll use for pseudo-linearity, 
memory-initialization soundness,
and so on.

John.

>> Anyway, I really like that load [borrow] is scoped..  Are you planning to 
>> include the formation
>> restrictions on scopes instructions immediately, or will you do that in a 
>> later proposal?
> 
> It will be done in a later proposal. We are just trying to set the stage for 
> verification.
> 
>> 
>> The requirements we need from scopes are:
>>   - there has to be a well-defined set of IPs that lie within the scope and
>>   - there can't be a non-explicit transition into or out of the scope.
>> 
>> One way to get the first condition is to say that there has to be a unique 
>> scope-ender that
>> post-dominates 

Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-07 Thread Michael Gottesman via swift-dev

> On Oct 7, 2016, at 5:09 PM, John McCall  wrote:
> 
>> 
>> On Oct 7, 2016, at 2:38 PM, Michael Gottesman > > wrote:
>> 
>> Attached below is an updated version of the proposal. Again a rendered 
>> version is located at:
>> 
>> https://gottesmm.github.io/proposals/high-level-arc-memory-operations.html 
>> 
>> 
>> Michael
>> 
>> 
>> 
>> # Summary
>> 
>> This document proposes:
>> 
>> 1. adding the following ownership qualifiers to `load`: `[take]`, `[copy]`,
>>`[borrow]`, `[trivial]`.
>> 2. adding the following ownership qualifiers to `store`: `[init]`, 
>> `[assign]`,
>>`[trivial]`.
>> 3. requiring all `load` and `store` operations to have ownership qualifiers.
>> 4. banning the use of `load [trivial]`, `store [trivial]` on memory 
>> locations of
>>`non-trivial` type.
>> 
>> This will allow for:
>> 
>> 1. eliminating optimizer miscompiles that occur due to releases being moved 
>> into
>>the region in between a `load`/`retain`, `load`/`release`,
>>`store`/`release`. (For a specific example, see the appendix).
>> 2. explicitly modeling `load [trivial]`/`store [trivial]` as having `unsafe
>>unowned` ownership semantics. This will be enforced via the verifier.
>> 3. more aggressive ARC code motion.
>> 
>> # Definitions
>> 
>> ## ownership qualified load
>> 
>> We propose four different ownership qualifiers for load. Define `load 
>> [trivial]`
>> as:
>> 
>> %x = load [trivial] %x_ptr : $*Int
>> 
>>   =>
>> 
>> %x = load %x_ptr : $*Int
>> 
>> A `load [trivial]` can not be used to load values of non-trivial type.
> 
> Should we mandate the reverse as well, that e.g. load [copy] cannot be used to
> load values of trivial type?  That's a little more work for substituting 
> cloners, but it
> keeps everything nice and canonical.

No. I think that in the trivial case, load [copy] optimizes to load [trivial] 
as a canonicalization. This is just like applying a retain_value to a trivial 
type.

> 
>> Define
>> `load [copy]` as:
>> 
>> %x = load [copy] %x_ptr : $*C
>> 
>>   =>
>> 
>> %x = load %x_ptr : $*C
>> retain_value %x : $C
>> 
>> Then define `load [take]` as:
>> 
>> %x = load [take] %x_ptr : $*Builtin.NativeObject
>> 
>>   =>
>> 
>> %x = load %x_ptr : $*Builtin.NativeObject
>> 
>> **NOTE** `load [take]` implies that the loaded from memory location no longer
>> owns the result object (i.e. a take is a move). Loading from the memory 
>> location
>> again without reinitialization is illegal.
>> 
>> Next we provide `load [borrow]`:
>> 
>> %x = load [borrow] %x_ptr : $*Builtin.NativeObject
>> ...
>> endBorrow(%x, %x_ptr)
>> 
>>   =>
>> 
>> %x = load %x_ptr : $*Builtin.NativeObject
>> ...
>> endBorrow(%x, %x_ptr)
>> 
>> `load [borrow]` implies that in the region between the `load` and the
>> `endBorrow`, the loaded object must semantically remain alive.
> 
> For consistency with other multi-word SIL instructions, this should be 
> end_borrow.

Sure.

> 
> I wonder whether it might make more sense for load [borrow] to be a different 
> instruction.
> There's a couple reasons for that first.  The first is that it's the only 
> load which introduces
> a scope, which is a really big difference structurally.  The second is that 
> it's the only load
> which returns a non-owned value, which will be a typing difference when we 
> record
> ownership in the type system.

I am fine with a load_borrow. If this is the only change left that you want can 
I just send out a proposal with that small change and start implementing. I am 
nervous about perfection being the enemy of the good (and I want to start 
implementing this weekend if possible *evil smile*).

> 
> Anyway, I really like that load [borrow] is scoped..  Are you planning to 
> include the formation
> restrictions on scopes instructions immediately, or will you do that in a 
> later proposal?

It will be done in a later proposal. We are just trying to set the stage for 
verification.

> 
> The requirements we need from scopes are:
>   - there has to be a well-defined set of IPs that lie within the scope and
>   - there can't be a non-explicit transition into or out of the scope.
> 
> One way to get the first condition is to say that there has to be a unique 
> scope-ender that
> post-dominates the scope-beginner, but that's a pretty hard restriction for 
> SILGen to satisfy
> (as well as the optimizer, I imagine), and it doesn't handle "unreachable" or 
> infinite loops.
> We need to allow multiple scope-enders, and we need to allow scope-enders to 
> be missing
> in some cases.

I agree with you here. We definitely want to be able to support multiple 
scope-enders.

>  Here's the right formalism, I think:
> 
> For all walks W beginning from the entry point of the function:
>   For each node B in the CFG which is a 

Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-06 Thread John McCall via swift-dev
> On Oct 5, 2016, at 4:48 PM, Michael Gottesman  wrote:
>> On Oct 5, 2016, at 4:40 PM, Michael Gottesman via swift-dev 
>> > wrote:
>> 
>>> 
>>> On Oct 4, 2016, at 1:04 PM, John McCall >> > wrote:
>>> 
 
 On Sep 30, 2016, at 11:54 PM, Michael Gottesman via swift-dev 
 > wrote:
 
 The document attached below contains the first "Semantic ARC" mini 
 proposal: the High Level ARC Memory Operations Proposal.
 
 An html rendered version of this markdown document is available at the 
 following URL:
 
 https://gottesmm.github.io/proposals/high-level-arc-memory-operations.html 
 
 
 
 
 # Summary
 
 This document proposes:
 
 1. adding the `load_strong`, `store_strong` instructions to SIL. These can 
 only
be used with memory locations of `non-trivial` type.
>>> 
>>> I would really like to avoid using the word "strong" here.  Under the 
>>> current proposal, these instructions will be usable with arbitrary 
>>> non-trivial types, not just primitive class references.  Even if you think 
>>> of an aggregate that happens to contain one or more strong references as 
>>> some sort of aggregate strong reference (which is questionable but not 
>>> completely absurd), we already have loadable non-strong class references 
>>> that this operation would be usable with, like native unowned references.  
>>> "load_strong %0 : $*@sil_unowned T" as an operation yielding a scalar 
>>> "@sil_unowned T" is ridiculous, and it will only get more ridiculous when 
>>> we eventually allow this operation to work with types that are currently 
>>> address-only, like weak references.
>>> 
>>> Brainstorming:
>>> 
>>> Something like load_copy and store_copy would be a bit unfortunate, since 
>>> store_copy doesn't actually copy the source operand and we want to have a 
>>> load_copy [take].
>>> 
>>> load_value and store_value seem excessively generic.  It's not like 
>>> non-trivial types aren't values.
>>> 
>>> One question that comes to mind: do we actually need new instructions here 
>>> other than for staging purposes?  We don't actually need new instructions 
>>> for pseudo-linear SIL to work; we just need to say that we only enforce 
>>> pseudo-linearity for non-trivial types.
>>> 
>>> If we just want the instruction to be explicit about ownership so that we 
>>> can easily distinguish these cases, we can make the rule always explicit, 
>>> e.g.:
>>>   load [take] %0 : $*MyClass
>>>   load [copy] %0 : $*MyClass
>>>   load [trivial] %0 : $*Int
>>> 
>>>   store %0 to [initialization] %1 : $*MyClass
>>>   store %0 to [assignment] %1 : $*MyClass
>>>   store %0 to [trivial] %1 : $*Int
>>> 
>>> John.
>> 
>> The reason why I originally suggested to go the load_strong route is that we 
>> already have load_weak, load_unowned instructions. If I could add a 
>> load_strong instruction, then it would make sense to assign an engineer to 
>> do a pass over all 3 of these instructions and combine them into 1 load 
>> instruction. That is, first transform into a form amenable for 
>> canonicalization and then canonicalize all at once.
>> 
>> As you pointed out, both load_unowned and load_weak involve representation 
>> changes in type (for instance the change of weak pointers to Optional). 
>> Such a change would be against the "spirit" of a load instruction to perform 
>> such representation changes versus ownership changes.
>> 
>> In terms of the properties that we actually want here, what is important is 
>> that we can verify that no non-trivially typed values are loaded in an 
>> unsafe unowned manner. That can be done also with ownership flags on 
>> load/store.
>> 
>> Does this sound reasonable:
>> 
>> 1. We introduce two enums that define memory ownership changes, one for load 
>> and one for store. Both of these enums will contain a [trivial] ownership.
>> 2. We enforce in the verifier that non-trivial types must have a non-trivial 
>> ownership modifier on any memory operations that they are involved in.
> 
> Sorry for not being explicit. I will not add new instructions, just 
> modifiers. Assuming that this is agreeable to you, I am going to prepare a 
> quick additional version of the proposal document.

That sounds great, thanks.

John.
___
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev


Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-05 Thread Michael Gottesman via swift-dev

> On Oct 4, 2016, at 1:04 PM, John McCall  wrote:
> 
>> 
>> On Sep 30, 2016, at 11:54 PM, Michael Gottesman via swift-dev 
>> > wrote:
>> 
>> The document attached below contains the first "Semantic ARC" mini proposal: 
>> the High Level ARC Memory Operations Proposal.
>> 
>> An html rendered version of this markdown document is available at the 
>> following URL:
>> 
>> https://gottesmm.github.io/proposals/high-level-arc-memory-operations.html 
>> 
>> 
>> 
>> 
>> # Summary
>> 
>> This document proposes:
>> 
>> 1. adding the `load_strong`, `store_strong` instructions to SIL. These can 
>> only
>>be used with memory locations of `non-trivial` type.
> 
> I would really like to avoid using the word "strong" here.  Under the current 
> proposal, these instructions will be usable with arbitrary non-trivial types, 
> not just primitive class references.  Even if you think of an aggregate that 
> happens to contain one or more strong references as some sort of aggregate 
> strong reference (which is questionable but not completely absurd), we 
> already have loadable non-strong class references that this operation would 
> be usable with, like native unowned references.  "load_strong %0 : 
> $*@sil_unowned T" as an operation yielding a scalar "@sil_unowned T" is 
> ridiculous, and it will only get more ridiculous when we eventually allow 
> this operation to work with types that are currently address-only, like weak 
> references.
> 
> Brainstorming:
> 
> Something like load_copy and store_copy would be a bit unfortunate, since 
> store_copy doesn't actually copy the source operand and we want to have a 
> load_copy [take].
> 
> load_value and store_value seem excessively generic.  It's not like 
> non-trivial types aren't values.
> 
> One question that comes to mind: do we actually need new instructions here 
> other than for staging purposes?  We don't actually need new instructions for 
> pseudo-linear SIL to work; we just need to say that we only enforce 
> pseudo-linearity for non-trivial types.
> 
> If we just want the instruction to be explicit about ownership so that we can 
> easily distinguish these cases, we can make the rule always explicit, e.g.:
>   load [take] %0 : $*MyClass
>   load [copy] %0 : $*MyClass
>   load [trivial] %0 : $*Int
> 
>   store %0 to [initialization] %1 : $*MyClass
>   store %0 to [assignment] %1 : $*MyClass
>   store %0 to [trivial] %1 : $*Int
> 
> John.

The reason why I originally suggested to go the load_strong route is that we 
already have load_weak, load_unowned instructions. If I could add a load_strong 
instruction, then it would make sense to assign an engineer to do a pass over 
all 3 of these instructions and combine them into 1 load instruction. That is, 
first transform into a form amenable for canonicalization and then canonicalize 
all at once.

As you pointed out, both load_unowned and load_weak involve representation 
changes in type (for instance the change of weak pointers to Optional). Such 
a change would be against the "spirit" of a load instruction to perform such 
representation changes versus ownership changes.

In terms of the properties that we actually want here, what is important is 
that we can verify that no non-trivially typed values are loaded in an unsafe 
unowned manner. That can be done also with ownership flags on load/store.

Does this sound reasonable:

1. We introduce two enums that define memory ownership changes, one for load 
and one for store. Both of these enums will contain a [trivial] ownership.
2. We enforce in the verifier that non-trivial types must have a non-trivial 
ownership modifier on any memory operations that they are involved in.

Michael

> 
>> 2. banning the use of `load`, `store` on values of `non-trivial` type.
>> 
>> This will allow for:
>> 
>> 1. eliminating optimizer miscompiles that occur due to releases being moved 
>> into
>>the region in between a `load`/`retain`, `load`/`release`,
>>`store`/`release`. (For a specific example, see the appendix).
>> 2. modeling `load`/`store` as having `unsafe unowned` ownership semantics. 
>> This
>>will be enforced via the verifier.
>> 3. more aggressive ARC code motion.
>> 
>> # Definitions
>> 
>> ## load_strong
>> 
>> We propose three different forms of load_strong differentiated via flags. 
>> First
>> define `load_strong` as follows:
>> 
>> %x = load_strong %x_ptr : $*C
>> 
>>   =>
>> 
>> %x = load %x_ptr : $*C
>> retain_value %x : $C
>> 
>> Then define `load_strong [take]` as:
>> 
>> %x = load_strong [take] %x_ptr : $*Builtin.NativeObject
>> 
>>   =>
>> 
>> %x = load %x_ptr : $*Builtin.NativeObject
>> 
>> **NOTE** `load_strong [take]` implies that the loaded from memory location no
>> longer owns the result object (i.e. a take is a move). Loading from the 
>> memory
>> location 

Re: [swift-dev] [semantic-arc][proposal] High Level ARC Memory Operations

2016-10-04 Thread John McCall via swift-dev

> On Sep 30, 2016, at 11:54 PM, Michael Gottesman via swift-dev 
>  wrote:
> 
> The document attached below contains the first "Semantic ARC" mini proposal: 
> the High Level ARC Memory Operations Proposal.
> 
> An html rendered version of this markdown document is available at the 
> following URL:
> 
> https://gottesmm.github.io/proposals/high-level-arc-memory-operations.html 
> 
> 
> 
> 
> # Summary
> 
> This document proposes:
> 
> 1. adding the `load_strong`, `store_strong` instructions to SIL. These can 
> only
>be used with memory locations of `non-trivial` type.

I would really like to avoid using the word "strong" here.  Under the current 
proposal, these instructions will be usable with arbitrary non-trivial types, 
not just primitive class references.  Even if you think of an aggregate that 
happens to contain one or more strong references as some sort of aggregate 
strong reference (which is questionable but not completely absurd), we already 
have loadable non-strong class references that this operation would be usable 
with, like native unowned references.  "load_strong %0 : $*@sil_unowned T" as 
an operation yielding a scalar "@sil_unowned T" is ridiculous, and it will only 
get more ridiculous when we eventually allow this operation to work with types 
that are currently address-only, like weak references.

Brainstorming:

Something like load_copy and store_copy would be a bit unfortunate, since 
store_copy doesn't actually copy the source operand and we want to have a 
load_copy [take].

load_value and store_value seem excessively generic.  It's not like non-trivial 
types aren't values.

One question that comes to mind: do we actually need new instructions here 
other than for staging purposes?  We don't actually need new instructions for 
pseudo-linear SIL to work; we just need to say that we only enforce 
pseudo-linearity for non-trivial types.

If we just want the instruction to be explicit about ownership so that we can 
easily distinguish these cases, we can make the rule always explicit, e.g.:
  load [take] %0 : $*MyClass
  load [copy] %0 : $*MyClass
  load [trivial] %0 : $*Int

  store %0 to [initialization] %1 : $*MyClass
  store %0 to [assignment] %1 : $*MyClass
  store %0 to [trivial] %1 : $*Int

John.

> 2. banning the use of `load`, `store` on values of `non-trivial` type.
> 
> This will allow for:
> 
> 1. eliminating optimizer miscompiles that occur due to releases being moved 
> into
>the region in between a `load`/`retain`, `load`/`release`,
>`store`/`release`. (For a specific example, see the appendix).
> 2. modeling `load`/`store` as having `unsafe unowned` ownership semantics. 
> This
>will be enforced via the verifier.
> 3. more aggressive ARC code motion.
> 
> # Definitions
> 
> ## load_strong
> 
> We propose three different forms of load_strong differentiated via flags. 
> First
> define `load_strong` as follows:
> 
> %x = load_strong %x_ptr : $*C
> 
>   =>
> 
> %x = load %x_ptr : $*C
> retain_value %x : $C
> 
> Then define `load_strong [take]` as:
> 
> %x = load_strong [take] %x_ptr : $*Builtin.NativeObject
> 
>   =>
> 
> %x = load %x_ptr : $*Builtin.NativeObject
> 
> **NOTE** `load_strong [take]` implies that the loaded from memory location no
> longer owns the result object (i.e. a take is a move). Loading from the memory
> location again without reinitialization is illegal.
> 
> Next we provide `load_strong [guaranteed]`:
> 
> %x = load_strong [guaranteed] %x_ptr : $*Builtin.NativeObject
> ...
> fixLifetime(%x)
> 
>   =>
> 
> %x = load %x_ptr : $*Builtin.NativeObject
> ...
> fixLifetime(%x)
> 
> `load_strong [guaranteed]` implies that in the region before the fixLifetime,
> the loaded object is guaranteed semantically to remain alive. The fixLifetime
> communicates to the optimizer the location up to which the value's lifetime is
> guaranteed to live. An example of where this construct is useful is when one 
> has
> a let binding to a class instance `c` that contains a let field `f`. In that
> case `c`'s lifetime guarantees `f`'s lifetime.
> 
> ## store_strong
> 
> Define a store_strong as follows:
> 
> store_strong %x to %x_ptr : $*C
> 
>=>
> 
> %old_x = load %x_ptr : $*C
> store %new_x to %x_ptr : $*C
> release_value %old_x : $C
> 
> *NOTE* store_strong is defined as a consuming operation. We also provide
> `store_strong [init]` in the case where we know statically that there is no
> previous value in the memory location:
> 
> store_strong %x to [init] %x_ptr : $*C
> 
>=>
> 
> store %new_x to %x_ptr : $*C
> 
> # Implementation
> 
> ## Goals
> 
> Our implementation strategy goals are:
> 
> 1. zero impact on other compiler developers until the feature is fully
>developed. This implies all work will be done behind a flag.
> 2. separation of feature