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)
This ireapplies commit 255c52de9f.
Original commit message:
Serialize debug scope and location info in the SIL assembler language.
At the moment it is only possible to test the effects that SIL
optimization passes have on debug information by observing the
effects of a full .swift -> LLVM IR compilation. This change enable us
to write targeted testcases for single SIL optimization passes.
The new syntax is as follows:
sil-scope-ref ::= 'scope' [0-9]+
sil-scope ::= 'sil_scope' [0-9]+ '{'
sil-loc
'parent' scope-parent
('inlined_at' sil-scope-ref )?
'}'
scope-parent ::= sil-function-name ':' sil-type
scope-parent ::= sil-scope-ref
sil-loc ::= 'loc' string-literal ':' [0-9]+ ':' [0-9]+
Each instruction may have a debug location and a SIL scope reference
at the end. Debug locations consist of a filename, a line number, and
a column number. If the debug location is omitted, it defaults to the
location in the SIL source file. SIL scopes describe the position
inside the lexical scope structure that the Swift expression a SIL
instruction was generated from had originally. SIL scopes also hold
inlining information.
<rdar://problem/22706994>
At the moment it is only possible to test the effects that SIL
optimization passes have on debug information by observing the
effects of a full .swift -> LLVM IR compilation. This change enable us
to write targeted testcases for single SIL optimization passes.
The new syntax is as follows:
sil-scope-ref ::= 'scope' [0-9]+
sil-scope ::= 'sil_scope' [0-9]+ '{'
sil-loc
'parent' scope-parent
('inlined_at' sil-scope-ref )?
'}'
scope-parent ::= sil-function-name ':' sil-type
scope-parent ::= sil-scope-ref
sil-loc ::= 'loc' string-literal ':' [0-9]+ ':' [0-9]+
Each instruction may have a debug location and a SIL scope reference
at the end. Debug locations consist of a filename, a line number, and
a column number. If the debug location is omitted, it defaults to the
location in the SIL source file. SIL scopes describe the position
inside the lexical scope structure that the Swift expression a SIL
instruction was generated from had originally. SIL scopes also hold
inlining information.
<rdar://problem/22706994>
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.
This is something that we have wanted for a long time and will enable us to
remove some hacks from the compiler (i.e. how we determine in the ARC optimizer
that we have "fatalError" like function) and also express new things like
"noarc".
There's a buggy SIL verifier check that was previously tautological,
and it turns out that it's violated, apparently harmlessly. Since it
was already doing nothing, I've commented it out temporarily while
I figure out the right way to fix SILGen to get the invariant right.
This centralizes the entrypoints for creating SILFunctions. Creating a
SILFunction is intimately tied to a specific SILModule, so it makes sense to
either centralize the creation on SILModule or SILFunction. Since a SILFunction
is in a SILModule, it seems more natural to put it on SILModule.
I purposely created a new override on SILMod that exactly matches the signature
of SILFunction::create so that beyond the extra indirection through SILMod, this
change should be NFC. We can refactor individual cases in later iterations of
refactoring.
The drivers for this change are providing a simpler API to SIL pass
authors, having a more efficient of the in-memory representation,
and ruling out an entire class of common bugs that usually result
in hard-to-debug backend crashes.
Summary
-------
SILInstruction
Old New
+---------------+ +------------------+ +-----------------+
|SILInstruction | |SILInstruction | |SILDebugLocation |
+---------------+ +------------------+ +-----------------+
| ... | | ... | | ... |
|SILLocation | |SILDebugLocation *| -> |SILLocation |
|SILDebugScope *| +------------------+ |SILDebugScope * |
+---------------+ +-----------------+
We’re introducing a new class SILDebugLocation which represents the
combination of a SILLocation and a SILDebugScope.
Instead of storing an inline SILLocation and a SILDebugScope pointer,
SILInstruction now only has one SILDebugLocation pointer. The APIs of
SILBuilder and SILDebugLocation guarantees that every SILInstruction
has a nonempty SILDebugScope.
Developer-visible changes include:
SILBuilder
----------
In the old design SILBuilder populated the InsertedInstrs list to
allow setting the debug scopes of all built instructions in bulk
at the very end (as the responsibility of the user). In the new design,
SILBuilder now carries a "current debug scope" state and immediately
sets the debug scope when an instruction is inserted.
This fixes a use-after-free issue with with SIL passes that delete
instructions before destroying the SILBuilder that created them.
Because of this, SILBuilderWithScopes no longer needs to be a template,
which simplifies its call sites.
SILInstruction
--------------
It is neither possible or necessary to manually call setDebugScope()
on a SILInstruction any more. The function still exists as a private
method, but is only used when splicing instructions from one function
to another.
Efficiency
----------
In addition to dropping 20 bytes from each SILInstruction,
SILDebugLocations are now allocated in the SILModule's bump pointer
allocator and are uniqued by SILBuilder. Unfortunately repeat compiles
of the standard library already vary by about 5% so I couldn’t yet
produce reliable numbers for how much this saves overall.
rdar://problem/22017421
This re-applies commit r31763 with a change to the predicate we
use for determining the linkage of a definition. It turns out we
could have definitions with a Clang body that were still public,
so instead of checking for a Clang body just check if the Clang
declaration is externally visible or not.
Swift SVN r31777
If an external SIL function has a Clang-generated body, I think this
means we have a static function, and we want to use Shared linkage,
not Public.
Add a new flag to SILFunction for this and plumb it through to
appease assertions from SILVerifier.
Swift SVN r31763
This patch implements the pre-specialization for the most popular generic types from the standard library. If there are invocations of generic functions from the standard library in the user-code and the compiler can find the specialized, optimized versions of these functions, then calls of generic functions are simply replaced by the calls of the specialized functions.
This feature is supposed to be used with -Onone to produce much faster (e.g. 5x-10x faster) executables in debug builds without impacting the compile time. In fact, the compile-time is even improved, because IRGen has less work to do. The feature can be considered a light-weight version of the -Odebug, because pre-specialization is limited in scope, but does not have a potentially negative compile-time impact compared to -Odebug. It is planned to enable it by default in the future.
This feature is disabled by default for the time being. It can be enabled by using a hidden flag: -Xllvm -use-prespecialized.
The implementation consists of two logical steps:
- When the standard library is being built, we force a creation of specializations for the most popular generic types from the stdlib, e.g. Arrays of integer and floating point types, Range<Int>, etc. The list of specializations is not fixed and can be easily altered by editing the Prespecialized.swift file, which is responsible for forcing the specialization of generic types (this is simple solution for now, until we have a proper annotation to indicate which specializations of a given generic type or function we want to generate by means of the pre-specialization). These specializations are then optimized and preserved in the stdlib dylib and in the Swift SIL module. The size increase of the stdlib due to creation of pre-specializations is currently about 3%-7%.
- When a user-code is being compiled with -Onone, the compiler would run a generic specializer over the user-code. If there are calls of generic functions from the standard library, the specializer would check if there is an existing specialization matching these invocations. If such a specialization is found, the original call is replaced by the call of this more efficient specialized version.
Swift SVN r30309
The old SIL-based approach failed to recognize function arguments of
SIL-inlined functions as arguments.
Post-Xcode 7 it would be better to lower this information during SILGen
and emit a debug_value for every function argument so we don't need
to reconstruct them after all the SIL optimizations.
rdar://problem/21109015
Swift SVN r29180
The two ways functions are created currently is via the two
SILModule::getOrCreateFunction(). One of the methods, takes in a raw mangled
name and uses that to create the function. The other takes in a SILDeclRef to
generate the mangled name. Most function emission (besides some thunk creation
functions) goes through the latter. For now we update the map there. This is ok,
since this map will only be used to provide extra verification that guaranteed
self is occuring everywhere that it is supposed to (since constructors and
destructors still have @owned self).
Swift SVN r27240
These aren't really orthogonal concerns--you'll never have a @thick @cc(objc_method), or an @objc_block @cc(witness_method)--and we have gross decision trees all over the codebase that try to hopscotch between the subset of combinations that make sense. Stop the madness by eliminating AbstractCC and folding its states into SILFunctionTypeRepresentation. This cleans up a ton of code across the compiler.
I couldn't quite eliminate AbstractCC's information from AST function types, since SIL type lowering transiently created AnyFunctionTypes with AbstractCCs set, even though these never occur at the source level. To accommodate type lowering, allow AnyFunctionType::ExtInfo to carry a SILFunctionTypeRepresentation, and arrange for the overlapping representations to share raw values.
In order to avoid disturbing test output, AST and SILFunctionTypes are still printed and parsed using the existing @thin/@thick/@objc_block and @cc() attributes, which is kind of gross, but lets me stage in the real source-breaking change separately.
Swift SVN r27095