This allows us to hoist the error case of having a function signature with
conflicting ownership requirements into the creation of the return inst instead
of at the time of computing Operand Constraints.
This is the last part of the Operand Constraint computation that can fail that
once removed will let me use fail to mean any constriant is allowed.
I may turn this into an assert, but for now I am preserving the current behavior
albeit moving the bad behavior out of the visitor to the front of the API.
I think this assert was just testing the wrong thing. Specifically, it is trying
to say that either the given value has a consuming use or it is post-dominated
by dead end blocks.
Instead of checking that directly by using DeadEndBlocks::isDeadEnd(), it was
using a different empty check that caused the assert to actually semantically
say that:
A value must have a lifetime ending use *or* the dead end blocks analysis must
have found at least one block at all in the function that is reachable from a
function terminating terminator (e.x.: return).
This is clearly not the former and was causing the linear lifetime error to hit
this assert in certain cases.
<rdar://problem/70690127>
There are multiple reasons this is needed.
1. Most passes do not perform CFG transformations. However, we often
need to split critical edges and remember to invalidate all SIL
analyses at the end of virtually every pass. This is very innefficient
and highly bug prone.
2. Many SIL analysis algorithms needs to reason about CFG
edges. Avoiding critical edges leads to far simpler and more efficient
designs when edges can be identified by blocks.
3. Handling block arguments on conditional branches create complexity
at the lowest level of the SIL interface. This complexity is difficult
to abstract over and bleeds until any algorithm that needs to reason
about phi operands. It's far easier to work with phis if we can easily
recover the phi operand with only a reference to the predecessor
block.
4. Attempting to preserve critical edges in high and mid level IR
blocks optimizations that otherwise have no business optimizing
branches. Branch optimization should always be defered to machine
level IR where the most relevant heuristics are employed to remove
unconditional branches. If code didn't need to be placed on a critical
edges, then a branch optimization can easily remove that code from the
critical edge.
Previously, we always inferred the ownership of the switch_enum from its phi
operands. This forced us to need to model a failure to find a good
OperandOwnershipKindMap in OperandOwnership.cpp. We want to eliminate such
conditions so that we can use failing to find a constraint to mean that a value
can accept any value rather than showing a failure.
I have a need to have SwitchEnum{,Addr}Inst have different base classes
(TermInst, OwnershipForwardingTermInst). To do this I need to add a template to
SwitchEnumInstBase so I can switch that BaseTy. Sadly since we are using
SwitchEnumInstBase as an ADT type as well as an actual base type for
Instructions, this is impossible to do without introducing a template in a ton
of places.
Rather than doing that, I changed the code that was using SwitchEnumInstBase as
an ADT to instead use a proper ADT SwitchEnumBranch. I am happy to change the
name as possible see fit (maybe SwitchEnumTerm?).
This makes it clearer that isConsumingUse() is not an owned oriented API and
returns also for instructions that end the lifetime of guaranteed values like
end_borrow.
`Builtin.createAsyncTask` takes flags, an optional parent task, and an
async/throwing function to execute, and passes it along to the
`swift_task_create_f` entry point to create a new (potentially child)
task, returning the new task and its initial context.
Implement a new builtin, `cancelAsyncTask()`, to cancel the given
asynchronous task. This lowers down to a call into the runtime
operation `swift_task_cancel()`.
Use this builtin to implement Task.Handle.cancel().
Rather than produce an "unowned" result from `getCurrentAsyncTask()`,
take advantage of the fact that the task is effectively guaranteed in
the scope. Do so be returning it as "unowned", and push an
end_lifetime cleanup to end the lifetime. This eliminates unnecessary
ref-count traffic as well as introducing another use of unowned.
Approach is thanks to Michael Gottesman, bugs are mine.
This introduces a new builtin, `getCurrentAsyncTask()`, that produces a
reference to the current task. This builtin can only be used within
`async` functions, and IR generation merely grabs the task argument
and packages it up.
The type of this function is `() -> Builtin.NativeObject`, because we
don't currently have a Swift-level representation of tasks, and can
probably handle everything through builtins or runtime calls.
Treating a trivial type as having ownership seems only to confuse the ownership verifier.
The structural property we're trying to enforce here (that a continuation is always
consumed by an `await` locally) can be enforced by flow-sensitive verification without
ownership.
This updates how we model reborrow's lifetimes for ownership verification.
Today we follow and combine a borrow's lifetime through phi args as well.
Owned values lifetimes end at a phi arg. This discrepency in modeling
lifetimes leads to the OwnershipVerifier raising errors incorrectly for
cases such as this, where the borrow and the base value do not dominate
the end_borrow:
bb0:
cond_br undef, bb1, bb2
bb1:
%copy0 = copy_value %0
%borrow0 = begin_borrow %copy0
br bb3(%borrow0, %copy0)
bb2:
%copy1 = copy_value %1
%borrow1 = begin_borrow %copy1
br bb3(%borrow1, %copy1)
bb3(%borrow, %baseVal):
end_borrow %borrow
destroy_value %baseVal
This PR adds a new ReborrowVerifier. The ownership verifier collects borrow's
lifetime ending users and populates the worklist of the ReborrowVerifier
with reborrows and the corresponding base value.
ReborrowVerifier then verifies that the lifetime of the reborrow is
within the lifetime of the base value.
Provide a mechanism to gradually migrate unit tests away from allowing
critical edges via -allow-critical-edges=false.
This will be the default in OSSA very soon, and will hopefully become
the default eventually for all SIL stages.
Note that not all required optimization pass changes have been
committed yet. I have pending changes in:
- SimplifyCFG
- SILCloner subclasses
- EagerSpecializer
- ArraySpecialization
- LoopUtils
- LoopRotate
There are multiple reasons we need to disallow critical edges:
1. Most passes do not perform CFG transformations. However, we often
need to split critical edges and remember to invalidate all SIL
analyses at the end of virtually every pass. This is very innefficient
and highly bug prone.
2. Many SIL analysis algorithms needs to reason about CFG
edges. Avoiding critical edges leads to far simpler and more efficient
designs when edges can be identified by blocks.
3. Handling block arguments on conditional branches create complexity
at the lowest level of the SIL interface. This complexity is difficult
to abstract over and bleeds until any algorithm that needs to reason
about phi operands. It's far easier to work with phis if we can easily
recover the phi operand with only a reference to the predecessor
block.
4. Attempting to preserve critical edges in high and mid level IR
blocks optimizations that otherwise have no business optimizing
branches. Branch optimization should always be defered to machine
level IR where the most relevant heuristics are employed to remove
unconditional branches. If code didn't need to be placed on a critical
edges, then a branch optimization can easily remove that code from the
critical edge.
This instructions ensures that all instructions, which need to run on the specified executor actually run on that executor.
For details see the description in SIL.rst.
When casting from existentials to class - and vice versa - it can happen that a cast is not RC identity preserving (because of potential bridging).
This also affects mayRelease() of such cast instructions.
For details see the comments in SILDynamicCastInst::isRCIdentityPreserving().
This change also includes some refactoring: I centralized the logic in SILDynamicCastInst::isRCIdentityPreserving().
rdar://problem/70454804
When lowering the type for `@objc` entry points of async declarations, restore
the original ObjC signature with the completion handler argument in the lowered
SIL type.