From 8229b374b165173a8b51f55b9bd11f74d9a681bd Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Thu, 21 Oct 2021 22:04:27 +0200 Subject: [PATCH] Performance annotations: add attributes @_noLocks and @_noAllocation --- docs/ReferenceGuides/UnderscoredAttributes.md | 9 +++++++++ docs/SIL.rst | 9 +++++++++ include/swift/AST/Attr.def | 10 ++++++++++ include/swift/SIL/SILFunction.h | 14 ++++++++++++++ lib/SIL/IR/SILFunction.cpp | 1 + lib/SIL/IR/SILFunctionBuilder.cpp | 6 ++++++ lib/SIL/IR/SILPrinter.cpp | 7 +++++++ lib/SIL/Parser/ParseSIL.cpp | 17 ++++++++++++----- lib/Sema/TypeCheckAttr.cpp | 2 ++ lib/Sema/TypeCheckDeclOverride.cpp | 2 ++ lib/Serialization/DeserializeSIL.cpp | 13 +++++++++---- lib/Serialization/ModuleFormat.h | 2 +- lib/Serialization/SILFormat.h | 1 + lib/Serialization/SerializeSIL.cpp | 3 ++- test/SIL/Parser/attributes.sil | 14 ++++++++++++++ test/SIL/Serialization/basic.sil | 14 ++++++++++++++ 16 files changed, 113 insertions(+), 11 deletions(-) diff --git a/docs/ReferenceGuides/UnderscoredAttributes.md b/docs/ReferenceGuides/UnderscoredAttributes.md index 54c312cfcaa..8fc6d9d70b1 100644 --- a/docs/ReferenceGuides/UnderscoredAttributes.md +++ b/docs/ReferenceGuides/UnderscoredAttributes.md @@ -625,3 +625,12 @@ for more details. Marks a var decl as a variable that must be copied explicitly using the builtin function Builtin.copy. + +## `@_noAllocation`, `@_noLocks` + +These attributes are performance annotations. If a function is annotated with +such an attribute, the compiler issues a diagnostic message if the function +calls a runtime function which allocates memory or locks, respectively. +The `@_noLocks` attribute implies `@_noAllocation` because a memory allocation +also locks. + diff --git a/docs/SIL.rst b/docs/SIL.rst index 74e748aecd4..678278d3c7c 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -1107,6 +1107,15 @@ Specifies for which types specialized code should be generated. sil-function-attribute ::= '[clang "' identifier '"]' The clang node owner. +:: + + sil-function-attribute ::= '[' performance-constraint ']' + performance-constraint :: 'no_locks' + performance-constraint :: 'no_allocation' + +Specifies the performance constraints for the function, which defines which type +of runtime functions are allowed to be called from the function. + Basic Blocks ~~~~~~~~~~~~ diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index ae67ee30c02..aaca77e4482 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -679,6 +679,16 @@ SIMPLE_DECL_ATTR(_noImplicitCopy, NoImplicitCopy, OnVar, 122) +SIMPLE_DECL_ATTR(_noLocks, NoLocks, + OnAbstractFunction | OnSubscript | UserInaccessible | + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + 123) + +SIMPLE_DECL_ATTR(_noAllocation, NoAllocation, + OnAbstractFunction | OnSubscript | UserInaccessible | + ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove, + 124) + // If you're adding a new underscored attribute here, please document it in // docs/ReferenceGuides/UnderscoredAttributes.md. diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 870ededef45..c2888227c87 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -60,6 +60,12 @@ enum IsExactSelfClass_t { IsExactSelfClass, }; +enum class PerformanceConstraints : uint8_t { + None = 0, + NoAllocation = 1, + NoLocks = 2, +}; + class SILSpecializeAttr final { friend SILFunction; public: @@ -232,6 +238,8 @@ private: Purpose specialPurpose = Purpose::None; + PerformanceConstraints perfConstraints = PerformanceConstraints::None; + /// This is the number of uses of this SILFunction inside the SIL. /// It does not include references from debug scopes. unsigned RefCount = 0; @@ -813,6 +821,12 @@ public: OptMode = unsigned(mode); } + PerformanceConstraints getPerfConstraints() const { return perfConstraints; } + + void setPerfConstraints(PerformanceConstraints perfConstr) { + perfConstraints = perfConstr; + } + /// \returns True if the function is optimizable (i.e. not marked as no-opt), /// or is raw SIL (so that the mandatory passes still run). bool shouldOptimize() const; diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index 6399c9a3de5..fe90ca4fec8 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -187,6 +187,7 @@ void SILFunction::init(SILLinkage Linkage, StringRef Name, this->IsStaticallyLinked = false; this->IsWithoutActuallyEscapingThunk = false; this->OptMode = unsigned(OptimizationMode::NotSet); + this->perfConstraints = PerformanceConstraints::None; this->EffectsKindAttr = unsigned(E); assert(!Transparent || !IsDynamicReplaceable); validateSubclassScope(classSubclassScope, isThunk, nullptr); diff --git a/lib/SIL/IR/SILFunctionBuilder.cpp b/lib/SIL/IR/SILFunctionBuilder.cpp index 5d6667268d4..65171d0aeb7 100644 --- a/lib/SIL/IR/SILFunctionBuilder.cpp +++ b/lib/SIL/IR/SILFunctionBuilder.cpp @@ -105,6 +105,12 @@ void SILFunctionBuilder::addFunctionAttributes( if (Attrs.hasAttribute() || Attrs.hasAttribute()) F->setHasCReferences(true); + if (Attrs.hasAttribute()) { + F->setPerfConstraints(PerformanceConstraints::NoLocks); + } else if (Attrs.hasAttribute()) { + F->setPerfConstraints(PerformanceConstraints::NoAllocation); + } + // Validate `@differentiable` attributes by calling `getParameterIndices`. // This is important for: // - Skipping invalid `@differentiable` attributes in non-primary files. diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 675bc44d2fe..a6bcc49bd2a 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2871,6 +2871,13 @@ void SILFunction::print(SILPrintContext &PrintCtx) const { default: break; } + PerformanceConstraints perf = getPerfConstraints(); + switch (perf) { + case PerformanceConstraints::None: break; + case PerformanceConstraints::NoLocks: OS << "[no_locks] "; break; + case PerformanceConstraints::NoAllocation: OS << "[no_allocation] "; break; + } + if (getEffectsKind() == EffectsKind::ReadOnly) OS << "[readonly] "; else if (getEffectsKind() == EffectsKind::ReadNone) diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index f49ca820d2e..5f816cefee4 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -943,6 +943,7 @@ static bool parseDeclSILOptional(bool *isTransparent, SILFunction::Purpose *specialPurpose, Inline_t *inlineStrategy, OptimizationMode *optimizationMode, + PerformanceConstraints *perfConstraints, bool *isLet, bool *isWeakImported, AvailabilityContext *availability, @@ -1019,6 +1020,10 @@ static bool parseDeclSILOptional(bool *isTransparent, *optimizationMode = OptimizationMode::ForSpeed; else if (optimizationMode && SP.P.Tok.getText() == "Osize") *optimizationMode = OptimizationMode::ForSize; + else if (perfConstraints && SP.P.Tok.getText() == "no_locks") + *perfConstraints = PerformanceConstraints::NoLocks; + else if (perfConstraints && SP.P.Tok.getText() == "no_allocation") + *perfConstraints = PerformanceConstraints::NoAllocation; else if (inlineStrategy && SP.P.Tok.getText() == "always_inline") *inlineStrategy = AlwaysInline; else if (MRK && SP.P.Tok.getText() == "readnone") @@ -6187,6 +6192,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { bool isWithoutActuallyEscapingThunk = false; Inline_t inlineStrategy = InlineDefault; OptimizationMode optimizationMode = OptimizationMode::NotSet; + PerformanceConstraints perfConstr = PerformanceConstraints::None; SmallVector Semantics; SmallVector SpecAttrs; ValueDecl *ClangDecl = nullptr; @@ -6198,7 +6204,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { &isTransparent, &isSerialized, &isCanonical, &hasOwnershipSSA, &isThunk, &isDynamic, &isExactSelfClass, &DynamicallyReplacedFunction, &objCReplacementFor, &specialPurpose, &inlineStrategy, - &optimizationMode, nullptr, &isWeakImported, &availability, + &optimizationMode, &perfConstr, nullptr, &isWeakImported, &availability, &isWithoutActuallyEscapingThunk, &Semantics, &SpecAttrs, &ClangDecl, &MRK, FunctionState, M) || P.parseToken(tok::at_sign, diag::expected_sil_function_name) || @@ -6242,6 +6248,7 @@ bool SILParserState::parseDeclSIL(Parser &P) { isWithoutActuallyEscapingThunk); FunctionState.F->setInlineStrategy(inlineStrategy); FunctionState.F->setOptimizationMode(optimizationMode); + FunctionState.F->setPerfConstraints(perfConstr); FunctionState.F->setEffectsKind(MRK); if (ClangDecl) FunctionState.F->setClangNodeOwner(ClangDecl); @@ -6410,7 +6417,7 @@ bool SILParserState::parseSILGlobal(Parser &P) { if (parseSILLinkage(GlobalLinkage, P) || parseDeclSILOptional(nullptr, &isSerialized, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, + nullptr, nullptr, nullptr, &isLet, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, State, M) || P.parseToken(tok::at_sign, diag::expected_sil_value_name) || @@ -6462,7 +6469,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, SP, M)) + nullptr, nullptr, nullptr, nullptr, SP, M)) return true; ValueDecl *VD; @@ -6531,7 +6538,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, VTableState, M)) return true; @@ -7037,7 +7044,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, WitnessState, M)) return true; diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 45f39f547a2..f9b3a393252 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -92,6 +92,8 @@ public: IGNORED_ATTR(RequiresStoredPropertyInits) IGNORED_ATTR(RestatedObjCConformance) IGNORED_ATTR(Semantics) + IGNORED_ATTR(NoLocks) + IGNORED_ATTR(NoAllocation) IGNORED_ATTR(EmitAssemblyVisionRemarks) IGNORED_ATTR(ShowInInterface) IGNORED_ATTR(SILGenName) diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index a2b820c2581..f5eb31d11fd 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1466,6 +1466,8 @@ namespace { UNINTERESTING_ATTR(InheritsConvenienceInitializers) UNINTERESTING_ATTR(Inline) UNINTERESTING_ATTR(Optimize) + UNINTERESTING_ATTR(NoLocks) + UNINTERESTING_ATTR(NoAllocation) UNINTERESTING_ATTR(Inlinable) UNINTERESTING_ATTR(Effects) UNINTERESTING_ATTR(Final) diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index db6ca01257e..69bff8f4626 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -526,14 +526,16 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, GenericSignatureID genericSigID; unsigned rawLinkage, isTransparent, isSerialized, isThunk, isWithoutactuallyEscapingThunk, specialPurpose, inlineStrategy, - optimizationMode, subclassScope, hasCReferences, effect, numSpecAttrs, + optimizationMode, perfConstr, + subclassScope, hasCReferences, effect, numSpecAttrs, hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available), isDynamic, isExactSelfClass; ArrayRef SemanticsIDs; SILFunctionLayout::readRecord( scratch, rawLinkage, isTransparent, isSerialized, isThunk, isWithoutactuallyEscapingThunk, specialPurpose, inlineStrategy, - optimizationMode, subclassScope, hasCReferences, effect, numSpecAttrs, + optimizationMode, perfConstr, + subclassScope, hasCReferences, effect, numSpecAttrs, hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available), isDynamic, isExactSelfClass, funcTyID, replacedFunctionID, genericSigID, clangNodeOwnerID, SemanticsIDs); @@ -656,6 +658,7 @@ SILDeserializer::readSILFunctionChecked(DeclID FID, SILFunction *existingFn, fn->setSpecialPurpose(SILFunction::Purpose(specialPurpose)); fn->setEffectsKind(EffectsKind(effect)); fn->setOptimizationMode(OptimizationMode(optimizationMode)); + fn->setPerfConstraints((PerformanceConstraints)perfConstr); fn->setAlwaysWeakImported(isWeakImported); fn->setClassSubclassScope(SubclassScope(subclassScope)); fn->setHasCReferences(bool(hasCReferences)); @@ -2921,14 +2924,16 @@ bool SILDeserializer::hasSILFunction(StringRef Name, GenericSignatureID genericSigID; unsigned rawLinkage, isTransparent, isSerialized, isThunk, isWithoutactuallyEscapingThunk, isGlobal, inlineStrategy, - optimizationMode, subclassScope, hasCReferences, effect, numSpecAttrs, + optimizationMode, perfConstr, + subclassScope, hasCReferences, effect, numSpecAttrs, hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available), isDynamic, isExactSelfClass; ArrayRef SemanticsIDs; SILFunctionLayout::readRecord( scratch, rawLinkage, isTransparent, isSerialized, isThunk, isWithoutactuallyEscapingThunk, isGlobal, inlineStrategy, - optimizationMode, subclassScope, hasCReferences, effect, numSpecAttrs, + optimizationMode, perfConstr, + subclassScope, hasCReferences, effect, numSpecAttrs, hasQualifiedOwnership, isWeakImported, LIST_VER_TUPLE_PIECES(available), isDynamic, isExactSelfClass, funcTyID, replacedFunctionID, genericSigID, clangOwnerID, SemanticsIDs); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index fc03d5a0c27..48cb7d8afd2 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -56,7 +56,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 = 637; // @_noImplicitCopy +const uint16_t SWIFTMODULE_VERSION_MINOR = 638; // PerformanceConstraints /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/lib/Serialization/SILFormat.h b/lib/Serialization/SILFormat.h index 153e2eb771e..b446fd91544 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -283,6 +283,7 @@ namespace sil_block { BCFixed<3>, // specialPurpose BCFixed<2>, // inlineStrategy BCFixed<2>, // optimizationMode + BCFixed<3>, // perfConstraints BCFixed<2>, // classSubclassScope BCFixed<1>, // hasCReferences BCFixed<3>, // side effect info. diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 977ebaabb89..b640dd516a0 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -484,7 +484,8 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) { (unsigned)F.isTransparent(), (unsigned)F.isSerialized(), (unsigned)F.isThunk(), (unsigned)F.isWithoutActuallyEscapingThunk(), (unsigned)F.getSpecialPurpose(), (unsigned)F.getInlineStrategy(), - (unsigned)F.getOptimizationMode(), (unsigned)F.getClassSubclassScope(), + (unsigned)F.getOptimizationMode(), (unsigned)F.getPerfConstraints(), + (unsigned)F.getClassSubclassScope(), (unsigned)F.hasCReferences(), (unsigned)F.getEffectsKind(), (unsigned)numSpecAttrs, (unsigned)F.hasOwnership(), F.isAlwaysWeakImported(), LIST_VER_TUPLE_PIECES(available), diff --git a/test/SIL/Parser/attributes.sil b/test/SIL/Parser/attributes.sil index 705b35f3ec8..29c80f2e2a5 100644 --- a/test/SIL/Parser/attributes.sil +++ b/test/SIL/Parser/attributes.sil @@ -6,6 +6,20 @@ bb0: return undef : $() } +// CHECK-LABEL: sil [no_allocation] [ossa] @test_no_allocation : $@convention(thin) () -> () { +sil [no_allocation] [ossa] @test_no_allocation : $@convention(thin) () -> () { +bb0: + %1 = tuple () + return %1 : $() +} + +// CHECK-LABEL: sil [no_locks] [ossa] @test_no_locks : $@convention(thin) () -> () { +sil [no_locks] [ossa] @test_no_locks : $@convention(thin) () -> () { +bb0: + %1 = tuple () + return %1 : $() +} + // Make sure we don't try to parse the Swift decl as '@owned() func baz()'. sil @bar : $@convention(thin) () -> @owned () func baz() diff --git a/test/SIL/Serialization/basic.sil b/test/SIL/Serialization/basic.sil index 5aec86aeecc..458528bb198 100644 --- a/test/SIL/Serialization/basic.sil +++ b/test/SIL/Serialization/basic.sil @@ -90,6 +90,20 @@ bb0(%0 : $Builtin.NativeObject): return %1 : $Builtin.NativeObject } +// CHECK-LABEL: sil [no_allocation] [ossa] @test_no_allocation : $@convention(thin) () -> () { +sil [no_allocation] [ossa] @test_no_allocation : $@convention(thin) () -> () { +bb0: + %1 = tuple () + return %1 : $() +} + +// CHECK-LABEL: sil [no_locks] [ossa] @test_no_locks : $@convention(thin) () -> () { +sil [no_locks] [ossa] @test_no_locks : $@convention(thin) () -> () { +bb0: + %1 = tuple () + return %1 : $() +} + // CHECK-LABEL: sil [ossa] @test_pointer_to_address : $@convention(thin) (Builtin.RawPointer, Builtin.Int64) -> () { // CHECK: pointer_to_address %0 : $Builtin.RawPointer to [strict] $*Builtin.Int64 // CHECK: pointer_to_address %0 : $Builtin.RawPointer to [invariant] $*Builtin.Int64