First restore the basic PrunedLiveness abstraction to its original
intention. Move code outside of the basic abstraction that polutes the
abstraction and is fundamentally wrong from the perspective of the
liveness abstraction.
Most clients need to reason about live ranges, including the def
points, not just liveness based on use points. Add a PrunedLiveRange
layer of types that understand where the live range is
defined. Knowing where the live range is defined (the kill set) helps
reliably check that arbitrary points are within the boundary. This
way, the client doesn't need to be manage this on its own. We can also
support holes in the live range for non-SSA liveness. This makes it
safe and correct for the way liveness is now being used. This layer
safety handles:
- multiple defs
- instructions that are both uses and defs
- dead values
- unreachable code
- self-loops
So it's no longer the client's responsibility to check these things!
Add SSAPrunedLiveness and MultiDefPrunedLiveness to safely handle each
situation.
Split code that I can't figure out into
DiagnosticPrunedLiveness. Hopefully it will be deleted soon.
By using the keyword instead of the function, we actually get a much simpler
implementation since we avoid all of the machinery of SILGenApply. Given that we
are going down that path, I am removing the old builtin implementation since it
is dead code.
The reason why I am removing this now is that in a subsequent commit, I want to
move all of the ownership checking passes to run /before/ mandatory inlining. I
originally placed the passes after mandatory inlining since the function version
of the move keyword was transparent and needing to be inlined before we could
process it. Since we use the keyword now, that is no longer an issue.
Add ScopedAddressOperand and ScopedAddressValue abstraction utilities
Introduce verification for store_borrow to validate its uses are correctly enclosed in their scope.
Include end_borrow/end_access as implicit uses while validating a borrow introducer
Add flow sensitive verifier rule for store_borrow/end_borrow pair
Make sure store_borrow is always to an alloc_stack
Make sure uses to store borrow location are via its return address only
Require that lexical borrows of values which are instances of some
SILBoxType guard the lifetime of vars or closure captures; that's to
say, of AllocBoxInsts and SILFunctionArguments.
Look through a few instructions types to find those:
- copy_value - inserted by SILGen
- begin_borrow - inserted during inlining
- mark_uninitialized - inserted by SILGen
This is already an invariant for copy_value and now that I am implementing move
semantics for addresses, it makes sense to ban those in a similar
way. Specifically, we are enforcing the invariant that move only types are
allowed to be copied in Raw SIL, but not in later parts of SIL. For those who
are unaware, we then run checkers later to validate that we preserve ownership
semantics.
We had two notions of canonical types, one is the structural property
where it doesn't contain sugared types, the other one where it does
not contain reducible type parameters with respect to a generic
signature.
Rename the second one to a 'reduced type'.
Andy some time ago already created the new API but didn't go through and update
the old occurences. I did that in this PR and then deprecated the old API. The
tree is clean, so I could just remove it, but I decided to be nicer to
downstream people by deprecating it first.
I also created a SILType::isMoveOnly() helper that returns true if a type is
a move only wrapped type or a first class move only type. The verifier check
that move only types aren't copied in canonical SIL was rewired to use that as well.
Specifically this means that rather than always being owned, we now have owned
and guaranteed versions of copyable_to_moveonlywrapper. Similar to
moveonlywrapper_to_copyable, one chooses which variant one gets by using
specific SILBuilder APIs:
create{Owned,Guaranteed}CopyableToMoveOnlyWrapperValueInst. It is still
forwarding and the rest of the forwarding APIs work as expected except that the
forwarding ownership is fixed (and an assertion will result if one attempts to
do so).
NOTE: It is assumed that trivial operands are always passed to the owned
variant.
The type checker is now permitting `@Sendable` closures whose captures
can be by-reference, because we do not error. Remove an aspirational
SIL verifier check that banned this.
These instructions have the following attributes:
1. copyably_to_moveonlywrapper takes in a 'T' and maps it to a '@moveOnly
T'. This is semantically used when initializing a new moveOnly binding from a
copyable value. It semantically destroys its input @owned value and returns a
brand new independent @owned @moveOnly value. It also is used to convert a
trivial copyable value with type 'Trivial' into an owned non-trivial value of
type '@moveOnly Trivial'. If one thinks of '@moveOnly' as a monad, this is how
one injects a copyable value into the move only space.
2. moveonlywrapper_to_copyable takes in a '@moveOnly T' and produces a new 'T'
value. This is a 'forwarding' instruction where at parse time, we only allow for
one to choose it to be [owned] or [guaranteed].
* moveonlywrapper_to_copyable [owned] is used to signal the end of lifetime of
the '@moveOnly' wrapper. SILGen inserts these when ever a move only value has
its ownership passed to a situation where a copyable value is needed. Since it
is consuming, we know that the no implicit copy checker will ensure that if we
need a copy for it, the program will emit a diagnostic.
* moveonlywrapper_to_copyable [guaranteed] is used to pass a @moveOnly T value
as a copyable guaranteed parameter with type 'T' to a function. In the case of
using no-implicit-copy checking this is always fine since no-implicit-copy is a
local pattern. This would be an error when performing no escape
checking. Importantly, this instruction also is where in the case of an
@moveOnly trivial type, we convert from the non-trivial representation to the
trivial representation.
Some important notes:
1. In a forthcoming commit, I am going to rebase the no implicit copy checker on
top of these instructions. By using '@moveOnly' in the type system, we can
ensure that later in the SIL pipeline, we can have optimizations easily ignore
the code.
2. Be aware of is that due to SILGen only emitting '@moveOnly T' along immediate
accesses to the variable and always converts to a copyable representation when
calling other code, we can simply eliminate from the IR all moveonly-ness from
the IR using a lowering pass (that I am going to upstream). In the evil scheme
we are accomplishing here, we perform lowering of trivial values right after
ownership lowering and before diagnostics to simplify the pipeline.
On another note, I also fixed a few things in SILParsing around getASTType() vs
getRawASTType().
In order to transfer debug info from `debug_value` to `alloc_stack` we need to drop `op_ref` debug expression, as the latter instruction represents the location and not the value itself. Also, fix the debug info verifier as variable type was deduced from `alloc_stack` / `alloc_box` improperly: pointer type of instruction itself was used instead of underlying object type.
The issue is only exposed when both optimizations (SIL mem2reg at least) and debug info are enabled
Resolves SR-15849
Added an AST helper in Types.h:
- isPotentiallyAnyObject()
This formalizes logic for when cast operations forward
ownership. Various OSSA optimization rely on this for
correctness. This fixes latent bugs throughout the optimizer.
I was compelled to fix this now because we want to make OSSA
optimizations across dynamic casts more aggressive. For example, we
want to optimize retain/release across enum formation.
The main point of this change is to make sure that a shared function always has a body: both, in the optimizer pipeline and in the swiftmodule file.
This is important because the compiler always needs to emit code for a shared function. Shared functions cannot be referenced from outside the module.
In several corner cases we missed to maintain this invariant which resulted in unresolved-symbol linker errors.
As side-effect of this change we can drop the shared_external SIL linkage and the IsSerializable flag, which simplifies the serialization and linkage concept.
This ensures that opened archetypes always inherit any outer generic parameters from the context in which they reside. This matters because class bounds may bind generic parameters from these outer contexts, and without the outer context you can wind up with ill-formed generic environments like
<τ_0_0, where τ_0_0 : C<T>, τ_0_0 : P>
Where T is otherwise unbound because there is no entry for it among the generic parameters of the environment's associated generic signature.
This is just a quick fix to stop us from dropping live values such as m in the
following example:
```
public func addressOnlyVarTest<T : P>(_ x: T) {
var k = x
k.doSomething()
let m = _move(k)
m.doSomething()
k = x
k.doSomething()
}
```
Before this change, we would just drop m and one wouldn't even see it in the
debugger.
I am only doing this currently for cases where when we merge at least one
alloc_stack was moved. The reason why is that to implement this correctly, I
need to use llvm.dbg.addr and changing the debug info from using
llvm.dbg.declare -> llvm.dbg.addr requires statistics and needs to be done a
little later in the swift development process. If one of these alloc_stack had
the [moved] marker attached to it, we know the user /did/ use move so they have
in a sense opted into having a move function effect its program so we are only
changing how new code appears in the debugger.
This subclass of SILArgument should be eliminated--it's not always a
phi, and whether it is a "phi argument" has nothing whatsoever to do
with the opcode. That is a property of a value's uses, not a property of the
value.
Until then, provide a logical and useful API within the type. This
often avoids the need to explicitly cast to a SILPhiArgument type and
avoids a lot of boilerplate in code that deals with phis.
Note: PhiOperand and PhiValue are improved abstractions on top of this
API. But the SILArgument-level API is still an important bridge
between SILArgument and other phi abstractions.
A serialized specialized function can reference another specialized function.
In case the other specialization is already generated by the optimizer before the de-serialization point,
we can end up that a shared_external function references a shared function. This is okay.