Skip to content

Commit acdf28f

Browse files
committed
Merge pull request #1743 from atrick/reapply-specialize
Reapply "Merge pull request #1725 from atrick/specialize"
2 parents 6cbffa7 + 482b264 commit acdf28f

32 files changed

+1862
-280
lines changed

docs/Generics.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,35 @@ the virtual dispatch, inline calls when appropriate, and eliminate the overhead
713713
of the generic system. Such optimizations can be performed based on heuristics,
714714
user direction, or profile-guided optimization.
715715

716+
An internal @_specialize function attribute allows developers to force
717+
full specialization by listing concrete type names corresponding to the
718+
function's generic signature. A function's generic signature is a
719+
concatenation of its generic context and the function's own generic
720+
type parameters.::
721+
722+
struct S<T> {
723+
var x: T
724+
@_specialize(Int, Float)
725+
mutating func exchangeSecond<U>(u: U, _ t: T) -> (U, T) {
726+
x = t
727+
return (u, x)
728+
}
729+
}
730+
731+
// Substitutes: <T, U> with <Int, Float> producing:
732+
// S<Int>::exchangeSecond<Float>(u: Float, t: Int) -> (Float, Int)
733+
734+
@_specialize currently acts as a hint to the optimizer, which
735+
generates type checks and code to dispatch to the specialized routine
736+
without affecting the signature of the generic function. The
737+
intention is to support efforts at evaluating the performance of
738+
specialized code. The performance impact is not guaranteed and is
739+
likely to change with the optimizer. This attribute should only be
740+
used in conjunction with rigorous performance analysis. Eventually,
741+
a similar attribute could be defined in the language, allowing it to be
742+
exposed as part of a function's API. That would allow direct dispatch
743+
to specialized code without type checks, even across modules.
744+
716745
Existential Types and Generics
717746
------------------------------
718747

