Skip to content

Commit e2c43cf

Browse files
committed
Merge pull request #1725 from atrick/specialize
@_specialize attribute
2 parents 9078011 + 295dc96 commit e2c43cf

32 files changed

+1861
-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
@@ -1976,6 +1976,8 @@ ERROR(generic_type_requires_arguments,none,
19761976
"reference to generic type %0 requires arguments in <...>", (Type))
19771977
NOTE(generic_type_declared_here,none,
19781978
"generic type %0 declared here", (Identifier))
1979+
ERROR(cannot_partially_specialize_generic_function,none,
1980+
"cannot partially specialize a generic function", ())
19791981

19801982
// Ambiguities
19811983
ERROR(ambiguous_decl_ref,none,

include/swift/SIL/SILFunction.h

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

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

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

@@ -384,6 +405,18 @@ class SILFunction
384405
SemanticsAttrSet.erase(Iter);
385406
}
386407

408+
/// \returns the range of specialize attributes.
409+
ArrayRef<SILSpecializeAttr*> getSpecializeAttrs() const {
410+
return SpecializeAttrSet;
411+
}
412+
413+
/// Removes all specialize attributes from this function.
414+
void clearSpecializeAttrs() { SpecializeAttrSet.clear(); }
415+
416+
void addSpecializeAttr(SILSpecializeAttr *attr) {
417+
SpecializeAttrSet.push_back(attr);
418+
}
419+
387420
/// \returns True if the function is optimizable (i.e. not marked as no-opt),
388421
/// or is raw SIL (so that the mandatory passes still run).
389422
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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +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-
/// describe what change you made.
57-
const uint16_t VERSION_MINOR = 243; // Last change: re-number SIL stuff
56+
const uint16_t VERSION_MINOR = 244; // Last change: @_specialize attribute
5857

5958
using DeclID = PointerEmbeddedInt<unsigned, 31>;
6059
using DeclIDField = BCFixed<31>;
@@ -1380,6 +1379,11 @@ namespace decls_block {
13801379
// strings, separated by the prior index
13811380
>;
13821381

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

0 commit comments

Comments
 (0)