Skip to content

Commit 80222b8

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 9b3faad commit 80222b8

File tree

18 files changed

+309
-2
lines changed

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
@@ -1702,6 +1702,13 @@ ERROR(cdecl_empty_name,none,
17021702
ERROR(cdecl_throws,none,
17031703
"raising errors from @_cdecl functions is not supported", ())
17041704

1705+
ERROR(used_not_at_top_level,none,
1706+
"@_used can only be applied to global functions and variables", ())
1707+
ERROR(section_not_at_top_level,none,
1708+
"@_section can only be applied to global functions and variables", ())
1709+
ERROR(section_empty_name,none,
1710+
"@_section section name cannot be empty", ())
1711+
17051712
ERROR(expose_only_non_other_attr,none,
17061713
"@_expose attribute cannot be applied to an '%0' declaration", (StringRef))
17071714
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
@@ -190,6 +190,21 @@ class SILGlobalVariable
190190
StaticInitializerBlock.eraseAllInstructions(Module);
191191
}
192192

193+
/// Returns true if this global variable has `@_used` attribute.
194+
bool markedAsUsed() const {
195+
auto *V = getDecl();
196+
return V && V->getAttrs().hasAttribute<UsedAttr>();
197+
}
198+
199+
/// Returns a SectionAttr if this global variable has `@_section` attribute.
200+
SectionAttr *getSectionAttr() const {
201+
auto *V = getDecl();
202+
if (!V)
203+
return nullptr;
204+
205+
return V->getAttrs().getAttribute<SectionAttr>();
206+
}
207+
193208
/// Return whether this variable corresponds to a Clang node.
194209
bool hasClangNode() const;
195210

lib/AST/Attr.cpp

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

1133+
case DAK_Section:
1134+
Printer.printAttrName("@_section");
1135+
Printer << "(\"" << cast<SectionAttr>(this)->Name << "\")";
1136+
break;
1137+
11331138
case DAK_ObjC: {
11341139
Printer.printAttrName("@objc");
11351140
llvm::SmallString<32> scratch;
@@ -1602,6 +1607,8 @@ StringRef DeclAttribute::getAttrName() const {
16021607
return "backDeployed";
16031608
case DAK_Expose:
16041609
return "_expose";
1610+
case DAK_Section:
1611+
return "_section";
16051612
case DAK_Documentation:
16061613
return "_documentation";
16071614
case DAK_MacroRole:

lib/IRGen/GenDecl.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2699,6 +2699,12 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
26992699
}
27002700
if (!forDefinition)
27012701
gvar->setComdat(nullptr);
2702+
2703+
// Mark as llvm.used if @_used, set section if @_section
2704+
if (var->markedAsUsed())
2705+
addUsedGlobal(gvar);
2706+
if (auto *sectionAttr = var->getSectionAttr())
2707+
gvar->setSection(sectionAttr->Name);
27022708
}
27032709
if (forDefinition && !gvar->hasInitializer()) {
27042710
if (initVal) {
@@ -3468,6 +3474,12 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
34683474
fn = createFunction(*this, link, signature, insertBefore,
34693475
f->getOptimizationMode(), shouldEmitStackProtector(f));
34703476

3477+
// Mark as llvm.used if @_used, set section if @_section
3478+
if (f->markedAsUsed())
3479+
addUsedGlobal(fn);
3480+
if (!f->section().empty())
3481+
fn->setSection(f->section());
3482+
34713483
// If `hasCReferences` is true, then the function is either marked with
34723484
// @_silgen_name OR @_cdecl. If it is the latter, it must have a definition
34733485
// 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
@@ -2937,6 +2937,46 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes,
29372937

29382938
break;
29392939
}
2940+
2941+
case DAK_Section: {
2942+
if (!consumeIf(tok::l_paren)) {
2943+
diagnose(Loc, diag::attr_expected_lparen, AttrName,
2944+
DeclAttribute::isDeclModifier(DK));
2945+
return makeParserSuccess();
2946+
}
2947+
2948+
if (Tok.isNot(tok::string_literal)) {
2949+
diagnose(Loc, diag::attr_expected_string_literal, AttrName);
2950+
return makeParserSuccess();
2951+
}
2952+
2953+
auto Name = getStringLiteralIfNotInterpolated(
2954+
Loc, ("'" + AttrName + "'").str());
2955+
2956+
consumeToken(tok::string_literal);
2957+
2958+
if (Name.has_value())
2959+
AttrRange = SourceRange(Loc, Tok.getRange().getStart());
2960+
else
2961+
DiscardAttribute = true;
2962+
2963+
if (!consumeIf(tok::r_paren)) {
2964+
diagnose(Loc, diag::attr_expected_rparen, AttrName,
2965+
DeclAttribute::isDeclModifier(DK));
2966+
return makeParserSuccess();
2967+
}
2968+
2969+
// @_section in a local scope is not allowed.
2970+
if (CurDeclContext->isLocalContext()) {
2971+
diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName);
2972+
}
2973+
2974+
if (!DiscardAttribute)
2975+
Attributes.add(new (Context) SectionAttr(Name.value(), AtLoc,
2976+
AttrRange, /*Implicit=*/false));
2977+
2978+
break;
2979+
}
29402980

29412981
case DAK_Alignment: {
29422982
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)