This reduces the diff in between -Onone output when stripping before/after
serialization.
We support load_borrow by translating it to the load [copy] case. Specifically,
for +1, we normally perform the following transform.
store %1 to [init] %0
...
%2 = load [copy] %0
...
use(%2)
...
destroy_value %2
=>
%1a = copy_value %1
store %1 to [init] %0
...
use(%1a)
...
destroy_value %1a
We analogously can optimize load_borrow by replacing the load with a
begin_borrow:
store %1 to [init] %0
...
%2 = load_borrow %0
...
use(%2)
...
end_borrow %2
=>
%1a = copy_value %1
store %1 to [init] %0
...
%2 = begin_borrow %1a
...
use(%2)
...
end_borrow %2
destroy_value %1a
The store from outside a loop being used by a load_borrow inside a loop is a
similar transformation as the +0 version except that we use a begin_borrow
inside the loop instead of a copy_value (making it even more efficient).
I discovered this due to the mandatory inliner doing devirtualization. I ported
all of the relevant SIL tests to increase code coverage of this code when
ownership is enabled.
Just an oversight on my part. I double checked that the only remaining
difference is in how we handle checked_cast_br and switch_enum. I have a
separate patch that fixes that.
The problematic scenario is that a function receives an @in_guaranteed and @inout parameter where one is a copy of the other value. For example, when appending a container to itself.
In this case the optimization removed the copy_addr, resulting in passing the same stack location to both parameters.
rdar://problem/47632890
I am starting to use the linear lifetime checker in an optimizer role where it
no longer asserts but instead tells the optimizer pass what is needed to cause
the lifetime to be linear. To do so I need to be able to return richer
information to the caller such as whether or not a leak, double consume, or
use-after-free occurs.
Originally I wanted to try to use this code to optimize on ownership sil without
assuming verification passed (since the utility would check verification). In
the end, this was not useful.
A common pattern when working in ossa is the introduction of a copy for lifetime
reasons at a dominating point in a program with a single use that may not
post-dominate the use. This API gives a "tight lifetime" bound by returning the
blocks where the value needs to be destroyed to prevent leaks.
NOTE: I was not 100% sure if consume errors should still assert. It seems like a
double consume error would be user error, so an assert is probably fine.
It does not take ownership of its non-trivial arguments, is a trivial
function type and therefore must not be destroyed. The compiler must
make sure to extend the lifetime of non-trivial arguments beyond the
last use of the closure.
%objc = copy_value %0 : $AnObject
%closure = partial_apply [stack] [callee_guaranteed] %16(%obj) : $@convention(thin) (@guaranteed AnObject) -> ()
%closure2 = mark_dependence %closure : $@noescape @callee_guaranteed () -> () on %obj : $AnObject
%user = function_ref @useClosure : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> ()
apply %user(%closure2) : $@convention(thin) (@noescape @callee_guaranteed () -> ()) -> ()
dealloc_stack %closure : $() ->()
destroy_value %obj : $AnObject // noescape closure does not take ownership
SR-904
rdar://35590578
Specifically we add a groups of APIs for destructure operations. The destructure
helpers are a family of functions built around
emitDestructureValueOperation. These in ossa produce destructures and pass the
results off to the caller in some manner that hides the internal destructure
instruction. In non-ossa, the appropriate projections are created and passed off
to the caller.
This undoes some of Joe's work in 8665342 to add a guarantee: if an
@objc convenience initializer only calls other @objc initializers that
eventually call a designated initializer, it won't result in an extra
allocation. While Objective-C /allows/ returning a different object
from an initializer than the allocation you were given, doing so
doesn't play well with some very hairy implementation details of
compiled nib files (or NSCoding archives with cyclic references in
general).
This guarantee only applies to
(1) calling `self.init`
(2) where the delegated-to initializer is @objc
because convenience initializers must do dynamic dispatch when they
delegate, and Swift only stores allocating entry points for
initializers in a class's vtable. To dynamically find an initializing
entry point, ObjC dispatch must be used instead.
(It's worth noting that this patch does NOT check that the calling
initializer is a convenience initializer when deciding whether to use
ObjC dispatch for `self.init`. If we ever add peer delegation to
designated initializers, which is totally a valid feature, that should
use static dispatch and therefore should not go through objc_msgSend.)
This change doesn't /always/ result in fewer allocations; if the
delegated-to initializer ends up returning a different object after
all, the original allocation was wasted. Objective-C has the same
problem (one of the reasons why factory methods exist for things like
NSNumber and NSArray).
We do still get most of the benefits of Joe's original change. In
particular, vtables only ever contain allocating initializer entry
points, never the initializing ones, and never /both/ (which was a
thing that could happen with 'required' before).
rdar://problem/46823518
This lets the SIL optimizer reason about types (e.g. if a type is loadable) in specific functions.
For example, a type might be loadable in a resilient function, but not in an inlinable function.
All callers were doing the same thing here, so move it inside the
function. Also, change getRootNormalConformance(), which is deprecated,
to getRootConformance().
This is the first in a sequence of patches that implement various optimizations
to transform load [copy] into load_borrow.
The optimization works by looking for a load [copy] that:
1. Only has destroy_value as consuming users. This implies that we do not need
to pass off the in memory value at +1 and that we can use a +0 value.
2. Is loading from a memory location that is never written to or only written to
after all uses of the load [copy].
and then RAUW the load [copy] with a load_borrow and convertes the destroy_value
to end_borrow.
NOTE: I also a .def file for AccessedStorage so we can do visitors over the
kinds. The reason I want to do this is to ensure that people update these
optimizations if we add new storage kinds.
Adds memory objects and addresses to the constant interpreter, and
teaches the constant interpreter to interpret various instructions that
deal with memory and addresses.
Previously the cast optimizer bailed out on any conformance with
requirements.
We can now constant-propagate this:
```
protocol P {}
struct S<E> {
var e: E
}
extension S : P where E == Int {}
func specializeMe<T>(_ t: T) {
if let p = t as? P {
// do fast things.
}
}
specializeMe(S(e: 0))
```
This turns out to be as simple as calling the TypeChecker.
<rdar://problem/46375150> Inlining does not seem to handle
specialization properly for Data.
This enabled two SIL transformations required to optimize
the code above:
(1) The witness method call can be devirtualized.
(2) The allows expensive dynamic runtime checks such as:
unconditional_checked_cast_addr Array<UInt8> in %array : $*Array<UInt8> to ContiguousBytes in %protocol : $*ContiguousBytes
Will be converted into:
%value = init_existential_addr %existential : $*ContiguousBytes, $Array<UInt8>
store %array to %value : $*Array<UInt8>
In a previous commit, I banned in the verifier any SILValue from producing
ValueOwnershipKind::Any in preparation for this.
This change arises out of discussions in between John, Andy, and I around
ValueOwnershipKind::Trivial. The specific realization was that this ownership
kind was an unnecessary conflation of the a type system idea (triviality) with
an ownership idea (@any, an ownership kind that is compatible with any other
ownership kind at value merge points and can only create). This caused the
ownership model to have to contort to handle the non-payloaded or trivial cases
of non-trivial enums. This is unnecessary if we just eliminate the any case and
in the verifier separately verify that trivial => @any (notice that we do not
verify that @any => trivial).
NOTE: This is technically an NFC intended change since I am just replacing
Trivial with Any. That is why if you look at the tests you will see that I
actually did not need to update anything except removing some @trivial ownership
since @any ownership is represented without writing @any in the parsed sil.
rdar://46294760
We've been running doxygen with the autobrief option for a couple of
years now. This makes the \brief markers into our comments
redundant. Since they are a visual distraction and we don't want to
encourage more \brief markers in new code either, this patch removes
them all.
Patch produced by
for i in $(git grep -l '\\brief'); do perl -pi -e 's/\\brief //g' $i & done
* Remove apparently obsolete builtin functions.
- Remove s_to_u_checked_conversion and u_to_s_checked_conversion functions from builtin AST parsing, SIL/IR generation and from SIL optimisations.
* Remove apparently obsolete builtin functions - unit tests.
- Remove unit tests for SIL transformations relating to s_to_u_checked_conversion and u_to_s_checked_conversion builtin functions.
* Remove apparently obsolete builtin functions.
- Remove s_to_u_checked_conversion and u_to_s_checked_conversion functions from builtin AST parsing, SIL/IR generation and from SIL optimisations.
* Remove apparently obsolete builtin functions - unit tests.
- Remove unit tests for SIL transformations relating to s_to_u_checked_conversion and u_to_s_checked_conversion builtin functions.
When the Clang importer imports the components of a C function pointer
type, it generally translates foreign types into their native equivalents,
just for the convenience of Swift code working with those functions.
However, this translation must be unambiguously reversible, so (among
other things) it cannot do this when the native type is also a valid
foreign type. Specifically, this means that the Clang importer cannot
import ObjCBool as Swift.Bool in these positions because Swift.Bool
corresponds directly to the C type _Bool.
SIL type lowering manually reverses the type-import process using
a combination of duplicated logic and an abstraction pattern which
includes information about the original Clang type that was imported.
This abstraction pattern is generally able to tell SIL type lowering
exactly what type to reverse to. However, @convention(c) function
types may appear in positions from which it is impossible to recover
the original Clang function type; therefore the reversal must be
faithful to the proper rules. To do this we must propagate
bridgeability just as the imported would.
This reversal system is absolutely crazy, and we should really just
- record an unbridged function type for imported declarations and
- record an unbridged function type and Clang function type for
@convention (c) function types whenever we create them.
But for now, it's what we've got.
rdar://43656704