Skip to content

Commit 6f5aa77

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 2407256 commit 6f5aa77

File tree

15 files changed

+216
-1
lines changed

15 files changed

+216
-1
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: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,29 @@ class SILFunction
12341234
return V && V->getAttrs().hasAttribute<AlwaysEmitIntoClientAttr>();
12351235
}
12361236

1237+
/// Returns true if this function belongs to a declaration that
1238+
/// has `@_used` attribute.
1239+
bool markedAsUsed() const {
1240+
if (!hasLocation())
1241+
return false;
1242+
1243+
auto *V = getLocation().getAsASTNode<ValueDecl>();
1244+
return V && V->getAttrs().hasAttribute<UsedAttr>();
1245+
}
1246+
1247+
/// Returns a SectionAttr if this function belongs to a declaration that
1248+
/// has `@_section` attribute.
1249+
SectionAttr *getSectionAttr() const {
1250+
if (!hasLocation())
1251+
return nullptr;
1252+
1253+
auto *V = getLocation().getAsASTNode<ValueDecl>();
1254+
if (!V)
1255+
return nullptr;
1256+
1257+
return V->getAttrs().getAttribute<SectionAttr>();
1258+
}
1259+
12371260
/// Returns true if this function belongs to a declaration that returns
12381261
/// an opaque result type with one or more availability conditions that are
12391262
/// 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
@@ -2692,6 +2692,12 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
26922692
gvar->setInitializer(llvm::Constant::getNullValue(storageTypeWithContainer));
26932693
else
26942694
gvar->setComdat(nullptr);
2695+
2696+
// Mark as llvm.used if @_used, set section if @_section
2697+
if (var->markedAsUsed())
2698+
addUsedGlobal(gvar);
2699+
if (auto *sectionAttr = var->getSectionAttr())
2700+
gvar->setSection(sectionAttr->Name);
26952701
}
26962702
llvm::Constant *addr = gvar;
26972703
if (var->isInitializedObject() && !canMakeStaticObjectsReadOnly()) {
@@ -3406,6 +3412,12 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
34063412
fn = createFunction(*this, link, signature, insertBefore,
34073413
f->getOptimizationMode(), shouldEmitStackProtector(f));
34083414

3415+
// Mark as llvm.used if @_used, set section if @_section
3416+
if (f->markedAsUsed())
3417+
addUsedGlobal(fn);
3418+
if (auto *sectionAttr = f->getSectionAttr())
3419+
fn->setSection(sectionAttr->Name);
3420+
34093421
// If `hasCReferences` is true, then the function is either marked with
34103422
// @_silgen_name OR @_cdecl. If it is the latter, it must have a definition
34113423
// 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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,9 @@ SILFunction::isPossiblyUsedExternally() const {
858858
if (isRuntimeAccessible())
859859
return true;
860860

861+
if (markedAsUsed())
862+
return true;
863+
861864
// Declaration marked as `@_alwaysEmitIntoClient` that
862865
// returns opaque result type with availability conditions
863866
// has to be kept alive to emit opaque type metadata descriptor.

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)

lib/Serialization/ModuleFormat.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 781; // compound introduced names
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 782; // used and section attrs
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///
@@ -2031,6 +2031,12 @@ namespace decls_block {
20312031
BCBlob // _silgen_name
20322032
>;
20332033

2034+
using SectionDeclAttrLayout = BCRecordLayout<
2035+
Section_DECL_ATTR,
2036+
BCFixed<1>, // implicit flag
2037+
BCBlob // _section
2038+
>;
2039+
20342040
using CDeclDeclAttrLayout = BCRecordLayout<
20352041
CDecl_DECL_ATTR,
20362042
BCFixed<1>, // implicit flag

lib/Serialization/Serialization.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3047,6 +3047,15 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
30473047
return;
30483048
}
30493049

