The reason why I am doing this is that currently checked_cast_br of an AnyObject
may return a different value due to boxing. As an example, this can occur when
performing a checked_cast_br of a __SwiftValue(AnyHashable(Klass())).
To model this in SIL, I added to OwnershipForwardingMixin a bit of information
that states if the instruction directly forwards ownership with a default value
of true. In checked_cast_br's constructor though I specialize this behavior if
the source type is AnyObject and thus mark the checked_cast_br as not directly
forwarding its operand. If an OwnershipForwardingMixin directly forwards
ownership, one can assume that if it forwards ownership, it will always do so in
a way that ensures that each forwarded value is rc-identical to whatever result
it algebraically forwards ownership to. So in the context of checked_cast_br, it
means that the source value is rc-identical to the argument of the success and
default blocks.
I added a verifier check to CheckedCastBr that makes sure that it can never
forward guaranteed ownership and have a source type of AnyObject.
In SemanticARCOpts, I modified the code that builds extended live ranges of
owned values (*) to check if a OwnershipForwardingMixin is directly
forwarding. If it is not directly forwarding, then we treat the use just as an
unknown consuming use. This will then prevent us from converting such an owned
value to guaranteed appropriately in such a case.
I also in SILGen needed to change checked_cast_br emission in SILGenBuilder to
perform an ensurePlusOne on the input operand (converting it to +1 with an
appropriate cleanup) if the source type is an AnyObject. I found this via the
verifier check catching this behavior from SILGen when compiling libswift. This
just shows how important IR verification of invariants can be.
(*) For those unaware, SemanticARCOpts contains a model of an owned value and
all forwarding uses of it called an OwnershipLiveRange.
rdar://85710101
Rerun RLE with cutting off the base address of loads at `ref_element/tail_addr [immutable]`. This increases the chance of catching loads of immutable COW class properties or elements.
* add the IntegerLiteralInst + the Builder function to create it
* add Value.nonDebugUses
* add a general utility Sequence.isEmpty
* add PassContext.replaceAllUses
* add a function to erase all `debug_value` uses in PassContext.erase
Required for UnsafeRawPointer.withMemoryReboud(to:).
%out_token = rebind_memory %0 : $Builtin.RawPointer to %in_token
%0 must be of $Builtin.RawPointer type
%in_token represents a cached set of bound types from a prior memory state.
%out_token is an opaque $Builtin.Word representing the previously bound
types for this memory region.
This instruction's semantics are identical to ``bind_memory``, except
that the types to which memory will be bound, and the extent of the
memory region is unknown at compile time. Instead, the bound-types are
represented by a token that was produced by a prior memory binding
operation. ``%in_token`` must be the result of bind_memory or
This is just an initial prototype for people to play with. It is as always
behind the -enable-experimental-move-only flag.
NOTE: In this PR I implemented this only for 'local let' like things (local
lets/params). I did not implement in this PR support for local var and haven't
done anything with class ivars or globals.
rdar://83957028
The key thing is that the move checker will not consider the explicit copy value
to be a copy_value that can be rewritten, ensuring that any uses of the result
of the explicit copy_value (consuming or other wise) are not checked.
Similar to the _move operator I recently introduced, this is a transparent
function so we can perform one level of specialization and thus at least be
generic over all concrete types.
Define the possible runtime effects of an instruction in an enum `RuntimeEffect`.
Add a new utility `swift:getRuntimeEffect` to estimate the runtime effects of an instruction.
Also, add a mechanism to validate the correctness of the analysis in IRGen: annotate all runtime functions in RuntimeFunctions.def with the actual effect what the runtime function has or can have. Then check if the effects of emitted runtime functions for an instruction match what `getRuntimeEffect` predicts.
This check is only enabled on demand by defining the CHECK_RUNTIME_EFFECT_ANALYSIS macro in RuntimeEffect.h
This patch introduces a new stdlib function called _move:
```Swift
@_alwaysEmitIntoClient
@_transparent
@_semantics("lifetimemanagement.move")
public func _move<T>(_ value: __owned T) -> T {
#if $ExperimentalMoveOnly
Builtin.move(value)
#else
value
#endif
}
```
It is a first attempt at creating a "move" function for Swift, albeit a skleton
one since we do not yet perform the "no use after move" analysis. But this at
leasts gets the skeleton into place so we can built the analysis on top of it
and churn tree in a manageable way. Thus in its current incarnation, all it does
is take in an __owned +1 parameter and returns it after moving it through
Builtin.move.
Given that we want to use an OSSA based analysis for our "no use after move"
analysis and we do not have opaque values yet, we can not supporting moving
generic values since they are address only. This has stymied us in the past from
creating this function. With the implementation in this PR via a bit of
cleverness, we are now able to support this as a generic function over all
concrete types by being a little clever.
The trick is that when we transparent inline _move (to get the builtin), we
perform one level of specialization causing the inlined Builtin.move to be of a
loadable type. If after transparent inlining, we inline builtin "move" into a
context where it is still address only, we emit a diagnostic telling the user
that they applied move to a generic or existential and that this is not yet
supported.
The reason why we are taking this approach is that we wish to use this to
implement a new (as yet unwritten) diagnostic pass that verifies that _move
(even for non-trivial copyable values) ends the lifetime of the value. This will
ensure that one can write the following code to reliably end the lifetime of a
let binding in Swift:
```Swift
let x = Klass()
let _ = _move(x)
// hypotheticalUse(x)
```
Without the diagnostic pass, if one were to write another hypothetical use of x
after the _move, the compiler would copy x to at least hypotheticalUse(x)
meaning the lifetime of x would not end at the _move, =><=.
So to implement this diagnostic pass, we want to use the OSSA infrastructure and
that only works on objects! So how do we square this circle: by taking advantage
of the mandatory SIL optimzier pipeline! Specifically we take advantage of the
following:
1. Mandatory Inlining and Predictable Dead Allocation Elimination run before any
of the move only diagnostic passes that we run.
2. Mandatory Inlining is able to specialize a callee a single level when it
inlines code. One can take advantage of this to even at -Onone to
monomorphosize code.
and then note that _move is such a simple function that predictable dead
allocation elimination is able to without issue eliminate the extra alloc_stack
that appear in the caller after inlining without issue. So we (as the tests
show) get SIL that for concrete types looks exactly like we just had run a
move_value for that specific type as an object since we promote away the
stores/loads in favor of object operations when we eliminate the allocation.
In order to prevent any issue with this being used in a context where multiple
specializations may occur, I made the inliner emit a diagnostic if it inlines
_move into a function that applies it to an address only value. The diagnostic
is emitted at the source location where the function call occurs so it is easy
to find, e.x.:
```
func addressOnlyMove<T>(t: T) -> T {
_move(t) // expected-error {{move() used on a generic or existential value}}
}
moveonly_builtin_generic_failure.swift:12:5: error: move() used on a generic or existential value
_move(t)
^
```
To eliminate any potential ABI impact, if someone calls _move in a way that
causes it to be used in a context where the transparent inliner will not inline
it, I taught IRGen that Builtin.move is equivalent to a take from src -> dst and
marked _move as always emit into client (AEIC). I also took advantage of the
feature flag I added in the previous commit in order to prevent any cond_fails
from exposing Builtin.move in the stdlib. If one does not pass in the flag
-enable-experimental-move-only then the function just returns the value without
calling Builtin.move, so we are safe.
rdar://83957028
Adds two new IRGen-level builtins (one for allocating, the other for deallocating), a stdlib shim function for enhanced stack-promotion heuristics, and the proposed public stdlib functions.
Replaced a SmallSetVector worklist with a GraphNodeWorklist to avoid
infinitely pushing and popping the same value the same value into and
out from the worklist.
findInnerTransitiveUsesForAddress was incorrectly returning true for
pointer escapes.
Introduce enum AddressUseKind { NonEscaping, PointerEscape, Unknown };
Clients need to handle each of these cases differently.
Recording uses is now optional. This utility is also used simply to
check for PointerEscape. When uses are recorded, they are used to
extend a live range. There could be a large number of transitive uses
that we don't want to pass back to the caller.
Recording uses is now optional. This utility is also used simply to
check for PointerEscape. When uses are recorded, they are used to
extend a live range. There could be a large number of transitive uses
that we don't want to pass back to the caller.
This replaces the recent BorrowedAddress utility (the address may not
be borrowed!)
APIs:
AddressOwnership::hasLocalOwnershipLifetime() - convenience on top of
AccessBase::hasLocalOwnershipLifetime().
AddressOwnership::getOwnershipReferenceRoot() - convenience on top of
AccessBase::getOwnershipReferenceRoot().
AddressOwnership::findTransitiveUses() - wrapper API over the internal
helper findTransitiveUsesForAddress()
AddressOwnership::areUsesWithinLifetime() - wrapper adds a quick check
on top of BorrowedValue::areUsesWithinLifetime()
To visitExtendedScopeEndingUses.
We need to talk about the "local" scope, which does not cross
reborrows, in contrast to the extended scope. So the name was a
contradiction that could cause confusion elsewhere.
Temporary check for malformed borrow scopes. Def-use traversals expect
at least one scope-ending use. Otherwise we can end up missing the
borrow entirely and miscompiling. SIL should guarantee scope-ending
uses on all paths, but that isn't fully enforced yet.
This abstraction is heavily used to ask about the current borrow scope
enclosing some uses. Give it the functionality it needs.
Add BorrowedValue::computeLiveness(PrunedLiveness). Get the borrow
scope's live range. A trivial 4-line implementation.
Cleanup BorrowedValue::areUsesWithinScope(). Quick check to see if a set
of uses known to be dominated by the borrow are within the borrow scope.
Add BorrowedValue::hasReborrow(). Is this local borrow scope
reborrowed? If not, then it's local scope-ending operations are the
end of its required lifetime.
Simplify BorrowedValue::gatherReborrows() to use OperandOwnership::Reborrow.
Straighforward API to build pruned liveness from a single SSA value.
This 3-line function allows PrunedLiveness to be used anywhere
ValueLifetimeAnalysis was used in OSSA form. Aside from simplicity and
efficiency, the difference is that this composes with other utilities
that only care about PrunedLiveBlocks, PrunedLiveness, or
PrunesLivenessBounary independent of how those data structures were generated.
Simple wrapper to compute the boundary of PrunedLiveness.
Pervasively useful utility for working with OSSA reference lifetimes
and borrow scopes.
This can also replace the implementation in
CanonicalOSSALifetime. This will greatly simplify that utility's
logic, preparing it to handle diagnostics (like move-only SILValue
checking) and other features.
This API should not handle is phis because the phi argument's ownedhip
is independent from the phi itself. The client assumes that the
returned root is in the same lifetime/scope of the access.
Simple convenience on top of findOwnershipReferenceRoot to handle
guaranteed values forwarded through struct/tuple extract.
Note: eventually this should be handled by a ReferenceRoot abstraction
that stores the component path.
Replaces AccessStorage::isGuaranteedForFunction().
OSSA utilities need to determine whether two addresses with the same
AccessStorage are directly substitutable without "fixing-up" the OSSA
lifetime. Checking whether the access base is within a local borrow
scope is the first step.
This is a tiny routine that is useful for writing assertions to be
used for quick sanity checks without computing Dominance.
It doesn't always make sense to require a pass to maintain and pass
around Dominance just for an assert.
* ApplySite.arguments
* BasicBlock != operator
* some Function argument related properties
* Operand.isTypeDependent
* Type.isTrivial
* bridging of raw_ostream::write