introduce a common superclass, SILNode.
This is in preparation for allowing instructions to have multiple
results. It is also a somewhat more elegant representation for
instructions that have zero results. Instructions that are known
to have exactly one result inherit from a class, SingleValueInstruction,
that subclasses both ValueBase and SILInstruction. Some care must be
taken when working with SILNode pointers and testing for equality;
please see the comment on SILNode for more information.
A number of SIL passes needed to be updated in order to handle this
new distinction between SIL values and SIL instructions.
Note that the SIL parser is now stricter about not trying to assign
a result value from an instruction (like 'return' or 'strong_retain')
that does not produce any.
This removes the function body, but preserves the SILFunction object, which may be still referenced by different kinds of meta-information e.g. debug info for inlined functions, generic specializations information, etc.
Doing this unconditionally simplifies the code and makes it less error-prone to reference SILFunctions from any kind of meta-information. It just works. No need to set any special flags, etc.
This marker can be used e.g. when a function is referenced from the generic specialization information (or any other kind of metadata) to indicate that this SIL function should not be completely eliminated by the dead function elimination pass even if it is not referenced by any function_ref instruction anymore. Such a function will be preserved in a zombie list instead if it needs to be eliminated.
GenericSpecializationInformation contains information regarding how a given specialized function was created, e.g. which caller function triggered this specialization, which substitutions were used, etc. Provide some debugging flags to dump the collected specialization information.
The information about generic specializations is referenced by the specialized functions and by call-sites originating from specialized functions.
This information can be created/used by the generic specializer to detect generic call-sites whose specialization would result in non-terminating sequence of subsequent generic specializations.
This semantic attribute allows a user to disable ownership verification on
specific functions.
The intention is that once I turn on the sil ownership verifier for parts of the
stdlib, if an engineer exposes an ownership issue, they can disable ownership
verification on that specific function, file a bug, and continue with their
work.
rdar://31880847
The new API is broken. Popping a generic context frees all
dependent type lowerings, so this function returns a pointer
to freed memory.
This reverts commit 24dfae0716.
This generalizes a hack where re-abstraction thunks become fragile on contact
with fragile functions.
The old policy was:
- [fragile] functions always serialized
- [reabstraction_thunk] transitively referenced from fragile always serialized
The new policy is:
- [serialized] functions always serialized
- [serializable] functions transitively referenced from serialized functions
are always serialized
- Most kinds of thunks can now be [serializable], allowing them to be shared
between serialized and non-serialized code without any issues, as long as the
body of the thunk is sufficiently "simple" (doesn't reference private
symbols or performs direct access to resilient types)
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.
Take a seat and pour yourself a beer because this is
going to get pretty intense.
Recall that class methods that return 'Self', have a
'self' type of @dynamic_self X or @dynamic_self X.Type,
for some class X, based on if the method is an instance
method or a static method.
The instance type of a metatype is not lowered, and we
preserve DynamicSelfType there. This is required for
correct behavior with the SIL optimizer.
For example if you specialize a function that contains a
'metatype $((T) -> Int, T).Type' SIL instruction or
some other metatype of a structural type containing a
generic parameter, we might end up with something like
'metatype $((@dynamic_self X) -> Int, X).Type'
after substitution, for some class 'X'. Note that the
second occurrence of 'X', is in "lowered position" so
the @dynamic_self did, indeed, get stripped away.
So while *values* of @dynamic_self type don't need to
carry the fact that they're @dynamic_self at the SIL
level, because Sema has inserted all the right casts.
Metatypes do though, because when lowering the 'metatype'
instruction, IRGen has to know to emit the type metadata
from the method's 'self' parameter, and not the static
metadata for the exact class type.
Essentially, 'metatype @dynamic_self X.Type' is
the same as 'value_metatype %self : X.Type', except that
the @dynamic_self type can appear inside other structural
types also, which is something we cannot write in the
AST.
This is all well and good, but when lowering a
SILFunctionType we erase @dynamic_self from the 'self'
parameter type because when you *call* such a function
from another function, you are not necessarily calling
it on your own 'self' value. And if you are, Sema
already emitted the right unchecked downcast there to
turn the result into the right type.
The problem is that the type of an argument (the value
"inside" the function) used to always be identical to
the type of the parameter (the type from "outside" the
function, in the SILFunctionType). Of course this
assumption is no longer correct for static methods,
where the 'self' argument should really have type
@dynamic_self X.Type, not X.Type.
A further complication is closure captures, whose types
can also contain @dynamic_self inside metatypes in other
structural types. We used to erase @dynamic_self from
these.
Both of these are wrong, because if you call a generic
function <T> (T.Type) -> () with a T := @dynamic_self X
substitution (recall that substitutions are written in
terms of AST types and not lowered types) and pass in
the 'self' argument, we would pass in a value of type
X.Type and not @dynamic_self X.Type.
There were similar issues with captures, with
additional complications from nested closures.
Fix all this by having SILGenProlog emit a downcast
to turn the X.Type argument into a value of type
@dynamic_self X.Type, and tweak capture lowering to
not erase @dynamic_self from capture types.
This fixes several cases that used to fail with
asserts in SILGenApply or the SIL verifier, in particular
the example outlined in <rdar://problem/31226650>,
where we would crash when calling a protocol extension
method from a static class method (oops!).
If you got this far and still follow along,
congratulations, you now know more about DynamicSelfType
than I do.
We didn’t do that if an optimization looked up a witness table.
Fixes rdar://problem/30544344
I couldn’t come up with an isolated test case, but this should be covered with our existing tests.
(The problem shows up when inlining of generics are enabled)
SubstitutionList is going to be a more compact representation of
a SubstitutionMap, suitable for inline allocation inside another
object.
For now, it's just a typedef for ArrayRef<Substitution>.
In all cases the DeclCtx field was supposed to be initialized from the
SILLocation of the function, so we can save one pointer per
SILFunction.
There is one test case change where a different (more precise)
diagnostic is being generated after this change.
This API is meant to enable people working in SIL to be able to retrieve the
type lowering of a lowered type within the GenericSignature associated with a
given SILFunction's SILFunctionType.
Separate formal lowered types from SIL types.
The SIL type of an argument will depend on the SIL module's conventions.
The module conventions are determined by the SIL stage and LangOpts.
Almost NFC, but specialized manglings are broken incidentally as a result of
fixes to the way passes handle book-keeping of aruments. The mangler is fixed in
the subsequent commit.
Otherwise, NFC is intended, but quite possible do to rewriting the logic in many
places.
Before this commit all code relating to handling arguments in SILBasicBlock had
somewhere in the name BB. This is redundant given that the class's name is
already SILBasicBlock. This commit drops those names.
Some examples:
getBBArg() => getArgument()
BBArgList => ArgumentList
bbarg_begin() => args_begin()
This eliminates all inline creation of SILBasicBlock via placement new.
There are a few reasons to do this:
1. A SILBasicBlock is always created with a parent function. This commit
formalizes this into the SILBasicBlock API by only allowing for SILFunctions to
create SILBasicBlocks. This is implemented via the type system by making all
SILBasicBlock constructors private. Since SILFunction is a friend of
SILBasicBlock, SILFunction can still create a SILBasicBlock without issue.
2. Since all SILBasicBlocks will be created in only a few functions, it becomes
very easy to determine using instruments the amount of memory being allocated
for SILBasicBlocks by simply inverting the call tree in Allocations.
With LTO+PGO, normal inlining can occur if profitable so there shouldn't be
overhead that we care about in shipping compilers.
This is a NFC change, since verification still will be behind the flag. But this
will allow me to move copy_value, destroy_value in front of the
EnableSILOwnership flag and verify via SILGen that we are always using those
instructions.
rdar://28851920
Previously I was going to just set a flag and run the verifier once with that
flag enabled. Then I realized that given that the OwnershipModelEliminator is a
function pass, I really need to put the state on whether or not ownership is
enabled on functions. Now this commit refactors the verifier to use the state on
the function when determining if it should allow for ownership qualified
instructions or not in a specific function.
rdar://28685236
Over the past day or so I have been thinking about how we are going to need to
manage verification of semantic ARC semantics in the pass pipeline. Specifically
the Eliminator pass really needs to be a function pass to ensure that we can
transparently put it at any stage of the optimization pipeline. This means that
just having a flag on the SILVerifier that states whether or not ownership is
enabled is not sufficient for our purposes. Instead, while staging in the SIL
ownership model, we need a bit on all SILFunctions to state whether the function
has been run through the ownership model eliminator so that the verifier can
ensure that we are in a world with "SIL ownership" or in a world without "SIL
ownership", never in a world with only some "SIL ownership" instructions. We
embed this distinction in SIL by creating the concept of a function with
"qualified ownership" and a function with "unqualified ownership".
Define a function with "qualified ownership" as a function that contains no
instructions with "unqualified ownership" (i.e. unqualified load) and a function
with "unqualified ownership" as a function containing such no "ownership
qualified" instructions (i.e. load [copy]) and at least 1 unqualified ownership
instruction.
This commit embeds this distinction into SILFunction in a manner that is
transparently ignored when compiling with SIL ownership disabled. This is done
by representing qualified or unqualified ownership via an optional Boolean on
SILFunction. If the Boolean is None, then SILOwnership is not enabled and the
verifier/passes can work as appropriate. If the Boolean is not None, then it
states whether or not the function has been run through the Ownership Model
Eliminator and thus what invariants the verifier should enforce.
How does this concept flow through the compilation pipeline for functions in a
given module? When SIL Ownership is enabled, all SILFunctions that are produced
in a given module start with "qualified ownership" allowing them to contain SIL
ownership instructions. After the Ownership Model eliminator has run, the
Ownership Model sets the "unqualified" ownership flag on the SILFunction stating
that no more ownership qualified instructions are allowed to be seen in the
given function.
But what about functions that are parsed or are deserialized from another
module? Luckily, given the manner in which we have categories our functions, we
can categorize functions directly without needing to add anything to the parser
or to the deserializer. This is done by enforcing that it is illegal to have a
function with qualified ownership and unqualified ownership instructions and
asserting that functions without either are considered qualified.
rdar://28685236
This doesn't make a difference on Darwin, but on Linux it causes linker errors if a class inherits from a public class in another module which has private/internal members.
fixes SR-1901
This patch is rather large, since it was hard to make this change
incrementally, but most of the changes are mechanical.
Now that we have a lighter-weight data structure in the AST for mapping
interface types to archetypes and vice versa, use that in SIL instead of
a GenericParamList.
This means that when serializing a SILFunction body, we no longer need to
serialize references to archetypes from other modules.
Several methods used for forming substitutions can now be moved from
GenericParamList to GenericEnvironment.
Also, GenericParamList::cloneWithOuterParameters() and
GenericParamList::getEmpty() can now go away, since they were only used
when SILGen-ing witness thunks.
Finally, when printing generic parameters with identical names, the
SIL printer used to number them from highest depth to lowest, by
walking generic parameter lists starting with the innermost one.
Now, ambiguous generic parameters are numbered from lowest depth
to highest, by walking the generic signature, which means test
output in one of the SILGen tests has changed.
This is the first, and most trivial, usage of the new
GenericSignature::getSubstitutions() method.
Note that getForwardingSubstitutions() now takes a
GenericSignature, which is slightly awkward.
However, this is in line with our goal of 'hollowing out'
GenericParamList by removing knowledge of the finalized
generic requirements.
Also, there is now a new getForwardingSubstitutionMap()
function, which returns an interface type substitution
mapping. This is used in the new getForwardingSubstitutions()
implementation, and all also be used elsewhere later.
Finally, in the SILFunction we now cache the forwarding
substitutions, instead of re-computing them every time.
I doubt this makes a big difference in performance, but
it's a simple enhancement and every little bit helps.
Mostly NFC, this is just plumbing for the next patch.
Note that isNever() returns true for any uninhabited
enum.
It should be generalized so that stuff like (Never, Int)
is also known to be uninhabited, or even to support
generic substitutions that yield uninhabited types,
but for now I really see no reason to go that far, and
the current check for an enum with no cases seems
perfectly adequate.
Two fixes to optimization passes to maintain restrictions about what
[fragile] functions can reference:
- When devirtualizing witness methods, don't devirtualize if the caller
is fragile and the callee is not. This matches existing logic in
class devirtualization.
- When performing generic or function signature specialization, don't
specialize non-fragile functions referenced from fragile functions.
Since @_transparent functions are allowed to call 'static inline'
imported functions, also be sure to mark the foreign-to-native thunk
for such a function as [fragile].
With this patch, the standard library and performance test suite
now build with -enable-resilience.
No new tests for this stuff here -- the existing tests together
with an -enable-resilience build provide coverage.
Closes out <https://bugs.swift.org/browse/SR-267> and
<https://bugs.swift.org/browse/SR-268>.
When resilience is enabled, some functions in the standard library that
are marked @effects(readonly) now have indirect results.
The SILCombiner pass doesn't handle @effects(readonly) with @out results
correctly, so just disable the optimizations temporarily to avoid a
mis-compile when resilience is enabled.
The LLVM readonly attribute can never be applied to such functions, so
don't set it either.
Should not have an effect when resilience is disabled.
This was mistakenly reverted in an attempt to fix buildbots.
Unfortunately it's now smashed into one commit.
---
Introduce @_specialize(<type list>) internal attribute.
This attribute can be attached to generic functions. The attribute's
arguments must be a list of concrete types to be substituted in the
function's generic signature. Any number of specializations may be
associated with a generic function.
This attribute provides a hint to the compiler. At -O, the compiler
will generate the specified specializations and emit calls to the
specialized code in the original generic function guarded by type
checks.
The current attribute is designed to be an internal tool for
performance experimentation. It does not affect the language or
API. This work may be extended in the future to add user-visible
attributes that do provide API guarantees and/or direct dispatch to
specialized code.
This attribute works on any generic function: a freestanding function
with generic type parameters, a nongeneric method declared in a
generic class, a generic method in a nongeneric class or a generic
method in a generic class. A function's generic signature is a
concatenation of the generic context and the function's own generic
type parameters.
e.g.
struct S<T> {
var x: T
@_specialize(Int, Float)
mutating func exchangeSecond<U>(u: U, _ t: T) -> (U, T) {
x = t
return (u, x)
}
}
// Substitutes: <T, U> with <Int, Float> producing:
// S<Int>::exchangeSecond<Float>(u: Float, t: Int) -> (Float, Int)
---
[SILOptimizer] Introduce an eager-specializer pass.
This pass finds generic functions with @_specialized attributes and
generates specialized code for the attribute's concrete types. It
inserts type checks and guarded dispatch at the beginning of the
generic function for each specialization. Since we don't currently
expose this attribute as API and don't specialize vtables and witness
tables yet, the only way to reach the specialized code is by calling
the generic function which performs the guarded dispatch.
In the future, we can build on this work in several ways:
- cross module dispatch directly to specialized code
- dynamic dispatch directly to specialized code
- automated specialization based on less specific hints
- partial specialization
- and so on...
I reorganized and refactored the optimizer's generic utilities to
support direct function specialization as opposed to apply
specialization.
Temporarily reverting @_specialize because stdlib unit tests are
failing on an internal branch during deserialization.
This reverts commit e2c43cfe14, reversing
changes made to 9078011f93.
Introduce a new SILPrintContext class which is the main handle passed to the SILModule's and SILFunction's print functions.
It also allows to let derived classes implement call backs on instruction printing.
NFC for now, but needed for the upcoming SIL-debuginfo change.
This attribute can be attached to generic functions. The attribute's
arguments must be a list of concrete types to be substituted in the
function's generic signature. Any number of specializations may be
associated with a generic function.
This attribute provides a hint to the compiler. At -O, the compiler
will generate the specified specializations and emit calls to the
specialized code in the original generic function guarded by type
checks.
The current attribute is designed to be an internal tool for
performance experimentation. It does not affect the language or
API. This work may be extended in the future to add user-visible
attributes that do provide API guarantees and/or direct dispatch to
specialized code.
This attribute works on any generic function: a freestanding function
with generic type parameters, a nongeneric method declared in a
generic class, a generic method in a nongeneric class or a generic
method in a generic class. A function's generic signature is a
concatenation of the generic context and the function's own generic
type parameters.
e.g.
struct S<T> {
var x: T
@_specialize(Int, Float)
mutating func exchangeSecond<U>(u: U, _ t: T) -> (U, T) {
x = t
return (u, x)
}
}
// Substitutes: <T, U> with <Int, Float> producing:
// S<Int>::exchangeSecond<Float>(u: Float, t: Int) -> (Float, Int)