3050+
case DAK_Section: {
3051+
auto *theAttr = cast<SectionAttr>(DA);
3052+
auto abbrCode = S.DeclTypeAbbrCodes[SectionDeclAttrLayout::Code];
3053+
SectionDeclAttrLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode,
3054+
theAttr->isImplicit(),
3055+
theAttr->Name);
3056+
return;
3057+
}
3058+
30503059
case DAK_Documentation: {
30513060
auto *theAttr = cast<DocumentationAttr>(DA);
30523061
auto abbrCode = S.DeclTypeAbbrCodes[DocumentationDeclAttrLayout::Code];

test/IRGen/section.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-swift-frontend -primary-file %s -emit-sil | %FileCheck %s --check-prefix=SIL
2+
// RUN: %target-swift-frontend -primary-file %s -emit-ir | %FileCheck %s --check-prefix=IR
3+
4+
// REQUIRES: swift_in_compiler
5+
6+
@_section("__TEXT,__mysection") var g0: Int = 1
7+
@_section("__TEXT,__mysection") var g1: (Int, Int) = (42, 43)
8+
@_section("__TEXT,__mysection") var g2: Bool = true
9+
@_section("__TEXT,__mysection") func foo() {}
10+
11+
// SIL: @_section("__TEXT,__mysection") @_hasStorage @_hasInitialValue var g0: Int { get set }
12+
// SIL: @_section("__TEXT,__mysection") @_hasStorage @_hasInitialValue var g1: (Int, Int) { get set }
13+
// SIL: @_section("__TEXT,__mysection") @_hasStorage @_hasInitialValue var g2: Bool { get set }
14+
// SIL: @_section("__TEXT,__mysection") func foo()
15+
16+
// IR: @"$s7section2g0Sivp" = hidden global %TSi {{.*}}section "__TEXT,__mysection"
17+
// IR: @"$s7section2g1Si_Sitvp" = hidden global <{ %TSi, %TSi }> {{.*}}section "__TEXT,__mysection"
18+
// IR: @"$s7section2g2Sbvp" = hidden global %TSb {{.*}}section "__TEXT,__mysection"
19+
// IR: define {{.*}}@"$s7section3fooyyF"(){{.*}} section "__TEXT,__mysection"

test/IRGen/used.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-swift-frontend -primary-file %s -emit-sil | %FileCheck %s --check-prefix=SIL
2+
// RUN: %target-swift-frontend -primary-file %s -O -emit-sil | %FileCheck %s --check-prefix=SIL
3+
// RUN: %target-swift-frontend -primary-file %s -emit-ir | %FileCheck %s --check-prefix=IR
4+
// RUN: %target-swift-frontend -primary-file %s -O -emit-ir | %FileCheck %s --check-prefix=IR
5+
6+
// REQUIRES: swift_in_compiler
7+
8+
@_used var g0: Int = 1
9+
@_used var g1: (Int, Int) = (42, 43)
10+
@_used var g2: Bool = true
11+
@_used func foo() {}
12+
13+
// SIL: @_used @_hasStorage @_hasInitialValue var g0: Int { get set }
14+
// SIL: @_used @_hasStorage @_hasInitialValue var g1: (Int, Int) { get set }
15+
// SIL: @_used @_hasStorage @_hasInitialValue var g2: Bool { get set }
16+
// SIL: @_used func foo()
17+
18+
// IR: @llvm.used = appending global [{{.*}} x i8*] [
19+
// IR-SAME: i8* bitcast (%TSi* @"$s4used2g0Sivp" to i8*)
20+
// IR-SAME: i8* bitcast (<{ %TSi, %TSi }>* @"$s4used2g1Si_Sitvp" to i8*)
21+
// IR-SAME: i8* bitcast (%TSb* @"$s4used2g2Sbvp" to i8*)

utils/gyb_syntax_support/AttributeKinds.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,21 @@ def __init__(self, name, swift_name=None):
922922
ABIStableToAdd, ABIStableToRemove, APIStableToAdd, APIBreakingToRemove, # noqa: E501
923923
code=142),
924924
DeclAttributeAlias('freestanding', 'MacroRole'),
925+
926+
SimpleDeclAttribute('_used', 'Used',
927+
OnAbstractFunction, OnVar,
928+
UserInaccessible,
929+
ABIBreakingToAdd, ABIBreakingToRemove,
930+
APIBreakingToAdd, APIBreakingToRemove,
931+
code=143),
932+
933+
DeclAttribute('_section', 'Section',
934+
OnAbstractFunction, OnVar,
935+
UserInaccessible,
936+
ABIBreakingToAdd, ABIBreakingToRemove,
937+
APIBreakingToAdd, APIBreakingToRemove,
938+
code=144),
939+
925940
]
926941

927942
DEPRECATED_MODIFIER_KINDS = [

0 commit comments

Comments
 (0)