Skip to content

Commit 4c05227

Browse files
committed
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)
1 parent f5511da commit 4c05227

23 files changed

+627
-29
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/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, \

lib/AST/Attr.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,11 +442,21 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options)
442442
break;
443443
}
444444

445-
default:
446-
llvm_unreachable("handled before this switch");
445+
case DAK_Specialize: {
446+
Printer << "@" << getAttrName() << "(";
447+
auto *attr = cast<SpecializeAttr>(this);
448+
interleave(attr->getTypeLocs(),
449+
[&](TypeLoc tyLoc){ tyLoc.getType().print(Printer, Options); },
450+
[&]{ Printer << ", "; });
451+
Printer << ")";
452+
break;
453+
}
447454

448455
case DAK_Count:
449456
llvm_unreachable("exceed declaration attribute kinds");
457+
458+
default:
459+
llvm_unreachable("handled before this switch");
450460
}
451461

452462
return true;
@@ -552,6 +562,8 @@ StringRef DeclAttribute::getAttrName() const {
552562
return "swift3_migration";
553563
case DAK_WarnUnusedResult:
554564
return "warn_unused_result";
565+
case DAK_Specialize:
566+
return "_specialize";
555567
}
556568
llvm_unreachable("bad DeclAttrKind");
557569
}
@@ -722,4 +734,26 @@ const AvailableAttr *AvailableAttr::isUnavailable(const Decl *D) {
722734
return D->getAttrs().getUnavailable(ctx);
723735
}
724736

737+
SpecializeAttr::SpecializeAttr(SourceLoc atLoc, SourceRange range,
738+
ArrayRef<TypeLoc> typeLocs)
739+
: DeclAttribute(DAK_Specialize, atLoc, range, /*Implicit=*/false),
740+
numTypes(typeLocs.size())
741+
{
742+
std::copy(typeLocs.begin(), typeLocs.end(), getTypeLocData());
743+
}
744+
745+
ArrayRef<TypeLoc> SpecializeAttr::getTypeLocs() const {
746+
return const_cast<SpecializeAttr*>(this)->getTypeLocs();
747+
}
725748

749+
MutableArrayRef<TypeLoc> SpecializeAttr::getTypeLocs() {
750+
return { this->getTypeLocData(), numTypes };
751+
}
752+
753+
SpecializeAttr *SpecializeAttr::create(ASTContext &Ctx, SourceLoc atLoc,
754+
SourceRange range,
755+
ArrayRef<TypeLoc> typeLocs) {
756+
unsigned size = sizeof(SpecializeAttr) + (typeLocs.size() * sizeof(TypeLoc));
757+
void *mem = Ctx.Allocate(size, alignof(SpecializeAttr));
758+
return new (mem) SpecializeAttr(atLoc, range, typeLocs);
759+
}

lib/Parse/ParseDecl.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,42 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
12911291
rParenLoc, false));
12921292
break;
12931293
}
1294+
case DAK_Specialize: {
1295+
if (Tok.isNot(tok::l_paren)) {
1296+
diagnose(Loc, diag::attr_expected_lparen, AttrName,
1297+
DeclAttribute::isDeclModifier(DK));
1298+
return false;
1299+
}
1300+
1301+
SourceLoc lParenLoc = consumeToken();
1302+
1303+
SmallVector<TypeLoc, 8> TypeList;
1304+
do {
1305+
// Parse the concrete nominal type.
1306+
ParserResult<TypeRepr> Ty = parseTypeIdentifier();
1307+
if (Ty.isNull()) {
1308+
skipUntil(tok::r_paren);
1309+
return false;
1310+
}
1311+
1312+
// Record the type.
1313+
TypeList.push_back(Ty.get());
1314+
1315+
// Check for a ',', which indicates that there are more protocols coming.
1316+
} while (consumeIf(tok::comma));
1317+
1318+
// Parse the closing ')'.
1319+
SourceLoc rParenLoc;
1320+
if (!consumeIf(tok::r_paren, rParenLoc)) {
1321+
diagnose(lParenLoc, diag::attr_expected_rparen, AttrName,
1322+
/*DeclModifier=*/false);
1323+
return false;
1324+
}
1325+
Attributes.add(
1326+
SpecializeAttr::create(Context, AtLoc, SourceRange(Loc, rParenLoc),
1327+
TypeList));
1328+
break;
1329+
}
12941330
}
12951331

12961332
if (DuplicateAttribute) {

0 commit comments

Comments
 (0)