include/swift/AST/Attr.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ SIMPLE_DECL_ATTR(nonobjc, NonObjC,
164164
SIMPLE_DECL_ATTR(_fixed_layout, FixedLayout,
165165
OnVar | OnClass | OnStruct | OnEnum | UserInaccessible, 31)
166166

167+
DECL_ATTR(_specialize, Specialize,
168+
OnConstructor | OnFunc | AllowMultipleAttributes | LongAttribute
169+
| UserInaccessible, 32)
170+
167171
// Non-serialized attributes.
168172

169173
SIMPLE_DECL_ATTR(mutating, Mutating, OnFunc | DeclModifier | NotSerialized,

include/swift/AST/Attr.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "swift/Basic/Range.h"
2424
#include "swift/AST/Identifier.h"
2525
#include "swift/AST/AttrKind.h"
26+
#include "swift/AST/ConcreteDeclRef.h"
2627
#include "swift/AST/KnownProtocols.h"
2728
#include "swift/AST/Ownership.h"
2829
#include "swift/AST/PlatformKind.h"
@@ -38,6 +39,7 @@ class ASTContext;
3839
struct PrintOptions;
3940
class Decl;
4041
class ClassDecl;
42+
struct TypeLoc;
4143

4244
class InfixData {
4345
unsigned Precedence : 8;
@@ -1136,6 +1138,36 @@ class Swift3MigrationAttr : public DeclAttribute {
11361138
}
11371139
};
11381140

1141+
/// The @_specialize attribute, which forces specialization on the specified
1142+
/// type list.
1143+
class SpecializeAttr : public DeclAttribute {
1144+
unsigned numTypes;
1145+
ConcreteDeclRef specializedDecl;
1146+
1147+
TypeLoc *getTypeLocData() {
1148+
return reinterpret_cast<TypeLoc *>(this + 1);
1149+
}
1150+
1151+
SpecializeAttr(SourceLoc atLoc, SourceRange Range,
1152+
ArrayRef<TypeLoc> typeLocs);
1153+
1154+
public:
1155+
static SpecializeAttr *create(ASTContext &Ctx, SourceLoc atLoc,
1156+
SourceRange Range, ArrayRef<TypeLoc> typeLocs);
1157+
1158+
ArrayRef<TypeLoc> getTypeLocs() const;
1159+
1160+
MutableArrayRef<TypeLoc> getTypeLocs();
1161+
1162+
ConcreteDeclRef getConcreteDecl() const { return specializedDecl; }
1163+
1164+
void setConcreteDecl(ConcreteDeclRef ref) { specializedDecl = ref; }
1165+
1166+
static bool classof(const DeclAttribute *DA) {
1167+
return DA->getKind() == DAK_Specialize;
1168+
}
1169+
};
1170+
11391171
/// \brief Attributes that may be applied to declarations.
11401172
class DeclAttributes {
11411173
/// Linked list of declaration attributes.

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,6 +1978,8 @@ ERROR(generic_type_requires_arguments,none,
19781978
"reference to generic type %0 requires arguments in <...>", (Type))
19791979
NOTE(generic_type_declared_here,none,
19801980
"generic type %0 declared here", (Identifier))
1981+
ERROR(cannot_partially_specialize_generic_function,none,
1982+
"cannot partially specialize a generic function", ())
19811983

19821984
// Ambiguities
19831985
ERROR(ambiguous_decl_ref,none,

include/swift/SIL/SILFunction.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,24 @@ enum IsTransparent_t { IsNotTransparent, IsTransparent };
3838
enum Inline_t { InlineDefault, NoInline, AlwaysInline };
3939
enum IsThunk_t { IsNotThunk, IsThunk, IsReabstractionThunk };
4040

41+
class SILSpecializeAttr final :
42+
private llvm::TrailingObjects<SILSpecializeAttr, Substitution> {
43+
friend TrailingObjects;
44+
45+
unsigned numSubs;
46+
47+
SILSpecializeAttr(ArrayRef<Substitution> subs);
48+
49+
public:
50+
static SILSpecializeAttr *create(SILModule &M, ArrayRef<Substitution> subs);
51+
52+
ArrayRef<Substitution> getSubstitutions() const {
53+
return { getTrailingObjects<Substitution>(), numSubs };
54+
}
55+
56+
void print(llvm::raw_ostream &OS) const;
57+
};
58+
4159
/// SILFunction - A function body that has been lowered to SIL. This consists of
4260
/// zero or more SIL SILBasicBlock objects that contain the SILInstruction
4361
/// objects making up the function.
@@ -141,6 +159,9 @@ class SILFunction
141159
/// StringRefs?
142160
llvm::SmallVector<std::string, 1> SemanticsAttrSet;
143161

162+
/// The function's remaining set of specialize attributes.
163+
std::vector<SILSpecializeAttr*> SpecializeAttrSet;
164+
144165
/// The function's effects attribute.
145166
EffectsKind EffectsKindAttr;
146167

@@ -385,6 +406,18 @@ class SILFunction
385406
SemanticsAttrSet.erase(Iter);
386407
}
387408

409+
/// \returns the range of specialize attributes.
410+
ArrayRef<SILSpecializeAttr*> getSpecializeAttrs() const {
411+
return SpecializeAttrSet;
412+
}
413+
414+
/// Removes all specialize attributes from this function.
415+
void clearSpecializeAttrs() { SpecializeAttrSet.clear(); }
416+
417+
void addSpecializeAttr(SILSpecializeAttr *attr) {
418+
SpecializeAttrSet.push_back(attr);
419+
}
420+
388421
/// \returns True if the function is optimizable (i.e. not marked as no-opt),
389422
/// or is raw SIL (so that the mandatory passes still run).
390423
bool shouldOptimize() const;

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ PASS(DiagnoseUnreachable, "diagnose-unreachable",
8888
"Diagnose Unreachable Code")
8989
PASS(DiagnosticConstantPropagation, "diagnostic-constant-propagation",
9090
"Propagate constants and emit diagnostics")
91+
PASS(EagerSpecializer, "eager-specializer",
92+
"Specialize speculatively and insert dispatch guarded by type checks")
9193
PASS(EarlyCodeMotion, "early-codemotion",
9294
"Code motion without release hoisting")
9395
PASS(EarlyInliner, "early-inline",

include/swift/SILOptimizer/Utils/GenericCloner.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,24 +40,25 @@ class GenericCloner : public TypeSubstCloner<GenericCloner> {
4040
GenericCloner(SILFunction *F,
4141
const ReabstractionInfo &ReInfo,
4242
TypeSubstitutionMap &ContextSubs,
43+
ArrayRef<Substitution> ParamSubs,
4344
StringRef NewName,
44-
ArrayRef<Substitution> ApplySubs,
4545
CloneCollector::CallbackType Callback)
4646
: TypeSubstCloner(*initCloned(F, ReInfo, NewName), *F, ContextSubs,
47-
ApplySubs), ReInfo(ReInfo), Callback(Callback) {
47+
ParamSubs), ReInfo(ReInfo), Callback(Callback) {
4848
assert(F->getDebugScope()->Parent != getCloned()->getDebugScope()->Parent);
4949
}
5050
/// Clone and remap the types in \p F according to the substitution
5151
/// list in \p Subs. Parameters are re-abstracted (changed from indirect to
5252
/// direct) according to \p ReInfo.
53-
static SILFunction *cloneFunction(SILFunction *F,
54-
const ReabstractionInfo &ReInfo,
55-
TypeSubstitutionMap &ContextSubs,
56-
StringRef NewName, ApplySite Caller,
57-
CloneCollector::CallbackType Callback =nullptr) {
53+
static SILFunction *
54+
cloneFunction(SILFunction *F,
55+
const ReabstractionInfo &ReInfo,
56+
TypeSubstitutionMap &ContextSubs,
57+
ArrayRef<Substitution> ParamSubs,
58+
StringRef NewName,
59+
CloneCollector::CallbackType Callback =nullptr) {
5860
// Clone and specialize the function.
59-
GenericCloner SC(F, ReInfo, ContextSubs, NewName,
60-
Caller.getSubstitutions(), Callback);
61+
GenericCloner SC(F, ReInfo, ContextSubs, ParamSubs, NewName, Callback);
6162
SC.populateCloned();
6263
SC.cleanUp(SC.getCloned());
6364
return SC.getCloned();

include/swift/SILOptimizer/Utils/Generics.h

Lines changed: 76 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,51 @@
2222
#include "swift/SIL/SILFunction.h"
2323
#include "swift/SIL/SILInstruction.h"
2424
#include "swift/SILOptimizer/Utils/Local.h"
25-
#include "llvm/ADT/BitVector.h"
25+
#include "llvm/ADT/SmallBitVector.h"
2626
#include "llvm/Support/CommandLine.h"
2727
#include "llvm/Support/Debug.h"
2828

2929
namespace swift {
3030

31+
/// Tries to specialize an \p Apply of a generic function. It can be a full
32+
/// apply site or a partial apply.
33+
/// Replaced and now dead instructions are returned in \p DeadApplies.
34+
/// New created functions, like the specialized callee and thunks, are returned
35+
/// in \p NewFunctions.
36+
///
37+
/// This is the top-level entry point for specializing an existing call site.
38+
void trySpecializeApplyOfGeneric(
39+
ApplySite Apply,
40+
llvm::SmallVectorImpl<SILInstruction *> &DeadApplies,
41+
llvm::SmallVectorImpl<SILFunction *> &NewFunctions);
42+
3143
/// Helper class to describe re-abstraction of function parameters done during
3244
/// specialization.
3345
///
3446
/// Specifically, it contains information which parameters and returns are
3547
/// changed from indirect values to direct values.
3648
class ReabstractionInfo {
49+
/// A 1-bit means that this parameter/return value is converted from indirect
50+
/// to direct.
51+
llvm::SmallBitVector Conversions;
52+
53+
/// The first NumResults bits in Conversions refer to indirect out-parameters.
54+
unsigned NumResults;
55+
56+
/// The function type after applying the substitutions of the original
57+
/// apply site.
58+
CanSILFunctionType SubstitutedType;
59+
60+
/// The function type after applying the re-abstractions on the
61+
/// SubstitutedType.
62+
CanSILFunctionType SpecializedType;
63+
3764
public:
38-
/// Constructs the ReabstractionInfo for an apply site \p AI calling the
39-
/// generic function \p Orig.
65+
/// Constructs the ReabstractionInfo for generic function \p Orig with
66+
/// substitutions \p ParamSubs.
4067
/// If specialization is not possible getSpecializedType() will return an
4168
/// invalid type.
42-
ReabstractionInfo(SILFunction *Orig, ApplySite AI);
69+
ReabstractionInfo(SILFunction *Orig, ArrayRef<Substitution> ParamSubs);
4370

4471
/// Does the \p ArgIdx refer to an indirect out-parameter?
4572
bool isResultIndex(unsigned ArgIdx) const {
@@ -88,8 +115,8 @@ class ReabstractionInfo {
88115
return Conversions.size() - numArgs;
89116
}
90117

91-
/// Get the function type after applying the substitutions of the original
92-
/// apply site.
118+
/// Get the function type after applying the substitutions to the original
119+
/// generic function.
93120
CanSILFunctionType getSubstitutedType() const { return SubstitutedType; }
94121

95122
/// Get the function type after applying the re-abstractions on the
@@ -101,41 +128,61 @@ class ReabstractionInfo {
101128
/// SubstFTy by applying the re-abstractions.
102129
CanSILFunctionType createSpecializedType(CanSILFunctionType SubstFTy,
103130
SILModule &M) const;
104-
private:
105-
/// A 1-bit means that this parameter/return value is converted from indirect
106-
/// to direct.
107-
llvm::BitVector Conversions;
108-
109-
/// The first NumResults bits in Conversions refer to indirect out-parameters.
110-
unsigned NumResults;
131+
};
111132

112-
/// The function type after applying the substitutions of the original
113-
/// apply site.
114-
CanSILFunctionType SubstitutedType;
133+
/// Helper class for specializing a generic function given a list of
134+
/// substitutions.
135+
class GenericFuncSpecializer {
136+
SILModule &M;
137+
SILFunction *GenericFunc;
138+
ArrayRef<Substitution> ParamSubs;
139+
const ReabstractionInfo &ReInfo;
115140

116-
/// The function type after applying the re-abstractions on the
117-
/// SubstitutedType.
118-
CanSILFunctionType SpecializedType;
141+
TypeSubstitutionMap ContextSubs;
142+
std::string ClonedName;
143+
public:
144+
GenericFuncSpecializer(SILFunction *GenericFunc,
145+
ArrayRef<Substitution> ParamSubs,
146+
const ReabstractionInfo &ReInfo);
147+
148+
/// If we already have this specialization, reuse it.
149+
SILFunction *lookupSpecialization();
150+
151+
/// Return a newly created specialized function.
152+
SILFunction *tryCreateSpecialization();
153+
154+
/// Try to specialize GenericFunc given a list of ParamSubs.
155+
/// Returns either a new or existing specialized function, or nullptr.
156+
SILFunction *trySpecialization() {
157+
if (!ReInfo.getSpecializedType())
158+
return nullptr;
159+
160+
SILFunction *SpecializedF = lookupSpecialization();
161+
if (!SpecializedF)
162+
SpecializedF = tryCreateSpecialization();
163+
164+
return SpecializedF;
165+
}
119166
};
120167

121-
/// Tries to specialize an \p Apply of a generic function. It can be a full
122-
/// apply site or a partial apply.
123-
/// Replaced and now dead instructions are returned in \p DeadApplies.
124-
/// New created functions, like the specialized callee and thunks, are returned
125-
/// in \p NewFunctions.
126-
void trySpecializeApplyOfGeneric(ApplySite Apply,
127-
llvm::SmallVectorImpl<SILInstruction *> &DeadApplies,
128-
llvm::SmallVectorImpl<SILFunction *> &NewFunctions);
168+
// =============================================================================
169+
// Prespecialized symbol lookup.
170+
// =============================================================================
129171

130-
/// Checks if a given mangled name could be a name of a whitelisted specialization.
172+
/// Checks if a given mangled name could be a name of a whitelisted
173+
/// specialization.
131174
bool isWhitelistedSpecialization(StringRef SpecName);
132175

133176
/// Create a new apply based on an old one, but with a different
134177
/// function being applied.
135178
ApplySite replaceWithSpecializedFunction(ApplySite AI, SILFunction *NewF,
136179
const ReabstractionInfo &ReInfo);
137180

138-
SILFunction *getExistingSpecialization(SILModule &M, StringRef FunctionName);
181+
/// Returns a SILFunction for the symbol specified by FunctioName if it is
182+
/// visible to the current SILModule. This is used to link call sites to
183+
/// externally defined specialization and should only be used when the function
184+
/// body is not required for further optimization or inlining (-Onone).
185+
SILFunction *lookupPrespecializedSymbol(SILModule &M, StringRef FunctionName);
139186

140187
} // end namespace swift
141188

include/swift/Serialization/ModuleFormat.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ const uint16_t VERSION_MAJOR = 0;
5353
/// in source control, you should also update the comment to briefly
5454
/// describe what change you made. The content of this comment isn't important;
5555
/// it just ensures a conflict if two people change the module format.
56-
const uint16_t VERSION_MINOR = 245; // Last change: re-number SIL stuff
56+
const uint16_t VERSION_MINOR = 246; // Last change: @_specialize attribute
5757

5858
using DeclID = PointerEmbeddedInt<unsigned, 31>;
5959
using DeclIDField = BCFixed<31>;
@@ -1379,6 +1379,11 @@ namespace decls_block {
13791379
// strings, separated by the prior index
13801380
>;
13811381

1382+
using SpecializeDeclAttrLayout = BCRecordLayout<
1383+
Specialize_DECL_ATTR,
1384+
BCArray<TypeIDField> // concrete types
1385+
>;
1386+
13821387
#define SIMPLE_DECL_ATTR(X, CLASS, ...) \
13831388
using CLASS##DeclAttrLayout = BCRecordLayout< \
13841389
CLASS##_DECL_ATTR, \

0 commit comments

Comments
 (0)