* add the StaticInitCloner utility
* remove bridging of `copyStaticInitializer` and `createStaticInitializer`
* add `Context.mangleOutlinedVariable` and `Context.createGlobalVariable`
Before deleting a load, simply rewrite its debug info to refer to the
loaded address:
%l = load %a
debug_value %l, loc0, scope0, var0
--->
debug_value %a, loc0, scope0, var0, expr op_deref
%l = load %a
Note that alloc_stack addresses do not require the addition of
op_deref because they are already special-cased in IRGen.
This will be called from salvageDebugInfo when it is supported by
optmizations. Until then, it is only called selectively at -Onone.
This is a fundamental abstraction over loads. It was in
OwnershipOptUtils because load_borrow happens to be restricted by
OSSA. Passes should not include OwnershipOptUtils just because they
work with loads.
This reverts commit 224674cad1.
Originally, I made this change since we were going to follow the AST in a strict
way in terms of what closures are considered escaping or not from a diagnostics
perspective. Upon further investigation I found that we actually do something
different for inout escaping semantics and by treating the AST as the one point
of truth, we are being inconsistent with the rest of the compiler. As an
example, the following code is considered by the compiler to not be an invalid
escaping use of an inout implying that we do not consider the closure to be
escaping:
```
func f(_ x: inout Int) {
let g = {
_ = x
}
}
```
in contrast, a var is always considered to be an escape:
```
func f(_ x: inout Int) {
var g = {
_ = x
}
}
test2.swift:3:13: error: escaping closure captures 'inout' parameter 'x'
var g = {
^
test2.swift:2:10: note: parameter 'x' is declared 'inout'
func f(_ x: inout Int) {
^
test2.swift:4:11: note: captured here
_ = x
^
```
Of course, if we store the let into memory, we get the error one would expect:
```
var global: () -> () = {}
func f(_ x: inout Int) {
let g = {
_ = x
}
global = g
}
test2.swift:4:11: error: escaping closure captures 'inout' parameter 'x'
let g = {
^
test2.swift:3:10: note: parameter 'x' is declared 'inout'
func f(_ x: inout Int) {
^
test2.swift:5:7: note: captured here
_ = x
^
```
By reverting to the old behavior where allocbox to stack ran early, noncopyable
types now have the same sort of semantics: let closures that capture a
noncopyable type that do not on the face of it escape are considered
non-escaping, while if the closure is ever stored into memory (e.x.: store into
a global, into a local var) or escapes, we get the appropriate escaping
diagnostics. E.x.:
```
public struct E : ~Copyable {}
public func borrowVal(_ e: borrowing E) {}
public func consumeVal(_ e: consuming E) {}
func f1() {
var e = E()
// Mutable borrowing use of e. We can consume e as long as we reinit at end
// of function. We don't here, so we get an error.
let c1: () -> () = {
borrowVal(e)
consumeVal(e)
}
// Mutable borrowing use of e. We can consume e as long as we reinit at end
// of function. We do do that here, so no error.
let c2: () -> () = {
borrowVal(e)
consumeVal(e)
e = E()
}
}
```
* move the apply of partial_apply transformation from simplify-apply to simplify-partial_apply
* delete dead partial_apply instructions
* devirtualize apply, try_apply and begin_apply
This allows to run the NamedReturnValueOptimization only late in the pipeline.
The optimization shouldn't be done before serialization, because it might prevent predictable memory optimizations in the caller after inlining.
It converts a lazily initialized global to a statically initialized global variable.
When this pass runs on a global initializer `[global_init_once_fn]` it tries to create a static initializer for the initialized global.
```
sil [global_init_once_fn] @globalinit {
alloc_global @the_global
%a = global_addr @the_global
%i = some_const_initializer_insts
store %i to %a
}
```
The pass creates a static initializer for the global:
```
sil_global @the_global = {
%initval = some_const_initializer_insts
}
```
and removes the allocation and store instructions from the initializer function:
```
sil [global_init_once_fn] @globalinit {
%a = global_addr @the_global
%i = some_const_initializer_insts
}
```
The initializer then becomes a side-effect free function which let's the builtin-simplification remove the `builtin "once"` which calls the initializer.
Now that we handle inlined global initializers in LICM, CSE and the StringOptimization, we don't need to have a separate mid-level inliner pass, which treats global accessors specially.
A pass is skipped if no other pass changed the function since the previous run of the same pass.
Don't do this is if a pass depends on the function bodies of called functions, e.g. the inliner.
Other passes might change the callees, e.g. function signature opts, which makes it worth to run the inliner
again, even if the function itself didn't change.
Specifically, we already have the appropriate semantics for arguments captured
by escaping closures but in certain cases allocbox to stack is able to prove
that the closure doesn’t actually escape. This results in the capture being
converted into a non-escaping SIL form. This then causes the move checker to
emit the wrong kind of error.
The solution is to create an early allocbox to stack that doesn’t promote move
only types in boxes from heap -> stack if it is captured by an escaping closure
but does everything else normally. Then once the move checking is completed, we
run alloc box to stack an additional time to ensure that we keep the guarantee
that heap -> stack is performed in those cases.
rdar://108905586
Optimizations can rely on alias analysis to know that an in-argument (or parts of it) is not actually read.
We have to do the same in the verifier: if alias analysis says that an in-argument is not read, there is no need that the memory location is initialized.
Fixes a false verifier error.
rdar://106806899
to work with aggregates containing unknown values. Such aggregates
can be generated when an instruction is skipped during constant
evaluation and its results are used to create a struct.
Linear maps are captured in vjp routine via callee-guaranteed partial apply and are passed as @owned references to the enclosing pullback that finally consumes them. Necessary retains are inserted by a partial apply forwarder.
However, this is not the case when the function being differentiated contains loops as heap-allocated context is used and bare pointer is captured by the pullback partial apply. As a result, partial apply forwarder does not retain the linear maps that are owned by a heap-allocated context, however, they are still treated as @owned references and therefore are released in the pullback after the first call. As a result, subsequent pullback calls release linear maps and we'd end with possible use-after-free.
Ensure we retain values when we load values from the context.
Reproducible only when:
* Loops (so, heap-allocated context)
* Pullbacks of thick functions (so context is non-zero)
* Multiple pullback calls
* Some cleanup while there
Fixes#64257
Previously, the utility bailed out on lexical lifetimes because it
didn't respect deinit barriers. Here, deinit barriers are found and
added to liveness if the value is lexical. This enables copies to be
propagated without hoisting destroys over deinit barriers.
rdar://104630103
Use BasicBlockBitfield to record per-block liveness state. This has
been the intention since BasicBlockBitfield was first introduced.
Remove the per-field bitfield from PrunedLiveBlocks. This
(re)specializes the data structure for scalar liveness and drastically
simplifies the implementation.
This utility is fundamental to all ownership utilities. It will be on
the critical path in many areas of the compiler, including at
-Onone. It needs to be minimal and as easy as possible for compiler
engineers to understand, investigate, and debug.
This is in preparation for fixing bugs related to multi-def liveness
as used by the move checker.