Support for @noescape SILFunctionTypes.
These are the underlying SIL changes necessary to implement the new
closure capture ABI.
Note: This includes a change to function name mangling that
primarily affects reabstraction thunks.
The new ABI will allow stack allocation of non-escaping closures as a
simple optimization.
The new ABI, and the stack allocation optimization, also require
closure context to be @guaranteed. That will be implemented as the
next step.
Many SIL passes pattern match partial_apply sequences. These all
needed to be fixed to handle the convert_function that SILGen now
emits. The conversion is now needed whenever a function declaration,
which has an escaping type, is passed into a @NoEscape argument.
In addition to supporting new SIL patterns, some optimizations like
inlining and SIL combine are now stronger which could perturb some
benchmark results.
These underlying SIL changes should be merged now to avoid conflicting
with other work. Minor benchmark discrepancies can be investigated as part of
the stack-allocation work.
* Add a noescape attribute to SILFunctionType.
And set this attribute correctly when lowering formal function types to SILFunctionTypes based on @escaping.
This will allow stack allocation of closures, and unblock a related ABI change.
* Flip the polarity on @noescape on SILFunctionType and clarify that
we don't default it.
* Emit withoutActuallyEscaping using a convert_function instruction.
It might be better to use a specialized instruction here, but I'll leave that up to Andy.
Andy: And I'll leave that to Arnold who is implementing SIL support for guaranteed ownership of thick function types.
* Fix SILGen and SIL Parsing.
* Fix the LoadableByAddress pass.
* Fix ClosureSpecializer.
* Fix performance inliner constant propagation.
* Fix the PartialApplyCombiner.
* Adjust SILFunctionType for thunks.
* Add mangling for @noescape/@escaping.
* Fix test cases for @noescape attribute, mangling, convert_function, etc.
* Fix exclusivity test cases.
* Fix AccessEnforcement.
* Fix SILCombine of convert_function -> apply.
* Fix ObjC bridging thunks.
* Various MandatoryInlining fixes.
* Fix SILCombine optimizeApplyOfConvertFunction.
* Fix more test cases after merging (again).
* Fix ClosureSpecializer. Hande convert_function cloning.
Be conservative when combining convert_function. Most of our code doesn't know
how to deal with function type mismatches yet.
* Fix MandatoryInlining.
Be conservative with function conversion. The inliner does not yet know how to
cast arguments or convert between throwing forms.
* Fix PartialApplyCombiner.
Initializers for non-fixed-layout structs that are inlinable or
are defined in a different module are treated as delegating
initializers.
Previously, only initializers containing a 'self.init' call were
delegating; initializers that assigned to 'self' were not, which
resulted in DI treating them as a root initializer where the
stored 'self' value was exploded into a series of stores to each
stored property member.
They were not resilient as a result.
Fixes <https://bugs.swift.org/browse/SR-5649>,
<rdar://problem/33767516>.
conversions that reverse an implicit conversion done to align
foreign declarations with their imported types.
For example, consider an Objective-C method that returns an NSString*:
- (nonnull NSString*) foo;
This will be imported into Swift as a method returning a String:
func foo() -> String
A call to this method will implicitly convert the result to String
behind the scenes. If the user then casts the result back to NSString*,
that would normally be compiled as an additional conversion. The
compiler cannot simply eliminate the conversion because that is not
necessarily semantically equivalent.
This peephole recognizes as-casts that immediately reverse a bridging
conversion as a special case and gives them special power to eliminate
both conversions. For example, 'foo() as NSString' will simply return
the original return value. In addition to call results, this also
applies to call arguments, property accesses, and subscript accesses.
Also, add a third [serializable] state for functions whose bodies we
*can* serialize, but only do so if they're referenced from another
serialized function.
This will be used for bodies synthesized for imported definitions,
such as init(rawValue:), etc, and various thunks, but for now this
change is NFC.
Instead of appending a character for each substitution, we now prefix the substitution with the repeat count, e.g.
AbbbbB -> A5B
The same is done for known-type substitutions, e.g.
SiSiSi -> S3i
This significantly shrinks mangled names which contain large lists of the same type, like
func foo(_ x: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int))
rdar://problem/30707433
This is in preparation for removing the +0 self hack.
This commit in more detail does the following:
1. It adds Formal Evaluation Scopes to certain places where the scopes were
missing. Specifically:
a. The SILGenLValue cases are places where we are invoking accessors. In each
one of these cases, we had a formal evaluation scope in the accessor
itself, but we did not have a scope that closed over the base access and
the accessor access. The base access is a formal evaluation in the sense
that just like with inout bases, we must create a new reference to the
base and re-destroy the base in a chain of accesses. This is to ensure
that we do not extend the lifetime of the base inappropriately.
b. The SILGenPoly case is a place where we have never properly placed a
Formal Evaluation Scope and have completely been relying on the +0 self
hack to make sure that archetype callees are properly destroyed
immediately after a +0 call.
2. It changes all of the places in SILGen that emit self to using formal access
cleanups instead of normal cleanups.
rdar://29791263
This flag is hopefully going away one day, and using it for testing
resilience is especially suspect. Just invoke the frontend directly
to build the necessary modules with -emit-module first.
Officially kick SILBoxType over to be "nominal" in its layout, with generic layouts structurally parameterized only by formal types. Change SIL to lower a capture to a nongeneric box when possible, or a box capturing the enclosing generic context when necessary.
Use a syntax that declares the layout's generic parameters and fields,
followed by the generic arguments to apply to the layout:
{ var Int, let String } // A concrete box layout with a mutable Int
// and immutable String field
<T, U> { var T, let U } <Int, String> // A generic box layout,
// applied to Int and String
// arguments
Keep in mind that these are approximations that will not impact correctness
since in all cases I ensured that the SIL will be the same after the
OwnershipModelEliminator has run. The cases that I was unsure of I commented
with SEMANTIC ARC TODO. Once we have the verifier any confusion that may have
occurred here will be dealt with.
rdar://28685236
This ensures that ownership is properly propagated forward through the use-def
graph.
This was the work that was stymied by issues relating to SILBuilder performing
local ARC dataflow. I ripped out that local dataflow in 6f4e2ab and added a
cheap ARC guaranteed dataflow pass that performs the same optimization.
Also in the process of doing this work, I found that there were many SILGen
tests that were either pattern matching in the wrong functions or had wrong
CHECK lines (for instance CHECK_NEXT). I fixed all of these issues and also
expanded many of the tests so that they verify ownership. The only work I left
for a future PR is that there are certain places in tests where we are using the
projection from an original value, instead of a copy. I marked those with a
message SEMANTIC ARC TODO so that they are easy to find.
rdar://28685236
If a function is public, and either @_transparent or @inline(__always),
we need to make its body available for inlining in other resilience
domains. The more general concept here is an 'inlineable' function;
once the precise behaviors we want are nailed down, the set of AST
attributes for exposing this will likely change.
At the SIL level, inlineable functions are marked with the [fragile]
attribute. The SIL serializer only serializes [fragile] functions
unless -sil-serialize-all is passed in.
This patch fixes two problems in this area by consolidating some
duplicated logic:
1) Property accesses in Sema did not check for @inline(__always)
functions, or functions nested inside inlineable functions.
This manifested as IRGen crashes if an inlineable function
accessed a property of a resilient type.
2) In SILGen, functions nested inside [fragile] functions were
properly [fragile], but @inline(__always) was not taken into
account. This manifested as SIL serializer crashes where a
[fragile] function could reference a non-public, non-[fragile]
function.
This change is part of the series for building the standard library
without -sil-serialize-all.
The function pointer is a thin function and possibly polymorphic,
so it does not really have an AST type. Instead of pretending it has
an AST type, just return a RawPointer and remove some casts in the
process.
We will sometimes emit materializeForSet for resilience, even if
it is not the preferred method for inout accesses of the property
or subscript.
To avoid having SILGen behavior depend on whether Sema synthesized
a materializeForSet or not, add a more precise predicate. The
general idea as I understand it, is that we want to use
materializeForSet if we do not have perfect information about the
implementation of the property or subscript, due to polymorphism
or resilience.
Similarly to how we've always handled parameter types, we
now recursively expand tuples in result types and separately
determine a result convention for each result.
The most important code-generation change here is that
indirect results are now returned separately from each
other and from any direct results. It is generally far
better, when receiving an indirect result, to receive it
as an independent result; the caller is much more likely
to be able to directly receive the result in the address
they want to initialize, rather than having to receive it
in temporary memory and then copy parts of it into the
target.
The most important conceptual change here that clients and
producers of SIL must be aware of is the new distinction
between a SILFunctionType's *parameters* and its *argument
list*. The former is just the formal parameters, derived
purely from the parameter types of the original function;
indirect results are no longer in this list. The latter
includes the indirect result arguments; as always, all
the indirect results strictly precede the parameters.
Apply instructions and entry block arguments follow the
argument list, not the parameter list.
A relatively minor change is that there can now be multiple
direct results, each with its own result convention.
This is a minor change because I've chosen to leave
return instructions as taking a single operand and
apply instructions as producing a single result; when
the type describes multiple results, they are implicitly
bound up in a tuple. It might make sense to split these
up and allow e.g. return instructions to take a list
of operands; however, it's not clear what to do on the
caller side, and this would be a major change that can
be separated out from this already over-large patch.
Unsurprisingly, the most invasive changes here are in
SILGen; this requires substantial reworking of both call
emission and reabstraction. It also proved important
to switch several SILGen operations over to work with
RValue instead of ManagedValue, since otherwise they
would be forced to spuriously "implode" buffers.
And use project_box to get to the address value.
SILGen now generates a project_box for each alloc_box.
And IRGen re-uses the address value from the alloc_box if the operand of project_box is an alloc_box.
This lets the generated code be the same as before.
Other than that most changes of this (quite large) commit are straightforward.
We weren't adding them as external decls unless they were for
storage on an imported type, which meant SILGen wasn't emitting
them if the conforming type was from a different Swift source
file, or in whole-module mode, a different module. This led
to linker errors.
Instead, always add accessors to the external decl list, but
skip them in SILGen if they are contained in the DeclContext
we are currently emitting (which is a source file or module).
Note that they are still emitted with the wrong linkage, from a
resilience perspective. Clients must only ever see public
exports for getters, setters and materializeForSet emitted
because they are required by resilience or the access pattern;
'accidental' accessors synthesized for protocol conformance
should not be public.
Having a separate address and container value returned from alloc_stack is not really needed in SIL.
Even if they differ we have both addresses available during IRGen, because a dealloc_stack is always dominated by the corresponding alloc_stack in the same function.
Although this commit quite large, most changes are trivial. The largest non-trivial change is in IRGenSIL.
This commit is a NFC regarding the generated code. Even the generated SIL is the same (except removed #0, #1 and @local_storage).
Add a new ResilientStructTypeInfo. This is a singleton since
all resilient structs have opaque payloads and are accessed
through value witness tables.
With this in place, flesh out IRGenModule::isResilient() and
use the new singleton to convert resilient structs.
Note that the old isResilient() was hard-coded to report that
all Clang-imported classes are "resilient". Now that this has
been unified with NominalTypeDecl::hasFixedLayout(), we will
report Clang-imported classes are "resilient" at the SIL level.
This should not introduce any semantic differences at this
point.
Unlike SIL, where currently resilient types are always resilient
even when used from the same module, IRGen is able to perform
direct manipulation of resilient structs from the current
module, since IRGen's type lowering has a resilience scope
plumbed through.
Note that we do not yet support laying out structs and classes
containing resilient fields -- this will come in a future patch.
Synthesize accessors for stored properties when appropriate, and use them
if the struct is in a different module.
For now, this goes along with a resilience domain being a single module.
A fixed layout type is one about which the compiler is allowed to
make certain assumptions across resilience domains. The assumptions
will be documented elsewhere, but for the purposes of this patch
series, they will include:
- the size of the type
- offsets of stored properties
- whether accessed properties are stored or computed
When -enable-resilience is passed to the frontend, all types become
resilient unless annotated with the @fixed_layout attribute.
So far, the @fixed_layout attribute only comes into play in SIL type
lowering of structs and enums, which now become address-only unless
they are @fixed_layout. For now, @fixed_layout is also allowed on
classes, but has no effect. In the future, support for less resilient
type lowering within a single resilience domain will be added, with
appropriate loads and stores in function prologs and epilogs.
Resilience is not enabled by default, which gives all types fixed
layout and matches the behavior of the compiler today. Since
we do not want the -enable-resilience flag to change the behavior
of existing compiled modules, only the currently-compiling module,
Sema adds the @fixed_layout flag to all declarations when the flag
is off. To reduce the size of .swiftmodule files, this could become
a flag on the module itself in the future.
The reasoning behind this is that the usual case is building
applications and private frameworks, where there is no need to make
anything resilient.
For the standard library, we can start out with resilience disabled,
while perfoming an audit adding @fixed_layout annotations in the
right places. Once the implementation is robust enough we can then
build the standard library with resilience enabled.
Revert "Fix complete_decl_attribute test for @fixed_layout"
Revert "Sema: non-@objc private stored properties do not need accessors"
Revert "Sema: Access stored properties of resilient structs through accessors"
Revert "Strawman @fixed_layout attribute and -{enable,disable}-resilience flags"
This reverts commit c91c6a789e.
This reverts commit 693d3d339f.
This reverts commit 085f88f616.
This reverts commit 5d99dc9bb8.
Synthesize accessors for stored properties when appropriate, and use them
if the struct is in a different module.
For now, this goes along with a resilience domain being a single module.
A fixed layout type is one about which the compiler is allowed to
make certain assumptions across resilience domains. The assumptions
will be documented elsewhere, but for the purposes of this patch
series, they will include:
- the size of the type
- offsets of stored properties
- whether accessed properties are stored or computed
When -enable-resilience is passed to the frontend, all types become
resilient unless annotated with the @fixed_layout attribute.
So far, the @fixed_layout attribute only comes into play in SIL type
lowering of structs and enums, which now become address-only unless
they are @fixed_layout. For now, @fixed_layout is also allowed on
classes, but has no effect. In the future, support for less resilient
type lowering within a single resilience domain will be added, with
appropriate loads and stores in function prologs and epilogs.
Resilience is not enabled by default, which gives all types fixed
layout and matches the behavior of the compiler today. Since
we do not want the -enable-resilience flag to change the behavior
of existing compiled modules, only the currently-compiling module,
Sema adds the @fixed_layout flag to all declarations when the flag
is off. To reduce the size of .swiftmodule files, this could become
a flag on the module itself in the future.
The reasoning behind this is that the usual case is building
applications and private frameworks, where there is no need to make
anything resilient.
For the standard library, we can start out with resilience disabled,
while perfoming an audit adding @fixed_layout annotations in the
right places. Once the implementation is robust enough we can then
build the standard library with resilience enabled.