diff --git a/docs/ReferenceGuides/UnderscoredAttributes.md b/docs/ReferenceGuides/UnderscoredAttributes.md index ff51b7fe45eb0..5d01013af183e 100644 --- a/docs/ReferenceGuides/UnderscoredAttributes.md +++ b/docs/ReferenceGuides/UnderscoredAttributes.md @@ -832,6 +832,12 @@ Fully bypasses access control, allowing access to private declarations in the imported module. The imported module needs to be compiled with `-Xfrontend -enable-private-imports` for this to work. +## `@_section("section_name")` + +Places a global variable or a top-level function into a section of the object +file with the given name. It's the equivalent of clang's +`__attribute__((section))`. + ## `@_semantics("uniquely.recognized.id")` Allows the optimizer to make use of some key invariants in performance critical @@ -994,6 +1000,12 @@ for more details. 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. +## `@_used` + +Marks a global variable or a top-level function as "used externally" even if it +does not have visible users in the compilation unit. It's the equivalent of +clang's `__attribute__((used))`. + ## `@_weakLinked` Allows a declaration to be weakly-referenced, i.e., any references emitted by diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index cacb5faa4e36e..c8ff55152b424 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -502,6 +502,24 @@ class SILGenNameAttr : public DeclAttribute { } }; +/// Defines the @_section attribute. +class SectionAttr : public DeclAttribute { +public: + SectionAttr(StringRef Name, SourceLoc AtLoc, SourceRange Range, bool Implicit) + : DeclAttribute(DAK_Section, AtLoc, Range, Implicit), + Name(Name) {} + + SectionAttr(StringRef Name, bool Implicit) + : SectionAttr(Name, SourceLoc(), SourceRange(), Implicit) {} + + /// The section name. + const StringRef Name; + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DAK_Section; + } +}; + /// Defines the @_cdecl attribute. class CDeclAttr : public DeclAttribute { public: diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index 49716b7874b97..310c336a8f24f 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -332,6 +332,8 @@ ERROR(performance_unknown_callees,none, "called function is not known at compile time and can have unpredictable performance", ()) ERROR(performance_callee_unavailable,none, "called function is not available in this module and can have unpredictable performance", ()) +ERROR(section_attr_on_non_const_global,none, + "global variable must be a compile-time constant to use @_section attribute", ()) NOTE(performance_called_from,none, "called from here", ()) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 3efaf535c2739..bdf0f5157877a 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -1702,6 +1702,16 @@ ERROR(cdecl_empty_name,none, ERROR(cdecl_throws,none, "raising errors from @_cdecl functions is not supported", ()) +// @_used and @_section +ERROR(section_linkage_markers_disabled,none, + "attribute requires '-enable-experimental-feature SymbolLinkageMarkers'", ()) +ERROR(used_not_at_top_level,none, + "@_used can only be applied to global functions and variables", ()) +ERROR(section_not_at_top_level,none, + "@_section can only be applied to global functions and variables", ()) +ERROR(section_empty_name,none, + "@_section section name cannot be empty", ()) + ERROR(expose_only_non_other_attr,none, "@_expose attribute cannot be applied to an '%0' declaration", (StringRef)) ERROR(expose_inside_unexposed_decl,none, diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index bb621bd5d2168..863f67879ecb4 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -122,6 +122,9 @@ EXPERIMENTAL_FEATURE(FlowSensitiveConcurrencyCaptures, false) EXPERIMENTAL_FEATURE(CodeItemMacros, true) EXPERIMENTAL_FEATURE(TupleConformances, false) +// Whether to enable @_used and @_section attributes +EXPERIMENTAL_FEATURE(SymbolLinkageMarkers, true) + // FIXME: MoveOnlyClasses is not intended to be in production, // but our tests currently rely on it, and we want to run those // tests in non-asserts builds too. diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index bad7f11dd450b..0eb580a9864c7 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -291,6 +291,9 @@ class SILFunction /// The function's remaining set of specialize attributes. std::vector SpecializeAttrSet; + /// Name of a section if @_section attribute was used, otherwise empty. + StringRef Section; + /// Has value if there's a profile for this function /// Contains Function Entry Count ProfileCounter EntryCount; @@ -346,6 +349,9 @@ class SILFunction /// would indicate. unsigned HasCReferences : 1; + /// Whether attribute @_used was present + unsigned MarkedAsUsed : 1; + /// Whether cross-module references to this function should always use weak /// linking. unsigned IsAlwaysWeakImported : 1; @@ -1234,6 +1240,14 @@ class SILFunction return V && V->getAttrs().hasAttribute(); } + /// Return whether this function has attribute @_used on it + bool markedAsUsed() const { return MarkedAsUsed; } + void setMarkedAsUsed(bool value) { MarkedAsUsed = value; } + + /// Return custom section name if @_section was used, otherwise empty + StringRef section() const { return Section; } + void setSection(StringRef value) { Section = value; } + /// Returns true if this function belongs to a declaration that returns /// an opaque result type with one or more availability conditions that are /// allowed to produce a different underlying type at runtime. diff --git a/include/swift/SIL/SILGlobalVariable.h b/include/swift/SIL/SILGlobalVariable.h index 88661af4443e7..b6fefbdb3084c 100644 --- a/include/swift/SIL/SILGlobalVariable.h +++ b/include/swift/SIL/SILGlobalVariable.h @@ -190,6 +190,21 @@ class SILGlobalVariable StaticInitializerBlock.eraseAllInstructions(Module); } + /// Returns true if this global variable has `@_used` attribute. + bool markedAsUsed() const { + auto *V = getDecl(); + return V && V->getAttrs().hasAttribute(); + } + + /// Returns a SectionAttr if this global variable has `@_section` attribute. + SectionAttr *getSectionAttr() const { + auto *V = getDecl(); + if (!V) + return nullptr; + + return V->getAttrs().getAttribute(); + } + /// Return whether this variable corresponds to a Clang node. bool hasClangNode() const; diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 38c75481bcbea..0eb2e621ea8b5 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3211,6 +3211,17 @@ static bool usesFeatureTupleConformances(Decl *decl) { return false; } +static bool usesFeatureSymbolLinkageMarkers(Decl *decl) { + auto &attrs = decl->getAttrs(); + return std::any_of(attrs.begin(), attrs.end(), [](auto *attr) { + if (isa(attr)) + return true; + if (isa(attr)) + return true; + return false; + }); +} + static bool usesFeatureLayoutPrespecialization(Decl *decl) { auto &attrs = decl->getAttrs(); return std::any_of(attrs.begin(), attrs.end(), [](auto *attr) { diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 67ae2f398014c..ef1836f4df717 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1130,6 +1130,11 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, Printer << ")"; break; + case DAK_Section: + Printer.printAttrName("@_section"); + Printer << "(\"" << cast(this)->Name << "\")"; + break; + case DAK_ObjC: { Printer.printAttrName("@objc"); llvm::SmallString<32> scratch; @@ -1602,6 +1607,8 @@ StringRef DeclAttribute::getAttrName() const { return "backDeployed"; case DAK_Expose: return "_expose"; + case DAK_Section: + return "_section"; case DAK_Documentation: return "_documentation"; case DAK_MacroRole: diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 97608122f68df..7245fb3074cfa 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -2699,6 +2699,12 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var, } if (!forDefinition) gvar->setComdat(nullptr); + + // Mark as llvm.used if @_used, set section if @_section + if (var->markedAsUsed()) + addUsedGlobal(gvar); + if (auto *sectionAttr = var->getSectionAttr()) + gvar->setSection(sectionAttr->Name); } if (forDefinition && !gvar->hasInitializer()) { if (initVal) { @@ -3468,6 +3474,12 @@ llvm::Function *IRGenModule::getAddrOfSILFunction( fn = createFunction(*this, link, signature, insertBefore, f->getOptimizationMode(), shouldEmitStackProtector(f)); + // Mark as llvm.used if @_used, set section if @_section + if (f->markedAsUsed()) + addUsedGlobal(fn); + if (!f->section().empty()) + fn->setSection(f->section()); + // If `hasCReferences` is true, then the function is either marked with // @_silgen_name OR @_cdecl. If it is the latter, it must have a definition // associated with it. The combination of the two allows us to identify the diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index edbf1cb0e8895..c96d6d97ef174 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2937,6 +2937,46 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, break; } + + case DAK_Section: { + if (!consumeIf(tok::l_paren)) { + diagnose(Loc, diag::attr_expected_lparen, AttrName, + DeclAttribute::isDeclModifier(DK)); + return makeParserSuccess(); + } + + if (Tok.isNot(tok::string_literal)) { + diagnose(Loc, diag::attr_expected_string_literal, AttrName); + return makeParserSuccess(); + } + + auto Name = getStringLiteralIfNotInterpolated( + Loc, ("'" + AttrName + "'").str()); + + consumeToken(tok::string_literal); + + if (Name.has_value()) + AttrRange = SourceRange(Loc, Tok.getRange().getStart()); + else + DiscardAttribute = true; + + if (!consumeIf(tok::r_paren)) { + diagnose(Loc, diag::attr_expected_rparen, AttrName, + DeclAttribute::isDeclModifier(DK)); + return makeParserSuccess(); + } + + // @_section in a local scope is not allowed. + if (CurDeclContext->isLocalContext()) { + diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName); + } + + if (!DiscardAttribute) + Attributes.add(new (Context) SectionAttr(Name.value(), AtLoc, + AttrRange, /*Implicit=*/false)); + + break; + } case DAK_Alignment: { if (!consumeIf(tok::l_paren)) { diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index eca0c14b3e613..1d115c63d1e24 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -204,6 +204,7 @@ void SILFunction::init( this->InlineStrategy = inlineStrategy; this->Linkage = unsigned(Linkage); this->HasCReferences = false; + this->MarkedAsUsed = false; this->IsAlwaysWeakImported = false; this->IsDynamicReplaceable = isDynamic; this->ExactSelfClass = isExactSelfClass; @@ -280,11 +281,13 @@ void SILFunction::createSnapshot(int id) { newSnapshot->ObjCReplacementFor = ObjCReplacementFor; newSnapshot->SemanticsAttrSet = SemanticsAttrSet; newSnapshot->SpecializeAttrSet = SpecializeAttrSet; + newSnapshot->Section = Section; newSnapshot->Availability = Availability; newSnapshot->specialPurpose = specialPurpose; newSnapshot->perfConstraints = perfConstraints; newSnapshot->GlobalInitFlag = GlobalInitFlag; newSnapshot->HasCReferences = HasCReferences; + newSnapshot->MarkedAsUsed = MarkedAsUsed; newSnapshot->IsAlwaysWeakImported = IsAlwaysWeakImported; newSnapshot->HasOwnership = HasOwnership; newSnapshot->IsWithoutActuallyEscapingThunk = IsWithoutActuallyEscapingThunk; @@ -858,6 +861,9 @@ SILFunction::isPossiblyUsedExternally() const { if (isRuntimeAccessible()) return true; + if (markedAsUsed()) + return true; + // Declaration marked as `@_alwaysEmitIntoClient` that // returns opaque result type with availability conditions // has to be kept alive to emit opaque type metadata descriptor. diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index e61e3b598454a..9f667321330cb 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -161,6 +161,9 @@ void SILFunctionBuilder::addFunctionAttributes( if (Attrs.hasAttribute() || Attrs.hasAttribute()) F->setHasCReferences(true); + if (Attrs.hasAttribute()) + F->setMarkedAsUsed(true); + if (Attrs.hasAttribute()) { F->setPerfConstraints(PerformanceConstraints::NoLocks); } else if (Attrs.hasAttribute()) { @@ -195,6 +198,12 @@ void SILFunctionBuilder::addFunctionAttributes( return; auto *decl = constant.getDecl(); + // Don't add section for addressor functions (where decl is a global) + if (isa(decl)) { + if (auto *SA = Attrs.getAttribute()) + F->setSection(SA->Name); + } + // Only emit replacements for the objc entry point of objc methods. // There is one exception: @_dynamicReplacement(for:) of @objc methods in // generic classes. In this special case we use native replacement instead of diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index f85b8479b8a9d..1c70a65d4ac13 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -3236,6 +3236,12 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { OS << "[_specialize "; Attr->print(OS); OS << "] "; } + if (markedAsUsed()) + OS << "[used] "; + + if (!section().empty()) + OS << "[section \"" << section() << "\"] "; + // TODO: Handle clang node owners which don't have a name. if (hasClangNode() && getClangNodeOwner()->hasName()) { OS << "[clang "; diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index c393eb48b789e..adcbaf884ca3f 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -1034,6 +1034,8 @@ static bool parseDeclSILOptional(bool *isTransparent, Inline_t *inlineStrategy, OptimizationMode *optimizationMode, PerformanceConstraints *perfConstraints, + bool *markedAsUsed, + StringRef *section, bool *isLet, bool *isWeakImported, bool *needStackProtection, @@ -1122,6 +1124,23 @@ static bool parseDeclSILOptional(bool *isTransparent, *perfConstraints = PerformanceConstraints::NoLocks; else if (perfConstraints && SP.P.Tok.getText() == "no_allocation") *perfConstraints = PerformanceConstraints::NoAllocation; + else if (markedAsUsed && SP.P.Tok.getText() == "used") + *markedAsUsed = true; + else if (section && SP.P.Tok.getText() == "section") { + SP.P.consumeToken(tok::identifier); + if (SP.P.Tok.getKind() != tok::string_literal) { + SP.P.diagnose(SP.P.Tok, diag::expected_in_attribute_list); + return true; + } + + // Drop the double quotes. + StringRef rawString = SP.P.Tok.getText().drop_front().drop_back(); + *section = SP.P.Context.getIdentifier(rawString).str(); + SP.P.consumeToken(tok::string_literal); + + SP.P.parseToken(tok::r_square, diag::expected_in_attribute_list); + continue; + } else if (inlineStrategy && SP.P.Tok.getText() == "always_inline") *inlineStrategy = AlwaysInline; else if (MRK && SP.P.Tok.getText() == "readnone") @@ -6929,6 +6948,8 @@ bool SILParserState::parseDeclSIL(Parser &P) { Inline_t inlineStrategy = InlineDefault; OptimizationMode optimizationMode = OptimizationMode::NotSet; PerformanceConstraints perfConstr = PerformanceConstraints::None; + bool markedAsUsed = false; + StringRef section; SmallVector Semantics; SmallVector SpecAttrs; ValueDecl *ClangDecl = nullptr; @@ -6943,7 +6964,8 @@ bool SILParserState::parseDeclSIL(Parser &P) { &forceEnableLexicalLifetimes, &isExactSelfClass, &DynamicallyReplacedFunction, &AdHocWitnessFunction, &objCReplacementFor, &specialPurpose, &inlineStrategy, - &optimizationMode, &perfConstr, nullptr, &isWeakImported, + &optimizationMode, &perfConstr, &markedAsUsed, §ion, nullptr, + &isWeakImported, &needStackProtection, &availability, &isWithoutActuallyEscapingThunk, &Semantics, &SpecAttrs, &ClangDecl, &MRK, FunctionState, M) || P.parseToken(tok::at_sign, diag::expected_sil_function_name) || @@ -7189,6 +7211,7 @@ bool SILParserState::parseSILGlobal(Parser &P) { parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, &isLet, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, State, M) || P.parseToken(tok::at_sign, diag::expected_sil_value_name) || @@ -7240,6 +7263,7 @@ bool SILParserState::parseSILProperty(Parser &P) { if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, SP, M)) return true; @@ -7309,6 +7333,7 @@ bool SILParserState::parseSILVTable(Parser &P) { if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, VTableState, M)) return true; @@ -7419,6 +7444,7 @@ bool SILParserState::parseSILMoveOnlyDeinit(Parser &parser) { if (parseDeclSILOptional(nullptr, &Serialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, moveOnlyDeinitTableState, M)) @@ -7906,6 +7932,7 @@ bool SILParserState::parseSILWitnessTable(Parser &P) { if (parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, WitnessState, M)) return true; diff --git a/lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp b/lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp index b0b06d412619f..04c50546ccf65 100644 --- a/lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp +++ b/lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp @@ -476,6 +476,14 @@ class PerformanceDiagnosticsPass : public SILModuleTransform { SILModule *module = getModule(); PerformanceDiagnostics diagnoser(*module, getAnalysis()); + + // Check that @_section is only on constant globals + for (SILGlobalVariable &g : module->getSILGlobals()) { + if (!g.getStaticInitializerValue() && g.getSectionAttr()) + module->getASTContext().Diags.diagnose( + g.getDecl()->getLoc(), diag::section_attr_on_non_const_global); + } + bool annotatedFunctionsFound = false; for (SILFunction &function : *module) { diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index f46306bd76ca1..93ca4513b7c60 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -253,6 +253,8 @@ class AttributeChecker : public AttributeVisitor { void visitCDeclAttr(CDeclAttr *attr); void visitExposeAttr(ExposeAttr *attr); + void visitUsedAttr(UsedAttr *attr); + void visitSectionAttr(SectionAttr *attr); void visitDynamicCallableAttr(DynamicCallableAttr *attr); @@ -2068,6 +2070,32 @@ void AttributeChecker::visitExposeAttr(ExposeAttr *attr) { } } +void AttributeChecker::visitUsedAttr(UsedAttr *attr) { + if (!Ctx.LangOpts.hasFeature(Feature::SymbolLinkageMarkers)) { + diagnoseAndRemoveAttr(attr, diag::section_linkage_markers_disabled); + return; + } + + // Only top-level func/var decls are currently supported. + if (D->getDeclContext()->isTypeContext()) + diagnose(attr->getLocation(), diag::used_not_at_top_level); +} + +void AttributeChecker::visitSectionAttr(SectionAttr *attr) { + if (!Ctx.LangOpts.hasFeature(Feature::SymbolLinkageMarkers)) { + diagnoseAndRemoveAttr(attr, diag::section_linkage_markers_disabled); + return; + } + + // Only top-level func/var decls are currently supported. + if (D->getDeclContext()->isTypeContext()) + diagnose(attr->getLocation(), diag::section_not_at_top_level); + + // The name must not be empty. + if (attr->Name.empty()) + diagnose(attr->getLocation(), diag::section_empty_name); +} + void AttributeChecker::visitUnsafeNoObjCTaggedPointerAttr( UnsafeNoObjCTaggedPointerAttr *attr) { // Only class protocols can have the attribute. diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 426e577764e4f..3c341969b2f8f 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1546,6 +1546,8 @@ namespace { UNINTERESTING_ATTR(UsableFromInline) UNINTERESTING_ATTR(ObjCNonLazyRealization) UNINTERESTING_ATTR(UnsafeNoObjCTaggedPointer) + UNINTERESTING_ATTR(Used) + UNINTERESTING_ATTR(Section) UNINTERESTING_ATTR(SwiftNativeObjCRuntimeBase) UNINTERESTING_ATTR(ShowInInterface) UNINTERESTING_ATTR(Specialize) diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 9c0731b1b5315..23913fcff7c91 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 783; // pack element type +const uint16_t SWIFTMODULE_VERSION_MINOR = 784; // used and section attrs /// A standard hash seed used for all string hashes in a serialized module. /// @@ -2037,6 +2037,12 @@ namespace decls_block { BCBlob // _silgen_name >; + using SectionDeclAttrLayout = BCRecordLayout< + Section_DECL_ATTR, + BCFixed<1>, // implicit flag + BCBlob // _section + >; + using CDeclDeclAttrLayout = BCRecordLayout< CDecl_DECL_ATTR, BCFixed<1>, // implicit flag diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index 0fef132a713e4..3fcea8be45b23 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -3047,6 +3047,15 @@ class Serializer::DeclSerializer : public DeclVisitor { return; } + case DAK_Section: { + auto *theAttr = cast(DA); + auto abbrCode = S.DeclTypeAbbrCodes[SectionDeclAttrLayout::Code]; + SectionDeclAttrLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, + theAttr->isImplicit(), + theAttr->Name); + return; + } + case DAK_Documentation: { auto *theAttr = cast(DA); auto abbrCode = S.DeclTypeAbbrCodes[DocumentationDeclAttrLayout::Code]; diff --git a/test/IRGen/section.swift b/test/IRGen/section.swift new file mode 100644 index 0000000000000..1325926a8c9a9 --- /dev/null +++ b/test/IRGen/section.swift @@ -0,0 +1,48 @@ +// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -primary-file %s -emit-sil -parse-as-library | %FileCheck %s --check-prefix=SIL +// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -primary-file %s -emit-ir -parse-as-library | %FileCheck %s --check-prefix=IR +// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -primary-file %s -S -parse-as-library | %FileCheck %s --check-prefix=ASM --check-prefix ASM-%target-os + +// REQUIRES: swift_in_compiler + +@_section("__TEXT,__mysection") var g0: Int = 1 +@_section("__TEXT,__mysection") var g1: (Int, Int) = (42, 43) +@_section("__TEXT,__mysection") var g2: Bool = true +@_section("__TEXT,__mysection") public var g3: Bool = true +@_section("__TEXT,__mysection") func foo() {} + +// SIL: @_section("__TEXT,__mysection") @_hasStorage @_hasInitialValue var g0: Int { get set } +// SIL: @_section("__TEXT,__mysection") @_hasStorage @_hasInitialValue var g1: (Int, Int) { get set } +// SIL: @_section("__TEXT,__mysection") @_hasStorage @_hasInitialValue var g2: Bool { get set } +// SIL: @_section("__TEXT,__mysection") func foo() +// SIL: sil private [global_init_once_fn] @$s7section2g0_WZ : $@convention(c) +// SIL: sil hidden [global_init] @$s7section2g0Sivau : $@convention(thin) +// SIL: sil private [global_init_once_fn] @$s7section2g1_WZ : $@convention(c) +// SIL: sil hidden [global_init] @$s7section2g1Si_Sitvau : $@convention(thin) +// SIL: sil private [global_init_once_fn] @$s7section2g2_WZ : $@convention(c) +// SIL: sil hidden [global_init] @$s7section2g2Sbvau : $@convention(thin) +// SIL: sil private [global_init_once_fn] @$s7section2g3_WZ : $@convention(c) +// SIL: sil [global_init] @$s7section2g3Sbvau : $@convention(thin) +// SIL: sil hidden [section "__TEXT,__mysection"] @$s7section3fooyyF : $@convention(thin) + +// IR: @"$s7section2g0_Wz" = internal global i64 0 +// IR: @"$s7section2g0Sivp" = hidden global %TSi <{ i64 1 }>, section "__TEXT,__mysection" +// IR: @"$s7section2g1_Wz" = internal global i64 0 +// IR: @"$s7section2g1Si_Sitvp" = hidden global <{ %TSi, %TSi }> <{ %TSi <{ i64 42 }>, %TSi <{ i64 43 }> }>, section "__TEXT,__mysection" +// IR: @"$s7section2g2_Wz" = internal global i64 0 +// IR: @"$s7section2g2Sbvp" = hidden global %TSb <{ i1 true }>, section "__TEXT,__mysection" +// IR: @"$s7section2g3_Wz" = internal global i64 0 +// IR: @"$s7section2g3Sbvp" = {{.*}}global %TSb <{ i1 true }>, section "__TEXT,__mysection" +// IR: define {{.*}}@"$s7section3fooyyF"(){{.*}} section "__TEXT,__mysection" + +// ASM: .section{{.*}}__TEXT,__mysection +// ASM-NOT: .section +// ASM: $s7section3fooyyF: +// ASM-linux-gnu: .section{{.*}}__TEXT,__mysection +// ASM-NOT: .section +// ASM: $s7section2g0Sivp: +// ASM-NOT: .section +// ASM: $s7section2g1Si_Sitvp: +// ASM-NOT: .section +// ASM: $s7section2g2Sbvp: +// ASM-NOT: .section +// ASM: $s7section2g3Sbvp: diff --git a/test/IRGen/section_non_const.swift b/test/IRGen/section_non_const.swift new file mode 100644 index 0000000000000..82f5f85744e1f --- /dev/null +++ b/test/IRGen/section_non_const.swift @@ -0,0 +1,13 @@ +// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -parse-as-library -emit-sil %s -o /dev/null -verify + +// REQUIRES: swift_in_compiler + +@_section("__TEXT,__mysection") var g0: Int = 1 +@_section("__TEXT,__mysection") var g1: (Int, Int) = (1, 2) +@_section("__TEXT,__mysection") var g2: [Int] = [1, 2, 3] // expected-error {{global variable must be a compile-time constant to use @_section attribute}} +@_section("__TEXT,__mysection") var g3: [Int:Int] = [:] // expected-error {{global variable must be a compile-time constant to use @_section attribute}} +@_section("__TEXT,__mysection") var g4: UInt = 42 +@_section("__TEXT,__mysection") var g5: String = "hello" // expected-error {{global variable must be a compile-time constant to use @_section attribute}} +@_section("__TEXT,__mysection") var g6: Any = 1 // expected-error {{global variable must be a compile-time constant to use @_section attribute}} +@_section("__TEXT,__mysection") var g7: UInt8 = 42 +@_section("__TEXT,__mysection") var g8: Int = 5 * 5 diff --git a/test/IRGen/used.swift b/test/IRGen/used.swift new file mode 100644 index 0000000000000..c07700c76c78c --- /dev/null +++ b/test/IRGen/used.swift @@ -0,0 +1,32 @@ +// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -primary-file %s -emit-sil | %FileCheck %s --check-prefix=SIL +// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -primary-file %s -O -emit-sil | %FileCheck %s --check-prefix=SIL +// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -primary-file %s -emit-ir | %FileCheck %s --check-prefix=IR +// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -primary-file %s -O -emit-ir | %FileCheck %s --check-prefix=IR +// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -primary-file %s -emit-sil -parse-as-library | %FileCheck %s --check-prefix=SIL +// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -primary-file %s -O -emit-sil -parse-as-library | %FileCheck %s --check-prefix=SIL +// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -primary-file %s -emit-ir -parse-as-library | %FileCheck %s --check-prefix=IR +// RUN: %target-swift-frontend -enable-experimental-feature SymbolLinkageMarkers -primary-file %s -O -emit-ir -parse-as-library | %FileCheck %s --check-prefix=IR + +// REQUIRES: swift_in_compiler + +@_used var g0: Int = 1 +@_used var g1: (Int, Int) = (42, 43) +@_used var g2: Bool = true +@_used func foo() {} + +// SIL: @_used @_hasStorage @_hasInitialValue var g0: Int { get set } +// SIL: @_used @_hasStorage @_hasInitialValue var g1: (Int, Int) { get set } +// SIL: @_used @_hasStorage @_hasInitialValue var g2: Bool { get set } +// SIL: @_used func foo() + +// SIL: sil_global hidden @$s4used2g0Sivp : $Int +// SIL: sil_global hidden @$s4used2g1Si_Sitvp : $(Int, Int) +// SIL: sil_global hidden @$s4used2g2Sbvp : $Bool + +// SIL: sil hidden [used] @$s4used3fooyyF : $@convention(thin) + +// IR: @llvm{{(\.compiler)?}}.used = appending global [{{.*}} x i8*] [ +// IR-SAME: i8* bitcast (%TSi* @"$s4used2g0Sivp" to i8*) +// IR-SAME: i8* bitcast (<{ %TSi, %TSi }>* @"$s4used2g1Si_Sitvp" to i8*) +// IR-SAME: i8* bitcast (%TSb* @"$s4used2g2Sbvp" to i8*) +// IR-SAME: i8* bitcast (void ()* @"$s4used3fooyyF" to i8*) diff --git a/utils/gyb_syntax_support/AttributeKinds.py b/utils/gyb_syntax_support/AttributeKinds.py index b304fe7e2ac3b..13f6001dc23ea 100644 --- a/utils/gyb_syntax_support/AttributeKinds.py +++ b/utils/gyb_syntax_support/AttributeKinds.py @@ -922,6 +922,21 @@ def __init__(self, name, swift_name=None): ABIStableToAdd, ABIStableToRemove, APIStableToAdd, APIBreakingToRemove, # noqa: E501 code=142), DeclAttributeAlias('freestanding', 'MacroRole'), + + SimpleDeclAttribute('_used', 'Used', + OnAbstractFunction, OnVar, + UserInaccessible, + ABIBreakingToAdd, ABIBreakingToRemove, + APIBreakingToAdd, APIBreakingToRemove, + code=143), + + DeclAttribute('_section', 'Section', + OnAbstractFunction, OnVar, + UserInaccessible, + ABIBreakingToAdd, ABIBreakingToRemove, + APIBreakingToAdd, APIBreakingToRemove, + code=144), + ] DEPRECATED_MODIFIER_KINDS = [