The new alloc_pack_metadata and dealloc_pack_metadata are inserted as
part of IRGen lowering. The former indicates that the next instruction
might result in on-stack pack metadata being emitted. The latter
indicates that this is the position at which metadata emitted on behalf
of its operand should be cleaned up.
User code should not be diagnosed as "unreachable" by the SIL optimizer when
the no-return function that made the code unreachable is a compiler inserted
call to `_diagnoseUnavailableCodeReached()`.
Part of rdar://107388493
When eliminating lexical lifetimes, also eliminate the lifetimes
introduced by move_values in addition to those introduced by
begin_borrow and communicated via alloc_stack.
This is used to teach the checker that the thing being checked is supposed to be
uninitialized at the mark_must_check point so that we don't put a destroy_addr
there.
The way this is implemented is that we always initially add
assignable_but_not_consumable but in DI once we discover that the assign we are
guarding is an init, we convert the assignable to its initable variant.
rdar://106525988
This is in preparation for wiring up debug info support for noncopyable
values. Originally this flag name made sense since it was set when we performed
consume operator checking. Now I am going to use it for noncopyable types as
well. I think the new name uses_moveable_value_debuginfo actually describes what
the flag is supposed to do, tell IRGen that the value may be moved since it
needs to use moveable value debug info emission.
`isSuccessorBlock` and `isPredecessorBlock` are dangerous because they can easily lead to quadratic behavior.
Fortunately they are not used anywhere (except in one place for verification, which I rewrote).
CSE relies on OSSA RAUW for lifetime extension when replacing a redundant instruction.
OSSA RAUW however does not handle lifetime extension for escaped base values.
Values escape from ownership via pointer escape, bitwise escape, forwarding unowned operations
and have none or unowned ownership. For all such values do not look through ownership instructions
while determining equality. It is possible to CSE such values with equivalent operands, because the
operand use guarantees lifetime of the base operand.
It's need to correctly maintain dependencies from an open-existential instruction to a `keypath` instruction which uses the opened type.
Fixes a SILVerifier crash.
rdar://105517521
Although nonescaping closures are representationally trivial pointers to their
on-stack context, it is useful to model them as borrowing their captures, which
allows for checking correct use of move-only values across the closure, and
lets us model the lifetime dependence between a closure and its captures without
an ad-hoc web of `mark_dependence` instructions.
During ownership elimination, We eliminate copy/destroy_value instructions and
end the partial_apply's lifetime with an explicit dealloc_stack as before,
for compatibility with existing IRGen and non-OSSA aware passes.
This is used to model global_addr/ref_element_addr/escaping closure captures
where we do not want to allow the user to consume the memory (leaving the memory
in a potentially uninitialized state), but we do want to allow for the user to
assign over the memory all at once.
Consider a situation like the following:
```
@_moveOnly
struct FileDescriptor {
var state: CInt = ...
}
final class Klass {
var descriptor = ...
}
func consumeDescriptor(_ x: __owned FileDescriptor) {}
// Eventually both of these will point at the same class.
var globalKlass = ...
var globalKlass2 = ...
func memoryUnsafe() {
consumeDescriptor(globalKlass.descriptor)
consumeDescriptor(globalKlass2.descriptor)
}
func callMemoryUnsafe() {
globalKlass2 = globalKlass()
memoryUnsafe()
}
```
Notice how in the above in memoryUnsafe, locally the compiler has no way of
knowing that globalKlass and globalKlass2 actually point at the same class and
thus we are attempting to consume the same descriptor twice. This is even
allowed by exclusivity. If descriptor was not move only, this would be safe
since we would just copy the value when we consume it. But b/c we ar3e using
move only, we must take from the memory.
This fits the name of the check better. The reason I am doing this renaming is
b/c I am going to add a nonconsumable but assignable check for
global_addr/ref_element_addr/captures with var semantics.
This reflects better the true meaning of this check which is that a value marked
with this check cannot be consumed on its boundary at all (when performing
let/var checking) and cannot be assigned over when performing var checking.
This instruction can be inserted by Onone optimizations as a replacement for deleted instructions to
ensure that it's possible to single step on its location.
This allows dynamically indexing into tuples. IRGen not yet
implemented.
I think I'm going to need a type_refine_addr instruction in
order to handle substitutions into the operand type that
eliminate the outer layer of tuple-ness. Gonna handle that
in a follow-up commit.
Having added these, I'm not entirely sure we couldn't just use
alloc_stack and dealloc_stack. Well, if we find ourselves adding
a lot of redundancy with those instructions (e.g. around DI), we
can always go back and rip these out.
These APIs are essential for complete OSSA liveness analysis. The
existing ad-hoc OSSA logic always misses some of the cases handled by
these new utilities. We need to start replacing that ad-hoc logic with
new utilities built on top of these APIs to define away potential
latent bugs.
Add FIXMEs to the inverse API: visitAdjacentBorrowsOfPhi. It should
probably be redesigned in terms of these new APIs.
Factors a mess of code in MemAccessUtils to handle forwarding
instruction types into a simpler utility. This utility is also needed
for ownership APIs, which need to be extended to handle these cases.
I've also fixed this so that it should work on instructions that
define multiple values. Someday we'll change all the open_existential
instructions to produce different values for the type dependency and
the value result; today is not that day, though.
Add TermInst::forwardedOperand.
Add SILArgument::forwardedTerminatorResultOperand. This API will be
moved into a proper TerminatorResult abstraction.
Remove getSingleTerminatorOperand, which could be misused because it's
not necessarilly forwarding ownership.
Remove the isTransformationTerminator API, which is not useful or well
defined.
Rewrite several instances of complex logic to handle block arguments
with the simple terminator result API. This defines away potential
bugs where we don't detect casts that perform implicit conversion.
Replace uses of the SILPhiArgument type and code that explicitly
handle block arguments. Control flow is irrelevant in these
situations. SILPhiArgument needs to be deleted ASAP. Instead, use
simple APIs like SILArgument::isTerminatorResult(). Eventually this
will be replaced by a TerminatorResult type.
Add `deletableInstructions()` and `reverseDeletableInstructions()` in SILBasicBlock.
It allows deleting instructions while iterating over all instructions of the block.
This is a replacement for `InstructionDeleter::updatingRange()`.
It's a simpler implementation than the existing `UpdatingListIterator` and `UpdatingInstructionIteratorRegistry`, because it just needs to keep the prev/next pointers for "deleted" instructions instead of the iterator-registration machinery.
It's also safer, because it doesn't require to delete instructions via a specific instance of an InstructionDeleter (which can be missed easily).
Instead of setting the parent pointer to null, set the `lastInitializedBitfieldID` to -1.
This allows to keep the parent block information, even when an instruction is removed from it's list.