Skip to content

Commit 888f675

Browse files
committed
Add @_used and @_section attributes for global variables and top-level functions
This adds: - @_used attribute that flags as a global variable or a top-level function as "do not dead-strip" via llvm.used, roughly the equivalent of __attribute__((used)) in C/C++. - @_section("...") attribute that places a global variable or a top-level function into a section with that name, roughly the equivalent of __attribute__((section("..."))) in C/C++.
1 parent 34faa58 commit 888f675

18 files changed

+309
-2
lines changed

include/swift/AST/Attr.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,24 @@ class SILGenNameAttr : public DeclAttribute {
502502
}
503503
};
504504

505+
/// Defines the @_section attribute.
506+
class SectionAttr : public DeclAttribute {
507+
public:
508+
SectionAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit)
509+
: DeclAttribute(DAK_Section, AtLoc, Range, Implicit),
510+
Name(Name) {}
511+
512+
SectionAttr(StringRef Name, bool Implicit)
513+
: SectionAttr(Name, SourceLoc(), SourceRange(), Implicit) {}
514+
515+
/// The section name.
516+
const StringRef Name;
517+
518+
static bool classof(const DeclAttribute *DA) {
519+
return DA->getKind() == DAK_Section;
520+
}
521+
};
522+
505523
/// Defines the @_cdecl attribute.
506524
class CDeclAttr : public DeclAttribute {
507525
public:

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,6 +1667,13 @@ ERROR(cdecl_empty_name,none,
16671667
ERROR(cdecl_throws,none,
16681668
"raising errors from @_cdecl functions is not supported", ())
16691669

1670+
ERROR(used_not_at_top_level,none,
1671+
"@_used can only be applied to global functions and variables", ())
1672+
ERROR(section_not_at_top_level,none,
1673+
"@_section can only be applied to global functions and variables", ())
1674+
ERROR(section_empty_name,none,
1675+
"@_section section name cannot be empty", ())
1676+
16701677
ERROR(expose_only_non_other_attr,none,
16711678
"@_expose attribute cannot be applied to an '%0' declaration", (StringRef))
16721679
ERROR(expose_inside_unexposed_decl,none,

include/swift/SIL/SILFunction.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,9 @@ class SILFunction
291291
/// The function's remaining set of specialize attributes.
292292
std::vector<SILSpecializeAttr*> SpecializeAttrSet;
293293

294+
/// Name of a section if @_section attribute was used, otherwise empty.
295+
StringRef Section;
296+
294297
/// Has value if there's a profile for this function
295298
/// Contains Function Entry Count
296299
ProfileCounter EntryCount;
@@ -346,6 +349,9 @@ class SILFunction
346349
/// would indicate.
347350
unsigned HasCReferences : 1;
348351

352+
/// Whether attribute @_used was present
353+
unsigned MarkedAsUsed : 1;
354+
349355
/// Whether cross-module references to this function should always use weak
350356
/// linking.
351357
unsigned IsAlwaysWeakImported : 1;
@@ -1234,6 +1240,14 @@ class SILFunction
12341240
return V && V->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>();
12351241
}
12361242

1243+
/// Return whether this function has attribute @_used on it
1244+
bool markedAsUsed() const { return MarkedAsUsed; }
1245+
void setMarkedAsUsed(bool value) { MarkedAsUsed = value; }
1246+
1247+
/// Return custom section name if @_section was used, otherwise empty
1248+
StringRef section() const { return Section; }
1249+
void setSection(StringRef value) { Section = value; }
1250+
12371251
/// Returns true if this function belongs to a declaration that returns
12381252
/// an opaque result type with one or more availability conditions that are
12391253
/// allowed to produce a different underlying type at runtime.

include/swift/SIL/SILGlobalVariable.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,21 @@ class SILGlobalVariable
200200
StaticInitializerBlock.eraseAllInstructions(Module);
201201
}
202202

203+
/// Returns true if this global variable has `@_used` attribute.
204+
bool markedAsUsed() const {
205+
auto *V = getDecl();
206+
return V && V->getAttrs().hasAttribute<UsedAttr>();
207+
}
208+
209+
/// Returns a SectionAttr if this global variable has `@_section` attribute.
210+
SectionAttr *getSectionAttr() const {
211+
auto *V = getDecl();
212+
if (!V)
213+
return nullptr;
214+
215+
return V->getAttrs().getAttribute<SectionAttr>();
216+
}
217+
203218
/// Return whether this variable corresponds to a Clang node.
204219
bool hasClangNode() const;
205220

lib/AST/Attr.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,11 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options,
11211121
Printer << ")";
11221122
break;
11231123

1124+
case DAK_Section:
1125+
Printer.printAttrName("@_section");
1126+
Printer << "(\"" << cast<SectionAttr>(this)->Name << "\")";
1127+
break;
1128+
11241129
case DAK_ObjC: {
11251130
Printer.printAttrName("@objc");
11261131
llvm::SmallString<32> scratch;
@@ -1593,6 +1598,8 @@ StringRef DeclAttribute::getAttrName() const {
15931598
return "backDeployed";
15941599
case DAK_Expose:
15951600
return "_expose";
1601+
case DAK_Section:
1602+
return "_section";
15961603
case DAK_Documentation:
15971604
return "_documentation";
15981605
case DAK_MacroRole:

lib/IRGen/GenDecl.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2705,6 +2705,12 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
27052705
gvar->setInitializer(llvm::Constant::getNullValue(storageTypeWithContainer));
27062706
else
27072707
gvar->setComdat(nullptr);
2708+
2709+
// Mark as llvm.used if @_used, set section if @_section
2710+
if (var->markedAsUsed())
2711+
addUsedGlobal(gvar);
2712+
if (auto *sectionAttr = var->getSectionAttr())
2713+
gvar->setSection(sectionAttr->Name);
27082714
}
27092715
llvm::Constant *addr = gvar;
27102716
if (var->isInitializedObject() && !canMakeStaticObjectsReadOnly()) {
@@ -3419,6 +3425,12 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
34193425
fn = createFunction(*this, link, signature, insertBefore,
34203426
f->getOptimizationMode(), shouldEmitStackProtector(f));
34213427

3428+
// Mark as llvm.used if @_used, set section if @_section
3429+
if (f->markedAsUsed())
3430+
addUsedGlobal(fn);
3431+
if (!f->section().empty())
3432+
fn->setSection(f->section());
3433+
34223434
// If `hasCReferences` is true, then the function is either marked with
34233435
// @_silgen_name OR @_cdecl. If it is the latter, it must have a definition
34243436
// associated with it. The combination of the two allows us to identify the

lib/Parse/ParseDecl.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2898,6 +2898,46 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
28982898

28992899
break;
29002900
}
2901+
2902+
case DAK_Section: {
2903+
if (!consumeIf(tok::l_paren)) {
2904+
diagnose(Loc, diag::attr_expected_lparen, AttrName,
2905+
DeclAttribute::isDeclModifier(DK));
2906+
return makeParserSuccess();
2907+
}
2908+
2909+
if (Tok.isNot(tok::string_literal)) {
2910+
diagnose(Loc, diag::attr_expected_string_literal, AttrName);
2911+
return makeParserSuccess();
2912+
}
2913+
2914+
auto Name = getStringLiteralIfNotInterpolated(
2915+
Loc, ("'" + AttrName + "'").str());
2916+
2917+
consumeToken(tok::string_literal);
2918+
2919+
if (Name.has_value())
2920+
AttrRange = SourceRange(Loc, Tok.getRange().getStart());
2921+
else
2922+
DiscardAttribute = true;
2923+
2924+
if (!consumeIf(tok::r_paren)) {
2925+
diagnose(Loc, diag::attr_expected_rparen, AttrName,
2926+
DeclAttribute::isDeclModifier(DK));
2927+
return makeParserSuccess();
2928+
}
2929+
2930+
// @_section in a local scope is not allowed.
2931+
if (CurDeclContext->isLocalContext()) {
2932+
diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName);
2933+
}
2934+
2935+
if (!DiscardAttribute)
2936+
Attributes.add(new (Context) SectionAttr(Name.value(), AtLoc,
2937+
AttrRange, /*Implicit=*/false));
2938+
2939+
break;
2940+
}
29012941

29022942
case DAK_Alignment: {
29032943
if (!consumeIf(tok::l_paren)) {

lib/SIL/IR/SILFunction.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ void SILFunction::init(
204204
this->InlineStrategy = inlineStrategy;
205205
this->Linkage = unsigned(Linkage);
206206
this->HasCReferences = false;
207+
this->MarkedAsUsed = false;
207208
this->IsAlwaysWeakImported = false;
208209
this->IsDynamicReplaceable = isDynamic;
209210
this->ExactSelfClass = isExactSelfClass;
@@ -280,11 +281,13 @@ void SILFunction::createSnapshot(int id) {
280281
newSnapshot->ObjCReplacementFor = ObjCReplacementFor;
281282
newSnapshot->SemanticsAttrSet = SemanticsAttrSet;
282283
newSnapshot->SpecializeAttrSet = SpecializeAttrSet;
284+
newSnapshot->Section = Section;
283285
newSnapshot->Availability = Availability;
284286
newSnapshot->specialPurpose = specialPurpose;
285287
newSnapshot->perfConstraints = perfConstraints;
286288
newSnapshot->GlobalInitFlag = GlobalInitFlag;
287289
newSnapshot->HasCReferences = HasCReferences;
290+
newSnapshot->MarkedAsUsed = MarkedAsUsed;
288291
newSnapshot->IsAlwaysWeakImported = IsAlwaysWeakImported;
289292
newSnapshot->HasOwnership = HasOwnership;
290293
newSnapshot->IsWithoutActuallyEscapingThunk = IsWithoutActuallyEscapingThunk;
@@ -858,6 +861,9 @@ SILFunction::isPossiblyUsedExternally() const {
858861
if (isRuntimeAccessible())
859862
return true;
860863

864+
if (markedAsUsed())
865+
return true;
866+
861867
// Declaration marked as `@_alwaysEmitIntoClient` that
862868
// returns opaque result type with availability conditions
863869
// has to be kept alive to emit opaque type metadata descriptor.

lib/SIL/IR/SILFunctionBuilder.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ void SILFunctionBuilder::addFunctionAttributes(
161161
if (Attrs.hasAttribute<SILGenNameAttr>() || Attrs.hasAttribute<CDeclAttr>())
162162
F->setHasCReferences(true);
163163

164+
if (Attrs.hasAttribute<UsedAttr>())
165+
F->setMarkedAsUsed(true);
166+
164167
if (Attrs.hasAttribute<NoLocksAttr>()) {
165168
F->setPerfConstraints(PerformanceConstraints::NoLocks);
166169
} else if (Attrs.hasAttribute<NoAllocationAttr>()) {
@@ -195,6 +198,12 @@ void SILFunctionBuilder::addFunctionAttributes(
195198
return;
196199
auto *decl = constant.getDecl();
197200

201+
// Don't add section for addressor functions (where decl is a global)
202+
if (isa<FuncDecl>(decl)) {
203+
if (auto *SA = Attrs.getAttribute<SectionAttr>())
204+
F->setSection(SA->Name);
205+
}
206+
198207
// Only emit replacements for the objc entry point of objc methods.
199208
// There is one exception: @_dynamicReplacement(for:) of @objc methods in
200209
// generic classes. In this special case we use native replacement instead of

lib/SIL/IR/SILPrinter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3236,6 +3236,12 @@ void SILFunction::print(SILPrintContext &PrintCtx) const {
32363236
OS << "[_specialize "; Attr->print(OS); OS << "] ";
32373237
}
32383238

3239+
if (markedAsUsed())
3240+
OS << "[used] ";
3241+
3242+
if (!section().empty())
3243+
OS << "[section \"" << section() << "\"] ";
3244+
32393245
// TODO: Handle clang node owners which don't have a name.
32403246
if (hasClangNode() && getClangNodeOwner()->hasName()) {
32413247
OS << "[clang ";

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,8 @@ static bool parseDeclSILOptional(bool *isTransparent,
10341034
Inline_t *inlineStrategy,
10351035
OptimizationMode *optimizationMode,
10361036
PerformanceConstraints *perfConstraints,
1037+
bool *markedAsUsed,
1038+
StringRef *section,
10371039
bool *isLet,
10381040
bool *isWeakImported,
10391041
bool *needStackProtection,
@@ -1122,6 +1124,23 @@ static bool parseDeclSILOptional(bool *isTransparent,
11221124
*perfConstraints = PerformanceConstraints::NoLocks;
11231125
else if (perfConstraints && SP.P.Tok.getText() == "no_allocation")
11241126
*perfConstraints = PerformanceConstraints::NoAllocation;
1127+
else if (markedAsUsed && SP.P.Tok.getText() == "used")
1128+
*markedAsUsed = true;
1129+
else if (section && SP.P.Tok.getText() == "section") {
1130+
SP.P.consumeToken(tok::identifier);
1131+
if (SP.P.Tok.getKind() != tok::string_literal) {
1132+
SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list);
1133+
return true;
1134+
}
1135+
1136+
// Drop the double quotes.
1137+
StringRef rawString = SP.P.Tok.getText().drop_front().drop_back();
1138+
*section = SP.P.Context.getIdentifier(rawString).str();
1139+
SP.P.consumeToken(tok::string_literal);
1140+
1141+
SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list);
1142+
continue;
1143+
}
11251144
else if (inlineStrategy && SP.P.Tok.getText() == "always_inline")
11261145
*inlineStrategy = AlwaysInline;
11271146
else if (MRK && SP.P.Tok.getText() == "readnone")
@@ -6929,6 +6948,8 @@ bool SILParserState::parseDeclSIL(Parser &P) {
69296948
Inline_t inlineStrategy = InlineDefault;
69306949
OptimizationMode optimizationMode = OptimizationMode::NotSet;
69316950
PerformanceConstraints perfConstr = PerformanceConstraints::None;
6951+
bool markedAsUsed = false;
6952+
StringRef section;
69326953
SmallVector<std::string, 1> Semantics;
69336954
SmallVector<ParsedSpecAttr, 4> SpecAttrs;
69346955
ValueDecl *ClangDecl = nullptr;
@@ -6943,7 +6964,8 @@ bool SILParserState::parseDeclSIL(Parser &P) {
69436964
&forceEnableLexicalLifetimes, &isExactSelfClass,
69446965
&DynamicallyReplacedFunction, &AdHocWitnessFunction,
69456966
&objCReplacementFor, &specialPurpose, &inlineStrategy,
6946-
&optimizationMode, &perfConstr, nullptr, &isWeakImported,
6967+
&optimizationMode, &perfConstr, &markedAsUsed, &section, nullptr,
6968+
&isWeakImported,
69476969
&needStackProtection, &availability, &isWithoutActuallyEscapingThunk,
69486970
&Semantics, &SpecAttrs, &ClangDecl, &MRK, FunctionState, M) ||
69496971
P.parseToken(tok::at_sign, diag::expected_sil_function_name) ||
@@ -7189,6 +7211,7 @@ bool SILParserState::parseSILGlobal(Parser &P) {
71897211
parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr,
71907212
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
71917213
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
7214+
nullptr, nullptr,
71927215
&isLet, nullptr, nullptr, nullptr, nullptr, nullptr,
71937216
nullptr, nullptr, nullptr, State, M) ||
71947217
P.parseToken(tok::at_sign, diag::expected_sil_value_name) ||
@@ -7240,6 +7263,7 @@ bool SILParserState::parseSILProperty(Parser &P) {
72407263
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr,
72417264
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
72427265
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
7266+
nullptr, nullptr,
72437267
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
72447268
nullptr, nullptr, nullptr, SP, M))
72457269
return true;
@@ -7309,6 +7333,7 @@ bool SILParserState::parseSILVTable(Parser &P) {
73097333
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr,
73107334
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
73117335
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
7336+
nullptr, nullptr,
73127337
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
73137338
nullptr, nullptr, nullptr, VTableState, M))
73147339
return true;
@@ -7419,6 +7444,7 @@ bool SILParserState::parseSILMoveOnlyDeinit(Parser &parser) {
74197444
if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr,
74207445
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
74217446
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
7447+
nullptr, nullptr,
74227448
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
74237449
nullptr, nullptr, nullptr, moveOnlyDeinitTableState,
74247450
M))
@@ -7906,6 +7932,7 @@ bool SILParserState::parseSILWitnessTable(Parser &P) {
79067932
if (parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr,
79077933
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
79087934
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
7935+
nullptr, nullptr,
79097936
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
79107937
nullptr, nullptr, nullptr, WitnessState, M))
79117938
return true;

lib/Sema/TypeCheckAttr.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
253253

254254
void visitCDeclAttr(CDeclAttr *attr);
255255
void visitExposeAttr(ExposeAttr *attr);
256+
void visitUsedAttr(UsedAttr *attr);
257+
void visitSectionAttr(SectionAttr *attr);
256258

257259
void visitDynamicCallableAttr(DynamicCallableAttr *attr);
258260

@@ -2068,6 +2070,22 @@ void AttributeChecker::visitExposeAttr(ExposeAttr *attr) {
20682070
}
20692071
}
20702072

2073+
void AttributeChecker::visitUsedAttr(UsedAttr *attr) {
2074+
// Only top-level func/var decls are currently supported.
2075+
if (D->getDeclContext()->isTypeContext())
2076+
diagnose(attr->getLocation(), diag::used_not_at_top_level);
2077+
}
2078+
2079+
void AttributeChecker::visitSectionAttr(SectionAttr *attr) {
2080+
// Only top-level func/var decls are currently supported.
2081+
if (D->getDeclContext()->isTypeContext())
2082+
diagnose(attr->getLocation(), diag::section_not_at_top_level);
2083+
2084+
// The name must not be empty.
2085+
if (attr->Name.empty())
2086+
diagnose(attr->getLocation(), diag::section_empty_name);
2087+
}
2088+
20712089
void AttributeChecker::visitUnsafeNoObjCTaggedPointerAttr(
20722090
UnsafeNoObjCTaggedPointerAttr *attr) {
20732091
// Only class protocols can have the attribute.

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,6 +1546,8 @@ namespace {
15461546
UNINTERESTING_ATTR(UsableFromInline)
15471547
UNINTERESTING_ATTR(ObjCNonLazyRealization)
15481548
UNINTERESTING_ATTR(UnsafeNoObjCTaggedPointer)
1549+
UNINTERESTING_ATTR(Used)
1550+
UNINTERESTING_ATTR(Section)
15491551
UNINTERESTING_ATTR(SwiftNativeObjCRuntimeBase)
15501552
UNINTERESTING_ATTR(ShowInInterface)
15511553
UNINTERESTING_ATTR(Specialize)

0 commit comments

Comments
 (0)