Skip to content

Commit 2d5f33e

Browse files
authored
Add @_used and @_section attributes for global variables and top-level functions (swiftlang#65901)
* 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 ff696c4 commit 2d5f33e

24 files changed

+355
-2
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,12 @@ Fully bypasses access control, allowing access to private declarations
832832
in the imported module. The imported module needs to be compiled with
833833
`-Xfrontend -enable-private-imports` for this to work.
834834

835+
## `@_section("section_name")`
836+
837+
Places a global variable or a top-level function into a section of the object
838+
file with the given name. It's the equivalent of clang's
839+
`__attribute__((section))`.
840+
835841
## `@_semantics("uniquely.recognized.id")`
836842

837843
Allows the optimizer to make use of some key invariants in performance critical
@@ -994,6 +1000,12 @@ for more details.
9941000

9951001
This `async` function uses the pre-SE-0338 semantics of unsafely inheriting the caller's executor. This is an underscored feature because the right way of inheriting an executor is to pass in the required executor and switch to it. Unfortunately, there are functions in the standard library which need to inherit their caller's executor but cannot change their ABI because they were not defined as `@_alwaysEmitIntoClient` in the initial release.
9961002

1003+
## `@_used`
1004+
1005+
Marks a global variable or a top-level function as "used externally" even if it
1006+
does not have visible users in the compilation unit. It's the equivalent of
1007+
clang's `__attribute__((used))`.
1008+
9971009
## `@_weakLinked`
9981010

9991011
Allows a declaration to be weakly-referenced, i.e., any references emitted by

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/DiagnosticsSIL.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,8 @@ ERROR(performance_unknown_callees,none,
332332
"called function is not known at compile time and can have unpredictable performance", ())
333333
ERROR(performance_callee_unavailable,none,
334334
"called function is not available in this module and can have unpredictable performance", ())
335+
ERROR(section_attr_on_non_const_global,none,
336+
"global variable must be a compile-time constant to use @_section attribute", ())
335337
NOTE(performance_called_from,none,
336338
"called from here", ())
337339

include/swift/AST/DiagnosticsSema.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,16 @@ ERROR(cdecl_empty_name,none,
17021702
ERROR(cdecl_throws,none,
17031703
"raising errors from @_cdecl functions is not supported", ())
17041704

1705+
// @_used and @_section
1706+
ERROR(section_linkage_markers_disabled,none,
1707+
"attribute requires '-enable-experimental-feature SymbolLinkageMarkers'", ())
1708+
ERROR(used_not_at_top_level,none,
1709+
"@_used can only be applied to global functions and variables", ())
1710+
ERROR(section_not_at_top_level,none,
1711+
"@_section can only be applied to global functions and variables", ())
1712+
ERROR(section_empty_name,none,
1713+
"@_section section name cannot be empty", ())
1714+
17051715
ERROR(expose_only_non_other_attr,none,
17061716
"@_expose attribute cannot be applied to an '%0' declaration", (StringRef))
17071717
ERROR(expose_inside_unexposed_decl,none,

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ EXPERIMENTAL_FEATURE(FlowSensitiveConcurrencyCaptures, false)
122122
EXPERIMENTAL_FEATURE(CodeItemMacros, true)
123123
EXPERIMENTAL_FEATURE(TupleConformances, false)
124124

125+
// Whether to enable @_used and @_section attributes
126+
EXPERIMENTAL_FEATURE(SymbolLinkageMarkers, true)
127+
125128
// FIXME: MoveOnlyClasses is not intended to be in production,
126129
// but our tests currently rely on it, and we want to run those
127130
// tests in non-asserts builds too.

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/ASTPrinter.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3211,6 +3211,17 @@ static bool usesFeatureTupleConformances(Decl *decl) {
32113211
return false;
32123212
}
32133213

3214+
static bool usesFeatureSymbolLinkageMarkers(Decl *decl) {
3215+
auto &attrs = decl->getAttrs();
3216+
return std::any_of(attrs.begin(), attrs.end(), [](auto *attr) {
3217+
if (isa<UsedAttr>(attr))
3218+
return true;
3219+
if (isa<SectionAttr>(attr))
3220+
return true;
3221+
return false;
3222+
});
3223+
}
3224+
32143225
static bool usesFeatureLayoutPrespecialization(Decl *decl) {
32153226
auto &attrs = decl->getAttrs();
32163227
return std::any_of(attrs.begin(), attrs.end(), [](auto *attr) {

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 ";

0 commit comments

Comments
 (0)