diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index 6a1b8a56a5294..54210b255b560 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -649,6 +649,15 @@ class ASTContext final { /// metadata. AvailabilityContext getPrespecializedGenericMetadataAvailability(); + /// Get the runtime availability of the swift_compareTypeContextDescriptors + /// for the target platform. + AvailabilityContext getCompareTypeContextDescriptorsAvailability(); + + /// Get the runtime availability of the + /// swift_compareProtocolConformanceDescriptors entry point for the target + /// platform. + AvailabilityContext getCompareProtocolConformanceDescriptorsAvailability(); + /// Get the runtime availability of features introduced in the Swift 5.2 /// compiler for the target platform. AvailabilityContext getSwift52Availability(); diff --git a/include/swift/AST/ASTTypeIDZone.def b/include/swift/AST/ASTTypeIDZone.def index 5c1eaa91fe286..daf638f797480 100644 --- a/include/swift/AST/ASTTypeIDZone.def +++ b/include/swift/AST/ASTTypeIDZone.def @@ -27,6 +27,7 @@ SWIFT_TYPEID(PropertyWrapperTypeInfo) SWIFT_TYPEID(Requirement) SWIFT_TYPEID(ResilienceExpansion) SWIFT_TYPEID(FragileFunctionKind) +SWIFT_TYPEID(TangentPropertyInfo) SWIFT_TYPEID(Type) SWIFT_TYPEID(TypePair) SWIFT_TYPEID(TypeWitnessAndDecl) diff --git a/include/swift/AST/ASTTypeIDs.h b/include/swift/AST/ASTTypeIDs.h index 9359f95427d0e..b81fdc686b9ac 100644 --- a/include/swift/AST/ASTTypeIDs.h +++ b/include/swift/AST/ASTTypeIDs.h @@ -19,6 +19,7 @@ #include "swift/Basic/LLVM.h" #include "swift/Basic/TypeID.h" + namespace swift { class AbstractFunctionDecl; @@ -58,14 +59,14 @@ class Requirement; enum class ResilienceExpansion : unsigned; struct FragileFunctionKind; class SourceFile; +struct TangentPropertyInfo; class Type; -class ValueDecl; -class VarDecl; -class Witness; class TypeAliasDecl; -class Type; struct TypePair; struct TypeWitnessAndDecl; +class ValueDecl; +class VarDecl; +class Witness; enum class AncestryFlags : uint8_t; enum class ImplicitMemberAction : uint8_t; struct FingerprintAndMembers; diff --git a/include/swift/AST/AutoDiff.h b/include/swift/AST/AutoDiff.h index 5b447027b20c5..18524c9933e9e 100644 --- a/include/swift/AST/AutoDiff.h +++ b/include/swift/AST/AutoDiff.h @@ -35,6 +35,7 @@ class AnyFunctionType; class SourceFile; class SILFunctionType; class TupleType; +class VarDecl; /// A function type differentiability kind. enum class DifferentiabilityKind : uint8_t { @@ -459,6 +460,99 @@ class DerivativeFunctionTypeError } }; +/// Describes the "tangent stored property" corresponding to an original stored +/// property in a `Differentiable`-conforming type. +/// +/// The tangent stored property is the stored property in the `TangentVector` +/// struct of the `Differentiable`-conforming type, with the same name as the +/// original stored property and with the original stored property's +/// `TangentVector` type. +struct TangentPropertyInfo { + struct Error { + enum class Kind { + /// The original property is `@noDerivative`. + NoDerivativeOriginalProperty, + /// The nominal parent type does not conform to `Differentiable`. + NominalParentNotDifferentiable, + /// The original property's type does not conform to `Differentiable`. + OriginalPropertyNotDifferentiable, + /// The parent `TangentVector` type is not a struct. + ParentTangentVectorNotStruct, + /// The parent `TangentVector` struct does not declare a stored property + /// with the same name as the original property. + TangentPropertyNotFound, + /// The tangent property's type is not equal to the original property's + /// `TangentVector` type. + TangentPropertyWrongType, + /// The tangent property is not a stored property. + TangentPropertyNotStored + }; + + /// The error kind. + Kind kind; + + private: + union Value { + Type type; + Value(Type type) : type(type) {} + Value() {} + } value; + + public: + Error(Kind kind) : kind(kind), value() { + assert(kind == Kind::NoDerivativeOriginalProperty || + kind == Kind::NominalParentNotDifferentiable || + kind == Kind::OriginalPropertyNotDifferentiable || + kind == Kind::ParentTangentVectorNotStruct || + kind == Kind::TangentPropertyNotFound || + kind == Kind::TangentPropertyNotStored); + }; + + Error(Kind kind, Type type) : kind(kind), value(type) { + assert(kind == Kind::TangentPropertyWrongType); + }; + + Type getType() const { + assert(kind == Kind::TangentPropertyWrongType); + return value.type; + } + + friend bool operator==(const Error &lhs, const Error &rhs); + }; + + /// The tangent stored property. + VarDecl *tangentProperty = nullptr; + + /// An optional error. + Optional error = None; + +private: + TangentPropertyInfo(VarDecl *tangentProperty, Optional error) + : tangentProperty(tangentProperty), error(error) {} + +public: + TangentPropertyInfo(VarDecl *tangentProperty) + : TangentPropertyInfo(tangentProperty, None) {} + + TangentPropertyInfo(Error::Kind errorKind) + : TangentPropertyInfo(nullptr, Error(errorKind)) {} + + TangentPropertyInfo(Error::Kind errorKind, Type errorType) + : TangentPropertyInfo(nullptr, Error(errorKind, errorType)) {} + + /// Returns `true` iff this tangent property info is valid. + bool isValid() const { return tangentProperty && !error; } + + explicit operator bool() const { return isValid(); } + + friend bool operator==(const TangentPropertyInfo &lhs, + const TangentPropertyInfo &rhs) { + return lhs.tangentProperty == rhs.tangentProperty && lhs.error == rhs.error; + } +}; + +void simple_display(llvm::raw_ostream &OS, TangentPropertyInfo info); + /// The key type used for uniquing `SILDifferentiabilityWitness` in /// `SILModule`: original function name, parameter indices, result indices, and /// derivative generic signature. diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index 34ca3c7b08c86..cbc3ad300a690 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -504,9 +504,26 @@ NOTE(autodiff_loadable_value_addressonly_tangent_unsupported,none, "properties", (Type, Type)) NOTE(autodiff_enums_unsupported,none, "differentiating enum values is not yet supported", ()) +NOTE(autodiff_stored_property_parent_not_differentiable,none, + "cannot differentiate access to property '%0.%1' because '%0' does not " + "conform to 'Differentiable'", (StringRef, StringRef)) +NOTE(autodiff_stored_property_not_differentiable,none, + "cannot differentiate access to property '%0.%1' because property type %2 " + "does not conform to 'Differentiable'", (StringRef, StringRef, Type)) +NOTE(autodiff_stored_property_tangent_not_struct,none, + "cannot differentiate access to property '%0.%1' because " + "'%0.TangentVector' is not a struct", (StringRef, StringRef)) NOTE(autodiff_stored_property_no_corresponding_tangent,none, - "property cannot be differentiated because '%0.TangentVector' does not " - "have a member named '%1'", (StringRef, StringRef)) + "cannot differentiate access to property '%0.%1' because " + "'%0.TangentVector' does not have a stored property named '%1'", + (StringRef, StringRef)) +NOTE(autodiff_tangent_property_wrong_type,none, + "cannot differentiate access to property '%0.%1' because " + "'%0.TangentVector.%1' does not have expected type %2", + (StringRef, StringRef, /*originalPropertyTanType*/ Type)) +NOTE(autodiff_tangent_property_not_stored,none, + "cannot differentiate access to property '%0.%1' because " + "'%0.TangentVector.%1' is not a stored property", (StringRef, StringRef)) NOTE(autodiff_coroutines_not_supported,none, "differentiation of coroutine calls is not yet supported", ()) NOTE(autodiff_cannot_differentiate_writes_to_global_variables,none, diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 46d6b5ae0810a..bed14bfd6c62a 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2191,6 +2191,27 @@ class DerivativeAttrOriginalDeclRequest bool isCached() const { return true; } }; +/// Resolves the "tangent stored property" corresponding to an original stored +/// property in a `Differentiable`-conforming type. +class TangentStoredPropertyRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + TangentPropertyInfo evaluate(Evaluator &evaluator, + VarDecl *originalField) const; + +public: + // Caching. + bool isCached() const { return true; } +}; + /// Checks whether a type eraser has a viable initializer. class TypeEraserHasViableInitRequest : public SimpleRequest(VarDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, TypeCheckFunctionBodyRequest, bool(AbstractFunctionDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, TypeCheckFunctionBodyAtLocRequest, diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index 51c547e9bfe70..4ad4b8751ab31 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -294,6 +294,20 @@ const /// the same context. bool equalContexts(const ContextDescriptor *a, const ContextDescriptor *b); +/// Determines whether two type context descriptors describe the same type +/// context. +/// +/// Runtime availability: Swift 5.4. +/// +/// \param lhs The first type context descriptor to compare. +/// \param rhs The second type context descriptor to compare. +/// +/// \returns true if both describe the same type context, false otherwise. +SWIFT_RUNTIME_EXPORT +SWIFT_CC(swift) +bool swift_compareTypeContextDescriptors(const TypeContextDescriptor *lhs, + const TypeContextDescriptor *rhs); + /// Compute the bounds of class metadata with a resilient superclass. ClassMetadataBounds getResilientMetadataBounds( const ClassDescriptor *descriptor); @@ -409,6 +423,21 @@ const WitnessTable *swift_getAssociatedConformanceWitness( const ProtocolRequirement *reqBase, const ProtocolRequirement *assocConformance); +/// Determine whether two protocol conformance descriptors describe the same +/// conformance of a type to a protocol. +/// +/// Runtime availability: Swift 5.4 +/// +/// \param lhs The first protocol conformance descriptor to compare. +/// \param rhs The second protocol conformance descriptor to compare. +/// +/// \returns true if both describe the same conformance, false otherwise. +SWIFT_RUNTIME_EXPORT +SWIFT_CC(swift) +bool swift_compareProtocolConformanceDescriptors( + const ProtocolConformanceDescriptor *lhs, + const ProtocolConformanceDescriptor *rhs); + /// Fetch a uniqued metadata for a function type. SWIFT_RUNTIME_EXPORT const FunctionTypeMetadata * diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index aad8ab980660a..910c1d2788245 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -616,6 +616,18 @@ FUNCTION(GetForeignTypeMetadata, swift_getForeignTypeMetadata, ARGS(SizeTy, TypeMetadataPtrTy), ATTRS(NoUnwind, ReadNone)) // only writes to runtime-private fields +// SWIFT_RUNTIME_EXPORT +// SWIFT_CC(swift) +// bool swift_compareTypeContextDescriptors(const TypeContextDescriptor *lhs, +// const TypeContextDescriptor *rhs); +FUNCTION(CompareTypeContextDescriptors, + swift_compareTypeContextDescriptors, SwiftCC, + CompareTypeContextDescriptorsAvailability, + RETURNS(Int1Ty), + ARGS(TypeContextDescriptorPtrTy, + TypeContextDescriptorPtrTy), + ATTRS(NoUnwind, ReadNone)) + // MetadataResponse swift_getSingletonMetadata(MetadataRequest request, // TypeContextDescriptor *type); FUNCTION(GetSingletonMetadata, swift_getSingletonMetadata, @@ -723,6 +735,18 @@ FUNCTION(GetAssociatedConformanceWitness, ProtocolRequirementStructTy->getPointerTo()), ATTRS(NoUnwind, ReadNone)) +// SWIFT_RUNTIME_EXPORT +// SWIFT_CC(swift) bool swift_compareProtocolConformanceDescriptors( +// const ProtocolConformanceDescriptor *lhs, +// const ProtocolConformanceDescriptor *rhs); +FUNCTION(CompareProtocolConformanceDescriptors, + swift_compareProtocolConformanceDescriptors, SwiftCC, + CompareProtocolConformanceDescriptorsAvailability, + RETURNS(Int1Ty), + ARGS(ProtocolConformanceDescriptorPtrTy, + ProtocolConformanceDescriptorPtrTy), + ATTRS(NoUnwind, ReadNone)) + // Metadata *swift_getMetatypeMetadata(Metadata *instanceTy); FUNCTION(GetMetatypeMetadata, swift_getMetatypeMetadata, C_CC, AlwaysAvailable, RETURNS(TypeMetadataPtrTy), diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 39be3a320fe1e..0e811b94a46eb 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -5758,6 +5758,13 @@ class FieldIndexCacheBase : public SingleValueInstruction { return s; } + static bool classof(const SILNode *node) { + SILNodeKind kind = node->getKind(); + return kind == SILNodeKind::StructExtractInst || + kind == SILNodeKind::StructElementAddrInst || + kind == SILNodeKind::RefElementAddrInst; + } + private: unsigned cacheFieldIndex(); }; diff --git a/include/swift/SILOptimizer/Differentiation/ADContext.h b/include/swift/SILOptimizer/Differentiation/ADContext.h index 1b0426aed0f1e..a5bf5e86f1505 100644 --- a/include/swift/SILOptimizer/Differentiation/ADContext.h +++ b/include/swift/SILOptimizer/Differentiation/ADContext.h @@ -253,11 +253,9 @@ ADContext::emitNondifferentiabilityError(SILValue value, getADDebugStream() << "For value:\n" << value; getADDebugStream() << "With invoker:\n" << invoker << '\n'; }); - auto valueLoc = value.getLoc().getSourceLoc(); // If instruction does not have a valid location, use the function location // as a fallback. Improves diagnostics in some cases. - if (valueLoc.isInvalid()) - valueLoc = value->getFunction()->getLocation().getSourceLoc(); + auto valueLoc = getValidLocation(value).getSourceLoc(); return emitNondifferentiabilityError(valueLoc, invoker, diag, std::forward(args)...); } @@ -272,12 +270,10 @@ ADContext::emitNondifferentiabilityError(SILInstruction *inst, getADDebugStream() << "For instruction:\n" << *inst; getADDebugStream() << "With invoker:\n" << invoker << '\n'; }); - auto instLoc = inst->getLoc().getSourceLoc(); // If instruction does not have a valid location, use the function location // as a fallback. Improves diagnostics for `ref_element_addr` generated in // synthesized stored property getters. - if (instLoc.isInvalid()) - instLoc = inst->getFunction()->getLocation().getSourceLoc(); + auto instLoc = getValidLocation(inst).getSourceLoc(); return emitNondifferentiabilityError(instLoc, invoker, diag, std::forward(args)...); } diff --git a/include/swift/SILOptimizer/Differentiation/Common.h b/include/swift/SILOptimizer/Differentiation/Common.h index a4d8637b386d8..82bdb4a2a492d 100644 --- a/include/swift/SILOptimizer/Differentiation/Common.h +++ b/include/swift/SILOptimizer/Differentiation/Common.h @@ -17,6 +17,8 @@ #ifndef SWIFT_SILOPTIMIZER_UTILS_DIFFERENTIATION_COMMON_H #define SWIFT_SILOPTIMIZER_UTILS_DIFFERENTIATION_COMMON_H +#include "swift/AST/DiagnosticsSIL.h" +#include "swift/AST/Expr.h" #include "swift/AST/SemanticAttrs.h" #include "swift/SIL/SILDifferentiabilityWitness.h" #include "swift/SIL/SILFunction.h" @@ -24,15 +26,18 @@ #include "swift/SIL/TypeSubstCloner.h" #include "swift/SILOptimizer/Analysis/ArraySemantic.h" #include "swift/SILOptimizer/Analysis/DifferentiableActivityAnalysis.h" +#include "swift/SILOptimizer/Differentiation/DifferentiationInvoker.h" namespace swift { +namespace autodiff { + +class ADContext; + //===----------------------------------------------------------------------===// // Helpers //===----------------------------------------------------------------------===// -namespace autodiff { - /// Prints an "[AD] " prefix to `llvm::dbgs()` and returns the debug stream. /// This is being used to print short debug messages within the AD pass. raw_ostream &getADDebugStream(); @@ -136,6 +141,34 @@ template Inst *peerThroughFunctionConversions(SILValue value) { return nullptr; } +//===----------------------------------------------------------------------===// +// Diagnostic utilities +//===----------------------------------------------------------------------===// + +// Returns `v`'s location if it is valid. Otherwise, returns `v`'s function's +// location as as a fallback. Used for diagnostics. +SILLocation getValidLocation(SILValue v); + +// Returns `inst`'s location if it is valid. Otherwise, returns `inst`'s +// function's location as as a fallback. Used for diagnostics. +SILLocation getValidLocation(SILInstruction *inst); + +//===----------------------------------------------------------------------===// +// Tangent property lookup utilities +//===----------------------------------------------------------------------===// + +/// Returns the tangent stored property of `originalField`. On error, emits +/// diagnostic and returns nullptr. +VarDecl *getTangentStoredProperty(ADContext &context, VarDecl *originalField, + SILLocation loc, + DifferentiationInvoker invoker); + +/// Returns the tangent stored property of the original stored property +/// referenced by `inst`. On error, emits diagnostic and returns nullptr. +VarDecl *getTangentStoredProperty(ADContext &context, + FieldIndexCacheBase *projectionInst, + DifferentiationInvoker invoker); + //===----------------------------------------------------------------------===// // Code emission utilities //===----------------------------------------------------------------------===// diff --git a/lib/AST/AutoDiff.cpp b/lib/AST/AutoDiff.cpp index df1376a6cc41a..87c65561dcf6c 100644 --- a/lib/AST/AutoDiff.cpp +++ b/lib/AST/AutoDiff.cpp @@ -421,3 +421,128 @@ void DerivativeFunctionTypeError::log(raw_ostream &OS) const { } } } + +bool swift::operator==(const TangentPropertyInfo::Error &lhs, + const TangentPropertyInfo::Error &rhs) { + if (lhs.kind != rhs.kind) + return false; + switch (lhs.kind) { + case TangentPropertyInfo::Error::Kind::NoDerivativeOriginalProperty: + case TangentPropertyInfo::Error::Kind::NominalParentNotDifferentiable: + case TangentPropertyInfo::Error::Kind::OriginalPropertyNotDifferentiable: + case TangentPropertyInfo::Error::Kind::ParentTangentVectorNotStruct: + case TangentPropertyInfo::Error::Kind::TangentPropertyNotFound: + case TangentPropertyInfo::Error::Kind::TangentPropertyNotStored: + return true; + case TangentPropertyInfo::Error::Kind::TangentPropertyWrongType: + return lhs.getType()->isEqual(rhs.getType()); + } +} + +void swift::simple_display(llvm::raw_ostream &os, TangentPropertyInfo info) { + os << "{ "; + os << "tangent property: " + << (info.tangentProperty ? info.tangentProperty->printRef() : "null"); + if (info.error) { + os << ", error: "; + switch (info.error->kind) { + case TangentPropertyInfo::Error::Kind::NoDerivativeOriginalProperty: + os << "'@noDerivative' original property has no tangent property"; + break; + case TangentPropertyInfo::Error::Kind::NominalParentNotDifferentiable: + os << "nominal parent does not conform to 'Differentiable'"; + break; + case TangentPropertyInfo::Error::Kind::OriginalPropertyNotDifferentiable: + os << "original property type does not conform to 'Differentiable'"; + break; + case TangentPropertyInfo::Error::Kind::ParentTangentVectorNotStruct: + os << "'TangentVector' type is not a struct"; + break; + case TangentPropertyInfo::Error::Kind::TangentPropertyNotFound: + os << "'TangentVector' struct does not have stored property with the " + "same name as the original property"; + break; + case TangentPropertyInfo::Error::Kind::TangentPropertyWrongType: + os << "tangent property's type is not equal to the original property's " + "'TangentVector' type"; + break; + case TangentPropertyInfo::Error::Kind::TangentPropertyNotStored: + os << "'TangentVector' property '" << info.tangentProperty->getName() + << "' is not a stored property"; + break; + } + } + os << " }"; +} + +TangentPropertyInfo +TangentStoredPropertyRequest::evaluate(Evaluator &evaluator, + VarDecl *originalField) const { + assert(originalField->hasStorage() && originalField->isInstanceMember() && + "Expected stored property"); + auto *parentDC = originalField->getDeclContext(); + assert(parentDC->isTypeContext()); + auto parentType = parentDC->getDeclaredTypeInContext(); + auto *moduleDecl = originalField->getModuleContext(); + auto parentTan = parentType->getAutoDiffTangentSpace( + LookUpConformanceInModule(moduleDecl)); + // Error if parent nominal type does not conform to `Differentiable`. + if (!parentTan) { + return TangentPropertyInfo( + TangentPropertyInfo::Error::Kind::NominalParentNotDifferentiable); + } + // Error if original stored property is `@noDerivative`. + if (originalField->getAttrs().hasAttribute()) { + return TangentPropertyInfo( + TangentPropertyInfo::Error::Kind::NoDerivativeOriginalProperty); + } + // Error if original property's type does not conform to `Differentiable`. + auto originalFieldTan = originalField->getType()->getAutoDiffTangentSpace( + LookUpConformanceInModule(moduleDecl)); + if (!originalFieldTan) { + return TangentPropertyInfo( + TangentPropertyInfo::Error::Kind::OriginalPropertyNotDifferentiable); + } + auto parentTanType = parentTan->getType(); + auto *parentTanStruct = parentTanType->getStructOrBoundGenericStruct(); + // Error if parent `TangentVector` is not a struct. + if (!parentTanStruct) { + return TangentPropertyInfo( + TangentPropertyInfo::Error::Kind::ParentTangentVectorNotStruct); + } + // Find the corresponding field in the tangent space. + VarDecl *tanField = nullptr; + // If `TangentVector` is the original struct, then the tangent property is the + // original property. + if (parentTanStruct == parentDC->getSelfStructDecl()) { + tanField = originalField; + } + // Otherwise, look up the field by name. + else { + auto tanFieldLookup = + parentTanStruct->lookupDirect(originalField->getName()); + llvm::erase_if(tanFieldLookup, + [](ValueDecl *v) { return !isa(v); }); + // Error if tangent property could not be found. + if (tanFieldLookup.empty()) { + return TangentPropertyInfo( + TangentPropertyInfo::Error::Kind::TangentPropertyNotFound); + } + tanField = cast(tanFieldLookup.front()); + } + // Error if tangent property's type is not equal to the original property's + // `TangentVector` type. + auto originalFieldTanType = originalFieldTan->getType(); + if (!originalFieldTanType->isEqual(tanField->getType())) { + return TangentPropertyInfo( + TangentPropertyInfo::Error::Kind::TangentPropertyWrongType, + originalFieldTanType); + } + // Error if tangent property is not a stored property. + if (!tanField->hasStorage()) { + return TangentPropertyInfo( + TangentPropertyInfo::Error::Kind::TangentPropertyNotStored); + } + // Otherwise, tangent property is valid. + return TangentPropertyInfo(tanField); +} diff --git a/lib/AST/Availability.cpp b/lib/AST/Availability.cpp index 7eacce69db72e..ecdbe6451fdf8 100644 --- a/lib/AST/Availability.cpp +++ b/lib/AST/Availability.cpp @@ -295,6 +295,15 @@ AvailabilityContext ASTContext::getPrespecializedGenericMetadataAvailability() { return getSwift53Availability(); } +AvailabilityContext ASTContext::getCompareTypeContextDescriptorsAvailability() { + return getSwiftFutureAvailability(); +} + +AvailabilityContext +ASTContext::getCompareProtocolConformanceDescriptorsAvailability() { + return getSwiftFutureAvailability(); +} + AvailabilityContext ASTContext::getSwift52Availability() { auto target = LangOpts.Target; diff --git a/lib/FrontendTool/ScanDependencies.cpp b/lib/FrontendTool/ScanDependencies.cpp index 874de771df525..1b0360726c8a3 100644 --- a/lib/FrontendTool/ScanDependencies.cpp +++ b/lib/FrontendTool/ScanDependencies.cpp @@ -459,12 +459,6 @@ bool swift::scanDependencies(CompilerInstance &instance) { break; } - // Swift -Onone support library. - if (invocation.shouldImportSwiftONoneSupport()) { - mainDependencies.addModuleDependency( - SWIFT_ONONE_SUPPORT, alreadyAddedModules); - } - // Add any implicit module names. for (const auto &moduleName : importInfo.ModuleNames) { mainDependencies.addModuleDependency(moduleName.str(), alreadyAddedModules); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 87f3207b6ef09..c7d01eb932b15 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -650,6 +650,26 @@ namespace RuntimeConstants { } return RuntimeAvailability::AlwaysAvailable; } + + RuntimeAvailability + CompareTypeContextDescriptorsAvailability(ASTContext &Context) { + auto featureAvailability = + Context.getCompareTypeContextDescriptorsAvailability(); + if (!isDeploymentAvailabilityContainedIn(Context, featureAvailability)) { + return RuntimeAvailability::ConditionallyAvailable; + } + return RuntimeAvailability::AlwaysAvailable; + } + + RuntimeAvailability + CompareProtocolConformanceDescriptorsAvailability(ASTContext &Context) { + auto featureAvailability = + Context.getCompareProtocolConformanceDescriptorsAvailability(); + if (!isDeploymentAvailabilityContainedIn(Context, featureAvailability)) { + return RuntimeAvailability::ConditionallyAvailable; + } + return RuntimeAvailability::AlwaysAvailable; + } } // namespace RuntimeConstants // We don't use enough attributes to justify generalizing the diff --git a/lib/SIL/Verifier/LinearLifetimeCheckerPrivate.h b/lib/SIL/Verifier/LinearLifetimeCheckerPrivate.h index cd98009e32348..bc6f452d65d0a 100644 --- a/lib/SIL/Verifier/LinearLifetimeCheckerPrivate.h +++ b/lib/SIL/Verifier/LinearLifetimeCheckerPrivate.h @@ -208,7 +208,7 @@ class LLVM_LIBRARY_VISIBILITY LinearLifetimeChecker::ErrorBuilder { } llvm::errs() << "Found ownership error?!\n"; - llvm::report_fatal_error("triggering standard assertion failure routine"); + assert(0 && "triggering standard assertion failure routine"); } }; diff --git a/lib/SILOptimizer/Differentiation/Common.cpp b/lib/SILOptimizer/Differentiation/Common.cpp index be3f802c0e7d2..4184387fd1624 100644 --- a/lib/SILOptimizer/Differentiation/Common.cpp +++ b/lib/SILOptimizer/Differentiation/Common.cpp @@ -17,6 +17,8 @@ #define DEBUG_TYPE "differentiation" #include "swift/SILOptimizer/Differentiation/Common.h" +#include "swift/AST/TypeCheckRequests.h" +#include "swift/SILOptimizer/Differentiation/ADContext.h" namespace swift { namespace autodiff { @@ -244,6 +246,97 @@ void collectMinimalIndicesForFunctionCall( })); } +//===----------------------------------------------------------------------===// +// Diagnostic utilities +//===----------------------------------------------------------------------===// + +SILLocation getValidLocation(SILValue v) { + auto loc = v.getLoc(); + if (loc.isNull() || loc.getSourceLoc().isInvalid()) + loc = v->getFunction()->getLocation(); + return loc; +} + +SILLocation getValidLocation(SILInstruction *inst) { + auto loc = inst->getLoc(); + if (loc.isNull() || loc.getSourceLoc().isInvalid()) + loc = inst->getFunction()->getLocation(); + return loc; +} + +//===----------------------------------------------------------------------===// +// Tangent property lookup utilities +//===----------------------------------------------------------------------===// + +VarDecl *getTangentStoredProperty(ADContext &context, VarDecl *originalField, + SILLocation loc, + DifferentiationInvoker invoker) { + auto &astCtx = context.getASTContext(); + auto tanFieldInfo = evaluateOrDefault( + astCtx.evaluator, TangentStoredPropertyRequest{originalField}, + TangentPropertyInfo(nullptr)); + // If no error, return the tangent property. + if (tanFieldInfo) + return tanFieldInfo.tangentProperty; + // Otherwise, diagnose error and return nullptr. + assert(tanFieldInfo.error); + auto *parentDC = originalField->getDeclContext(); + assert(parentDC->isTypeContext()); + auto parentDeclName = parentDC->getSelfNominalTypeDecl()->getNameStr(); + auto fieldName = originalField->getNameStr(); + auto sourceLoc = loc.getSourceLoc(); + switch (tanFieldInfo.error->kind) { + case TangentPropertyInfo::Error::Kind::NoDerivativeOriginalProperty: + llvm_unreachable( + "`@noDerivative` stored property accesses should not be " + "differentiated; activity analysis should not mark as varied"); + case TangentPropertyInfo::Error::Kind::NominalParentNotDifferentiable: + context.emitNondifferentiabilityError( + sourceLoc, invoker, + diag::autodiff_stored_property_parent_not_differentiable, + parentDeclName, fieldName); + break; + case TangentPropertyInfo::Error::Kind::OriginalPropertyNotDifferentiable: + context.emitNondifferentiabilityError( + sourceLoc, invoker, diag::autodiff_stored_property_not_differentiable, + parentDeclName, fieldName, originalField->getInterfaceType()); + break; + case TangentPropertyInfo::Error::Kind::ParentTangentVectorNotStruct: + context.emitNondifferentiabilityError( + sourceLoc, invoker, diag::autodiff_stored_property_tangent_not_struct, + parentDeclName, fieldName); + break; + case TangentPropertyInfo::Error::Kind::TangentPropertyNotFound: + context.emitNondifferentiabilityError( + sourceLoc, invoker, + diag::autodiff_stored_property_no_corresponding_tangent, parentDeclName, + fieldName); + break; + case TangentPropertyInfo::Error::Kind::TangentPropertyWrongType: + context.emitNondifferentiabilityError( + sourceLoc, invoker, diag::autodiff_tangent_property_wrong_type, + parentDeclName, fieldName, tanFieldInfo.error->getType()); + break; + case TangentPropertyInfo::Error::Kind::TangentPropertyNotStored: + context.emitNondifferentiabilityError( + sourceLoc, invoker, diag::autodiff_tangent_property_not_stored, + parentDeclName, fieldName); + break; + } + return nullptr; +} + +VarDecl *getTangentStoredProperty(ADContext &context, + FieldIndexCacheBase *projectionInst, + DifferentiationInvoker invoker) { + assert(isa(projectionInst) || + isa(projectionInst) || + isa(projectionInst)); + auto loc = getValidLocation(projectionInst); + return getTangentStoredProperty(context, projectionInst->getField(), loc, + invoker); +} + //===----------------------------------------------------------------------===// // Code emission utilities //===----------------------------------------------------------------------===// diff --git a/lib/SILOptimizer/Differentiation/JVPEmitter.cpp b/lib/SILOptimizer/Differentiation/JVPEmitter.cpp index 8f53b37667113..9e0cd4f47a2ae 100644 --- a/lib/SILOptimizer/Differentiation/JVPEmitter.cpp +++ b/lib/SILOptimizer/Differentiation/JVPEmitter.cpp @@ -547,29 +547,12 @@ CLONE_AND_EMIT_TANGENT(StructExtract, sei) { assert(!sei->getField()->getAttrs().hasAttribute() && "`struct_extract` with `@noDerivative` field should not be " "differentiated; activity analysis should not marked as varied."); - auto diffBuilder = getDifferentialBuilder(); - ; - auto tangentVectorTy = getRemappedTangentType(sei->getOperand()->getType()); - auto *tangentVectorDecl = tangentVectorTy.getStructOrBoundGenericStruct(); - // Find the corresponding field in the tangent space. - VarDecl *tanField = nullptr; - // If the tangent space is the original struct, then field is the same. - if (tangentVectorDecl == sei->getStructDecl()) - tanField = sei->getField(); - // Otherwise, look up the field by name. - else { - auto tanFieldLookup = - tangentVectorDecl->lookupDirect(sei->getField()->getName()); - if (tanFieldLookup.empty()) { - context.emitNondifferentiabilityError( - sei, invoker, diag::autodiff_stored_property_no_corresponding_tangent, - sei->getStructDecl()->getNameStr(), sei->getField()->getNameStr()); - errorOccurred = true; - return; - } - tanField = cast(tanFieldLookup.front()); + auto *tanField = getTangentStoredProperty(context, sei, invoker); + if (!tanField) { + errorOccurred = true; + return; } // Emit tangent `struct_extract`. auto tanStruct = @@ -590,32 +573,14 @@ CLONE_AND_EMIT_TANGENT(StructElementAddr, seai) { assert(!seai->getField()->getAttrs().hasAttribute() && "`struct_element_addr` with `@noDerivative` field should not be " "differentiated; activity analysis should not marked as varied."); - auto diffBuilder = getDifferentialBuilder(); auto *bb = seai->getParent(); - auto tangentVectorTy = getRemappedTangentType(seai->getOperand()->getType()); - auto *tangentVectorDecl = tangentVectorTy.getStructOrBoundGenericStruct(); - // Find the corresponding field in the tangent space. - VarDecl *tanField = nullptr; - // If the tangent space is the original struct, then field is the same. - if (tangentVectorDecl == seai->getStructDecl()) - tanField = seai->getField(); - // Otherwise, look up the field by name. - else { - auto tanFieldLookup = - tangentVectorDecl->lookupDirect(seai->getField()->getName()); - if (tanFieldLookup.empty()) { - context.emitNondifferentiabilityError( - seai, invoker, - diag::autodiff_stored_property_no_corresponding_tangent, - seai->getStructDecl()->getNameStr(), seai->getField()->getNameStr()); - errorOccurred = true; - return; - } - tanField = cast(tanFieldLookup.front()); + auto *tanField = getTangentStoredProperty(context, seai, invoker); + if (!tanField) { + errorOccurred = true; + return; } - // Emit tangent `struct_element_addr`. auto tanOperand = getTangentBuffer(bb, seai->getOperand()); auto tangentInst = diff --git a/lib/SILOptimizer/Differentiation/PullbackEmitter.cpp b/lib/SILOptimizer/Differentiation/PullbackEmitter.cpp index f6e2c5be52732..ff4d804ae5260 100644 --- a/lib/SILOptimizer/Differentiation/PullbackEmitter.cpp +++ b/lib/SILOptimizer/Differentiation/PullbackEmitter.cpp @@ -24,6 +24,7 @@ #include "swift/AST/Expr.h" #include "swift/AST/PropertyWrappers.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/SIL/InstructionUtils.h" #include "swift/SIL/Projection.h" #include "swift/SILOptimizer/PassManager/PrettyStackTrace.h" @@ -335,13 +336,8 @@ SILValue PullbackEmitter::getAdjointProjection(SILBasicBlock *origBB, assert(!seai->getField()->getAttrs().hasAttribute() && "`@noDerivative` struct projections should never be active"); auto adjSource = getAdjointBuffer(origBB, seai->getOperand()); - auto *tangentVectorDecl = - adjSource->getType().getStructOrBoundGenericStruct(); - // TODO(TF-970): Emit diagnostic when `TangentVector` is not a struct. - auto tanFieldLookup = - tangentVectorDecl->lookupDirect(seai->getField()->getName()); - assert(tanFieldLookup.size() == 1); - auto *tanField = cast(tanFieldLookup.front()); + auto *tanField = getTangentStoredProperty(getContext(), seai, getInvoker()); + assert(tanField && "Invalid projections should have been diagnosed"); return builder.createStructElementAddr(seai->getLoc(), adjSource, tanField); } // Handle `tuple_element_addr`. @@ -373,15 +369,8 @@ SILValue PullbackEmitter::getAdjointProjection(SILBasicBlock *origBB, // `TangentVector` struct. auto adjClass = materializeAdjointDirect(getAdjointValue(origBB, classOperand), loc); - auto *tangentVectorDecl = - adjClass->getType().getStructOrBoundGenericStruct(); - // TODO(TF-970): Replace assertions below with diagnostics. - assert(tangentVectorDecl && "`TangentVector` of a class must be a struct"); - auto tanFieldLookup = - tangentVectorDecl->lookupDirect(reai->getField()->getName()); - assert(tanFieldLookup.size() == 1 && - "Class `TangentVector` must have field of the same name"); - auto *tanField = cast(tanFieldLookup.front()); + auto *tanField = getTangentStoredProperty(getContext(), reai, getInvoker()); + assert(tanField && "Invalid projections should have been diagnosed"); // Create a local allocation for the element adjoint buffer. auto eltTanType = tanField->getValueInterfaceType()->getCanonicalType(); auto eltTanSILType = @@ -619,25 +608,19 @@ bool PullbackEmitter::runForSemanticMemberGetter() { "Getter should have one semantic result"); auto origResult = origFormalResults[*getIndices().results->begin()]; - // TODO(TF-970): Emit diagnostic when `TangentVector` is not a struct. auto tangentVectorSILTy = pullback.getConventions().getSingleSILResultType( TypeExpansionContext::minimal()); auto tangentVectorTy = tangentVectorSILTy.getASTType(); auto *tangentVectorDecl = tangentVectorTy->getStructOrBoundGenericStruct(); // Look up the corresponding field in the tangent space. - VarDecl *origField = cast(accessor->getStorage()); - VarDecl *tanField = nullptr; - auto tanFieldLookup = tangentVectorDecl->lookupDirect(origField->getName()); - if (tanFieldLookup.empty()) { - getContext().emitNondifferentiabilityError( - pbLoc.getSourceLoc(), getInvoker(), - diag::autodiff_stored_property_no_corresponding_tangent, - origSelf->getType().getASTType().getString(), origField->getNameStr()); + auto *origField = cast(accessor->getStorage()); + auto *tanField = + getTangentStoredProperty(getContext(), origField, pbLoc, getInvoker()); + if (!tanField) { errorOccurred = true; return true; } - tanField = cast(tanFieldLookup.front()); // Switch based on the base tangent struct's value category. // TODO(TF-1255): Simplify using unified adjoint value data structure. @@ -736,27 +719,14 @@ bool PullbackEmitter::runForSemanticMemberSetter() { SILValue origArg = original.getArgumentsWithoutIndirectResults()[0]; SILValue origSelf = original.getArgumentsWithoutIndirectResults()[1]; - // TODO(TF-970): Emit diagnostic when `TangentVector` is not a struct. - auto tangentVectorSILTy = pullback.getLoweredFunctionType() - ->getParameters()[0] - .getSILStorageInterfaceType(); - assert(tangentVectorSILTy.getCategory() == SILValueCategory::Address); - auto tangentVectorTy = tangentVectorSILTy.getASTType(); - auto *tangentVectorDecl = tangentVectorTy->getStructOrBoundGenericStruct(); - // Look up the corresponding field in the tangent space. - VarDecl *origField = cast(accessor->getStorage()); - VarDecl *tanField = nullptr; - auto tanFieldLookup = tangentVectorDecl->lookupDirect(origField->getName()); - if (tanFieldLookup.empty()) { - getContext().emitNondifferentiabilityError( - pbLoc.getSourceLoc(), getInvoker(), - diag::autodiff_stored_property_no_corresponding_tangent, - origSelf->getType().getASTType().getString(), origField->getNameStr()); + auto *origField = cast(accessor->getStorage()); + auto *tanField = + getTangentStoredProperty(getContext(), origField, pbLoc, getInvoker()); + if (!tanField) { errorOccurred = true; return true; } - tanField = cast(tanFieldLookup.front()); auto adjSelf = getAdjointBuffer(origEntry, origSelf); auto *adjSelfElt = builder.createStructElementAddr(pbLoc, adjSelf, tanField); @@ -797,6 +767,7 @@ bool PullbackEmitter::run() { "Functions without returns must have been diagnosed"); auto *origExit = &*origExitIt; + // Collect original formal results. SmallVector origFormalResults; collectAllFormalResultsInTypeOrder(original, origFormalResults); for (auto resultIndex : getIndices().results->getIndices()) { @@ -815,7 +786,7 @@ bool PullbackEmitter::run() { } } - // Get dominated active values in original blocks. + // Collect dominated active values in original basic blocks. // Adjoint values of dominated active values are passed as pullback block // arguments. DominanceOrder domOrder(original.getEntryBlock(), domInfo); @@ -829,15 +800,21 @@ bool PullbackEmitter::run() { auto &domBBActiveValues = activeValues[domNode->getBlock()]; bbActiveValues.append(domBBActiveValues.begin(), domBBActiveValues.end()); } - // Booleans tracking whether active-value-related errors have been emitted. - // This prevents duplicate diagnostics for the same active values. - bool diagnosedActiveEnumValue = false; - bool diagnosedActiveValueTangentValueCategoryIncompatible = false; - // Mark the activity of a value if it has not yet been visited. - auto markValueActivity = [&](SILValue v) { + // If `v` is active and has not been visited, records it as an active value + // in the original basic block. + // For active values unsupported by differentiation, emits a diagnostic and + // returns true. Otherwise, returns false. + auto recordValueIfActive = [&](SILValue v) -> bool { + // If value is not active, skip. + if (!getActivityInfo().isActive(v, getIndices())) + return false; + // If active value has already been visited, skip. if (visited.count(v)) - return; + return false; + // Mark active value as visited. visited.insert(v); + + // Diagnose unsupported active values. auto type = v->getType(); // Diagnose active values whose value category is incompatible with their // tangent types's value category. @@ -851,56 +828,61 @@ bool PullbackEmitter::run() { // $*A | $L | Yes (can create $*L adjoint buffer) // $L | $*A | No (cannot create $A adjoint value) // $*A | $*A | Yes (no mismatch) - if (!diagnosedActiveValueTangentValueCategoryIncompatible) { - if (auto tanSpace = getTangentSpace(remapType(type).getASTType())) { - auto tanASTType = tanSpace->getCanonicalType(); - auto &origTL = getTypeLowering(type.getASTType()); - auto &tanTL = getTypeLowering(tanASTType); - if (!origTL.isAddressOnly() && tanTL.isAddressOnly()) { - getContext().emitNondifferentiabilityError( - v, getInvoker(), - diag::autodiff_loadable_value_addressonly_tangent_unsupported, - type.getASTType(), tanASTType); - diagnosedActiveValueTangentValueCategoryIncompatible = true; - errorOccurred = true; - } + if (auto tanSpace = getTangentSpace(remapType(type).getASTType())) { + auto tanASTType = tanSpace->getCanonicalType(); + auto &origTL = getTypeLowering(type.getASTType()); + auto &tanTL = getTypeLowering(tanASTType); + if (!origTL.isAddressOnly() && tanTL.isAddressOnly()) { + getContext().emitNondifferentiabilityError( + v, getInvoker(), + diag::autodiff_loadable_value_addressonly_tangent_unsupported, + type.getASTType(), tanASTType); + errorOccurred = true; + return true; } } // Do not emit remaining activity-related diagnostics for semantic member // accessors, which have special-case pullback generation. if (isSemanticMemberAccessor(&original)) - return; + return false; // Diagnose active enum values. Differentiation of enum values requires // special adjoint value handling and is not yet supported. Diagnose // only the first active enum value to prevent too many diagnostics. - if (!diagnosedActiveEnumValue && type.getEnumOrBoundGenericEnum()) { + if (type.getEnumOrBoundGenericEnum()) { getContext().emitNondifferentiabilityError( v, getInvoker(), diag::autodiff_enums_unsupported); errorOccurred = true; - diagnosedActiveEnumValue = true; + return true; + } + // Diagnose unsupported stored property projections. + if (auto *inst = dyn_cast(v)) { + if (!getTangentStoredProperty(getContext(), inst, getInvoker())) { + errorOccurred = true; + return true; + } } // Skip address projections. // Address projections do not need their own adjoint buffers; they // become projections into their adjoint base buffer. if (Projection::isAddressProjection(v)) - return; + return false; + // Record active value. bbActiveValues.push_back(v); + return false; }; - // Visit bb arguments and all instruction operands/results. + // Record all active values in the basic block. for (auto *arg : bb->getArguments()) - if (getActivityInfo().isActive(arg, getIndices())) - markValueActivity(arg); + if (recordValueIfActive(arg)) + return true; for (auto &inst : *bb) { for (auto op : inst.getOperandValues()) - if (getActivityInfo().isActive(op, getIndices())) - markValueActivity(op); + if (recordValueIfActive(op)) + return true; for (auto result : inst.getResults()) - if (getActivityInfo().isActive(result, getIndices())) - markValueActivity(result); + if (recordValueIfActive(result)) + return true; } domOrder.pushChildren(bb); - if (errorOccurred) - return true; } // Create pullback blocks and arguments, visiting original blocks in @@ -1669,23 +1651,12 @@ void PullbackEmitter::visitStructInst(StructInst *si) { if (field->getAttrs().hasAttribute()) continue; // Find the corresponding field in the tangent space. - VarDecl *tanField = nullptr; - if (tangentVectorDecl == structDecl) - tanField = field; - // Otherwise, look up the field by name. - else { - auto tanFieldLookup = tangentVectorDecl->lookupDirect(field->getName()); - if (tanFieldLookup.empty()) { - getContext().emitNondifferentiabilityError( - si, getInvoker(), - diag::autodiff_stored_property_no_corresponding_tangent, - tangentVectorDecl->getNameStr(), field->getNameStr()); - errorOccurred = true; - return; - } - tanField = cast(tanFieldLookup.front()); + auto *tanField = + getTangentStoredProperty(getContext(), field, loc, getInvoker()); + if (!tanField) { + errorOccurred = true; + return; } - assert(tanField); auto tanElt = dti->getResult(fieldIndex); addAdjointValue(bb, si->getFieldValue(field), makeConcreteAdjointValue(tanElt), si->getLoc()); @@ -1712,36 +1683,16 @@ void PullbackEmitter::visitBeginApplyInst(BeginApplyInst *bai) { } void PullbackEmitter::visitStructExtractInst(StructExtractInst *sei) { - assert(!sei->getField()->getAttrs().hasAttribute() && - "`struct_extract` with `@noDerivative` field should not be " - "differentiated; activity analysis should not marked as varied"); auto *bb = sei->getParent(); auto structTy = remapType(sei->getOperand()->getType()).getASTType(); auto tangentVectorTy = getTangentSpace(structTy)->getCanonicalType(); assert(!getTypeLowering(tangentVectorTy).isAddressOnly()); auto tangentVectorSILTy = SILType::getPrimitiveObjectType(tangentVectorTy); auto *tangentVectorDecl = tangentVectorTy->getStructOrBoundGenericStruct(); - // TODO(TF-970): Emit diagnostic when `TangentVector` is not a struct. assert(tangentVectorDecl); // Find the corresponding field in the tangent space. - VarDecl *tanField = nullptr; - // If the tangent space is the original struct, then field is the same. - if (tangentVectorDecl == sei->getStructDecl()) - tanField = sei->getField(); - // Otherwise, look up the field by name. - else { - auto tanFieldLookup = - tangentVectorDecl->lookupDirect(sei->getField()->getName()); - if (tanFieldLookup.empty()) { - getContext().emitNondifferentiabilityError( - sei, getInvoker(), - diag::autodiff_stored_property_no_corresponding_tangent, - sei->getStructDecl()->getNameStr(), sei->getField()->getNameStr()); - errorOccurred = true; - return; - } - tanField = cast(tanFieldLookup.front()); - } + auto *tanField = getTangentStoredProperty(getContext(), sei, getInvoker()); + assert(tanField && "Invalid projections should have been diagnosed"); // Accumulate adjoint for the `struct_extract` operand. auto av = getAdjointValue(bb, sei); switch (av.getKind()) { @@ -1779,21 +1730,8 @@ void PullbackEmitter::visitRefElementAddrInst(RefElementAddrInst *reai) { assert(!getTypeLowering(tangentVectorTy).isAddressOnly()); auto tangentVectorSILTy = SILType::getPrimitiveObjectType(tangentVectorTy); auto *tangentVectorDecl = tangentVectorTy->getStructOrBoundGenericStruct(); - // TODO(TF-970): Emit diagnostic when `TangentVector` is not a struct. - assert(tangentVectorDecl); - // Look up the corresponding field in the tangent space by name. - VarDecl *tanField = nullptr; - auto tanFieldLookup = - tangentVectorDecl->lookupDirect(reai->getField()->getName()); - if (tanFieldLookup.empty()) { - getContext().emitNondifferentiabilityError( - reai, getInvoker(), - diag::autodiff_stored_property_no_corresponding_tangent, - reai->getClassDecl()->getNameStr(), reai->getField()->getNameStr()); - errorOccurred = true; - return; - } - tanField = cast(tanFieldLookup.front()); + auto *tanField = getTangentStoredProperty(getContext(), reai, getInvoker()); + assert(tanField && "Invalid projections should have been diagnosed"); // Accumulate adjoint for the `ref_element_addr` operand. SmallVector eltVals; for (auto *field : tangentVectorDecl->getStoredProperties()) { diff --git a/lib/SILOptimizer/Differentiation/Thunk.cpp b/lib/SILOptimizer/Differentiation/Thunk.cpp index 9d56ae56310b5..e5b15cc51e95f 100644 --- a/lib/SILOptimizer/Differentiation/Thunk.cpp +++ b/lib/SILOptimizer/Differentiation/Thunk.cpp @@ -237,7 +237,6 @@ CanSILFunctionType buildThunkType(SILFunction *fn, if (expectedType->hasErrorResult()) { auto errorResult = expectedType->getErrorResult(); interfaceErrorResult = errorResult.map(mapTypeOutOfContext); - ; } // The type of the thunk function. diff --git a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp index 8b35bfbc95a6d..9cde36a2239f4 100644 --- a/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp +++ b/lib/SILOptimizer/Transforms/AllocBoxToStack.cpp @@ -19,6 +19,7 @@ #include "swift/SIL/SILCloner.h" #include "swift/SILOptimizer/PassManager/Passes.h" #include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/InstOptUtils.h" #include "swift/SILOptimizer/Utils/SILOptFunctionBuilder.h" #include "swift/SILOptimizer/Utils/SpecializationMangler.h" #include "swift/SILOptimizer/Utils/StackNesting.h" @@ -28,12 +29,21 @@ #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" using namespace swift; STATISTIC(NumStackPromoted, "Number of alloc_box's promoted to the stack"); -//===----------------------------------------------------------------------===// +// MaxLocalApplyRecurDepth limits the recursive analysis depth while +// checking if a box can be promoted to stack. This is currently set to 4, a +// limit assumed to be sufficient to handle typical call chain of local +// functions through which a box can be passed. +static llvm::cl::opt MaxLocalApplyRecurDepth( + "max-local-apply-recur-depth", llvm::cl::init(4), + llvm::cl::desc("Max recursive depth for analyzing local functions")); + +//===-----------------------------------------------------------------------===// // SIL Utilities for alloc_box Promotion //===----------------------------------------------------------------------===// @@ -274,38 +284,76 @@ static bool partialApplyEscapes(SILValue V, bool examineApply) { return false; } -static SILInstruction *findUnexpectedBoxUse(SILValue Box, - bool examinePartialApply, - bool inAppliedFunction, - SmallVectorImpl &); +static SILInstruction *recursivelyFindBoxOperandsPromotableToAddress( + SILValue Box, bool inAppliedFunction, SmallVectorImpl &, + SmallPtrSetImpl &, unsigned CurrentRecurDepth); -/// checkPartialApplyBody - Check the body of a partial apply to see +/// checkLocalApplyBody - Check the body of an apply's callee to see /// if the box pointer argument passed to it has uses that would /// disqualify it from being promoted to a stack location. Return -/// true if this partial apply will not block our promoting the box. -static bool checkPartialApplyBody(Operand *O) { +/// true if this apply will not block our promoting the box. +static bool checkLocalApplyBody(Operand *O, + SmallVectorImpl &PromotedOperands, + SmallPtrSetImpl &VisitedCallees, + unsigned CurrentRecurDepth) { SILFunction *F = ApplySite(O->getUser()).getReferencedFunctionOrNull(); // If we cannot examine the function body, assume the worst. if (!F || F->empty()) return false; - // We don't actually use these because we're not recursively - // rewriting the partial applies we find. - SmallVector PromotedOperands; + // Since this function can be called recursively while analyzing the same box, + // mark the callee as visited, so that we don't end up in a recursive cycle. + auto iter = VisitedCallees.insert(F); + if (!iter.second) + return false; + auto calleeArg = F->getArgument(ApplySite(O->getUser()).getCalleeArgIndex(*O)); - return !findUnexpectedBoxUse(calleeArg, /* examinePartialApply = */ false, - /* inAppliedFunction = */ true, - PromotedOperands); + auto res = !recursivelyFindBoxOperandsPromotableToAddress( + calleeArg, + /* inAppliedFunction = */ true, PromotedOperands, VisitedCallees, + CurrentRecurDepth + 1); + return res; +} + +// Returns true if a callee is eligible to be cloned and rewritten for +// AllocBoxToStack opt. We don't want to increase code size, so this is +// restricted only for private local functions currently. +static bool isOptimizableApplySite(ApplySite Apply) { + auto callee = Apply.getReferencedFunctionOrNull(); + if (!callee) { + return false; + } + + // Callee should be optimizable. + if (!callee->shouldOptimize()) + return false; + + // External function definitions. + if (!callee->isDefinition()) + return false; + + // Do not optimize always_inlinable functions. + if (callee->getInlineStrategy() == Inline_t::AlwaysInline) + return false; + + if (callee->getLinkage() != SILLinkage::Private) + return false; + + return true; } /// Validate that the uses of a pointer to a box do not eliminate it from -/// consideration for promotion to a stack element. Optionally examine the body -/// of partial_apply to see if there is an unexpected use inside. Return the -/// instruction with the unexpected use if we find one. -static SILInstruction * -findUnexpectedBoxUse(SILValue Box, bool examinePartialApply, - bool inAppliedFunction, - SmallVectorImpl &PromotedOperands) { +/// consideration for promotion to a stack element. Return the instruction with +/// the unexpected use if we find one. +/// If a box has ApplySite users, we recursively examine the callees to check +/// for unexpected use of the box argument. If all the callees through which the +/// box is passed don't have any unexpected uses, `PromotedOperands` will be +/// populated with the box arguments in DFS order. +static SILInstruction *recursivelyFindBoxOperandsPromotableToAddress( + SILValue Box, bool inAppliedFunction, + SmallVectorImpl &PromotedOperands, + SmallPtrSetImpl &VisitedCallees, + unsigned CurrentRecurDepth = 0) { assert((Box->getType().is() || Box->getType() == SILType::getNativeObjectType(Box->getType().getASTContext())) @@ -337,15 +385,32 @@ findUnexpectedBoxUse(SILValue Box, bool examinePartialApply, continue; } - // For partial_apply, if we've been asked to examine the body, the - // uses of the argument are okay there, and the partial_apply - // itself cannot escape, then everything is fine. - if (auto *PAI = dyn_cast(User)) - if (examinePartialApply && checkPartialApplyBody(Op) && - !partialApplyEscapes(PAI, /* examineApply = */ true)) { - LocalPromotedOperands.push_back(Op); - continue; + if (auto Apply = ApplySite::isa(User)) { + if (CurrentRecurDepth > MaxLocalApplyRecurDepth) { + return User; + } + switch (Apply.getKind()) { + case ApplySiteKind::PartialApplyInst: { + if (checkLocalApplyBody(Op, LocalPromotedOperands, VisitedCallees, + CurrentRecurDepth) && + !partialApplyEscapes(cast(User), + /* examineApply = */ true)) { + LocalPromotedOperands.push_back(Op); + continue; + } + break; } + case ApplySiteKind::ApplyInst: + case ApplySiteKind::BeginApplyInst: + case ApplySiteKind::TryApplyInst: + if (isOptimizableApplySite(Apply) && + checkLocalApplyBody(Op, LocalPromotedOperands, VisitedCallees, + CurrentRecurDepth)) { + LocalPromotedOperands.push_back(Op); + continue; + } + } + } return User; } @@ -364,12 +429,13 @@ static InFlightDiagnostic diagnose(ASTContext &Context, SourceLoc loc, /// canPromoteAllocBox - Can we promote this alloc_box to an alloc_stack? static bool canPromoteAllocBox(AllocBoxInst *ABI, SmallVectorImpl &PromotedOperands) { + SmallPtrSet VisitedCallees; // Scan all of the uses of the address of the box to see if any // disqualifies the box from being promoted to the stack. - if (auto *User = findUnexpectedBoxUse(ABI, - /* examinePartialApply = */ true, - /* inAppliedFunction = */ false, - PromotedOperands)) { + if (auto *User = recursivelyFindBoxOperandsPromotableToAddress( + ABI, + /* inAppliedFunction = */ false, PromotedOperands, VisitedCallees, + /* CurrentRecurDepth = */ 0)) { (void)User; // Otherwise, we have an unexpected use. LLVM_DEBUG(llvm::dbgs() << "*** Failed to promote alloc_box in @" @@ -557,6 +623,9 @@ class PromotedParamCloner : public SILClonerWithScopes { void visitStrongRetainInst(StrongRetainInst *Inst); void visitCopyValueInst(CopyValueInst *Inst); void visitProjectBoxInst(ProjectBoxInst *Inst); + void checkNoPromotedBoxInApply(ApplySite Apply); +#define APPLYSITE_INST(Name, Parent) void visit##Name(Name *Inst); +#include "swift/SIL/SILNodes.def" }; } // end anonymous namespace @@ -633,11 +702,12 @@ SILFunction *PromotedParamCloner::initCloned(SILOptFunctionBuilder &FuncBuilder, && "SILFunction missing DebugScope"); assert(!Orig->isGlobalInit() && "Global initializer cannot be cloned"); auto *Fn = FuncBuilder.createFunction( - SILLinkage::Shared, ClonedName, ClonedTy, Orig->getGenericEnvironment(), - Orig->getLocation(), Orig->isBare(), Orig->isTransparent(), Serialized, - IsNotDynamic, Orig->getEntryCount(), Orig->isThunk(), - Orig->getClassSubclassScope(), Orig->getInlineStrategy(), - Orig->getEffectsKind(), Orig, Orig->getDebugScope()); + swift::getSpecializedLinkage(Orig, Orig->getLinkage()), ClonedName, + ClonedTy, Orig->getGenericEnvironment(), Orig->getLocation(), + Orig->isBare(), Orig->isTransparent(), Serialized, IsNotDynamic, + Orig->getEntryCount(), Orig->isThunk(), Orig->getClassSubclassScope(), + Orig->getInlineStrategy(), Orig->getEffectsKind(), Orig, + Orig->getDebugScope()); for (auto &Attr : Orig->getSemanticsAttrs()) { Fn->addSemanticsAttr(Attr); } @@ -678,9 +748,12 @@ PromotedParamCloner::populateCloned() { OrigPromotedParameters.insert(*I); NewPromotedArgs[ArgNo] = promotedArg; - - // All uses of the promoted box should either be projections, which are - // folded when visited, or copy/destroy operations which are ignored. + // We only promote boxes used in apply or projections or copy/destroy + // value operations. + // We should never see an apply user of the box, because we rewrite the + // applies and specialize the callees in dfs order. + // Projection users are folded when visited and copy/destroy operations + // are ignored. entryArgs.push_back(SILValue()); } else { // Create a new argument which copies the original argument. @@ -760,34 +833,63 @@ void PromotedParamCloner::visitProjectBoxInst(ProjectBoxInst *Inst) { SILCloner::visitProjectBoxInst(Inst); } -/// Specialize a partial_apply by promoting the parameters indicated by +// While cloning during specialization, make sure apply instructions do not have +// box arguments that need to be promoted. +// This is an assertion in debug builds only. The reason why this should never +// be true is that we have cloned our callees in DFS order meaning that any of +// our callees that had a promotable box will have already have been promoted +// away by the time this runs. +void PromotedParamCloner::checkNoPromotedBoxInApply(ApplySite Apply) { +#ifndef NDEBUG + for (auto &O : Apply.getArgumentOperands()) { + assert(OrigPromotedParameters.count(O.get()) == 0); + } +#endif +} +void PromotedParamCloner::visitApplyInst(ApplyInst *Inst) { + checkNoPromotedBoxInApply(Inst); + SILCloner::visitApplyInst(Inst); +} +void PromotedParamCloner::visitBeginApplyInst(BeginApplyInst *Inst) { + checkNoPromotedBoxInApply(Inst); + SILCloner::visitBeginApplyInst(Inst); +} +void PromotedParamCloner::visitPartialApplyInst(PartialApplyInst *Inst) { + checkNoPromotedBoxInApply(Inst); + SILCloner::visitPartialApplyInst(Inst); +} +void PromotedParamCloner::visitTryApplyInst(TryApplyInst *Inst) { + checkNoPromotedBoxInApply(Inst); + SILCloner::visitTryApplyInst(Inst); +} + +/// Specialize ApplySite by promoting the parameters indicated by /// indices. We expect these parameters to be replaced by stack address /// references. -static PartialApplyInst * -specializePartialApply(SILOptFunctionBuilder &FuncBuilder, - PartialApplyInst *PartialApply, - ArgIndexList &PromotedCalleeArgIndices, - AllocBoxToStackState &pass) { - auto *FRI = cast(PartialApply->getCallee()); - assert(FRI && "Expected a direct partial_apply!"); +static SILInstruction * +specializeApplySite(SILOptFunctionBuilder &FuncBuilder, ApplySite Apply, + ArgIndexList &PromotedCalleeArgIndices, + AllocBoxToStackState &pass) { + auto *FRI = cast(Apply.getCallee()); + assert(FRI && "Expected a direct ApplySite"); auto *F = FRI->getReferencedFunctionOrNull(); assert(F && "Expected a referenced function!"); IsSerialized_t Serialized = IsNotSerialized; - if (PartialApply->getFunction()->isSerialized()) + if (Apply.getFunction()->isSerialized()) Serialized = IsSerializable; std::string ClonedName = getClonedName(F, Serialized, PromotedCalleeArgIndices); - auto &M = PartialApply->getModule(); + auto &M = Apply.getModule(); SILFunction *ClonedFn; if (auto *PrevFn = M.lookUpFunction(ClonedName)) { assert(PrevFn->isSerialized() == Serialized); ClonedFn = PrevFn; } else { - // Clone the function the existing partial_apply references. + // Clone the function the existing ApplySite references. PromotedParamCloner Cloner(FuncBuilder, F, Serialized, PromotedCalleeArgIndices, ClonedName); @@ -796,95 +898,133 @@ specializePartialApply(SILOptFunctionBuilder &FuncBuilder, pass.T->addFunctionToPassManagerWorklist(ClonedFn, F); } - // Now create the new partial_apply using the cloned function. + // Now create the new ApplySite using the cloned function. SmallVector Args; ValueLifetimeAnalysis::Frontier PAFrontier; // Promote the arguments that need promotion. - for (auto &O : PartialApply->getArgumentOperands()) { + for (auto &O : Apply.getArgumentOperands()) { auto CalleeArgIndex = ApplySite(O.getUser()).getCalleeArgIndex(O); if (!count(PromotedCalleeArgIndices, CalleeArgIndex)) { Args.push_back(O.get()); continue; } - // If this argument is promoted, it is a box that we're turning into an - // address because we've proven we can keep this value on the stack. The - // partial_apply had ownership of this box so we must now release it - // explicitly when the partial_apply is released. - auto *Box = cast(O.get()); - assert((isa(Box) || isa(Box)) && - "Expected either an alloc box or a copy of an alloc box"); - SILBuilder B(Box); - Args.push_back(B.createProjectBox(Box->getLoc(), Box, 0)); - - if (PAFrontier.empty()) { - ValueLifetimeAnalysis VLA(PartialApply); - pass.CFGChanged |= !VLA.computeFrontier( - PAFrontier, ValueLifetimeAnalysis::AllowToModifyCFG); - assert(!PAFrontier.empty() && "partial_apply must have at least one use " - "to release the returned function"); - } + auto Box = O.get(); + assert(((isa(Box) && isa(Box) || + isa(Box)) || + isa(Box)) && + "Expected either an alloc box or a copy of an alloc box or a " + "function argument"); + auto InsertPt = Box->getDefiningInsertionPoint(); + assert(InsertPt); + SILBuilderWithScope B(InsertPt); + Args.push_back(B.createProjectBox(Box.getLoc(), Box, 0)); + + // For a partial_apply, if this argument is promoted, it is a box that we're + // turning into an address because we've proven we can keep this value on + // the stack. The partial_apply had ownership of this box so we must now + // release it explicitly when the partial_apply is released. + if (Apply.getKind() == ApplySiteKind::PartialApplyInst) { + if (PAFrontier.empty()) { + ValueLifetimeAnalysis VLA(cast(Apply)); + pass.CFGChanged |= !VLA.computeFrontier( + PAFrontier, ValueLifetimeAnalysis::AllowToModifyCFG); + assert(!PAFrontier.empty() && + "partial_apply must have at least one use " + "to release the returned function"); + } - // Insert destroys of the box at each point where the partial_apply becomes - // dead. - for (SILInstruction *FrontierInst : PAFrontier) { - SILBuilderWithScope Builder(FrontierInst); - Builder.createDestroyValue(PartialApply->getLoc(), Box); + // Insert destroys of the box at each point where the partial_apply + // becomes dead. + for (SILInstruction *FrontierInst : PAFrontier) { + SILBuilderWithScope Builder(FrontierInst); + Builder.createDestroyValue(Apply.getLoc(), Box); + } } } - SILBuilderWithScope Builder(PartialApply); - - // Build the function_ref and partial_apply. - SILValue FunctionRef = Builder.createFunctionRef(PartialApply->getLoc(), - ClonedFn); - return Builder.createPartialApply( - PartialApply->getLoc(), FunctionRef, PartialApply->getSubstitutionMap(), - Args, - PartialApply->getType().getAs()->getCalleeConvention()); + auto ApplyInst = Apply.getInstruction(); + SILBuilderWithScope Builder(ApplyInst); + + // Build the function_ref and ApplySite. + SILValue FunctionRef = Builder.createFunctionRef(Apply.getLoc(), ClonedFn); + switch (Apply.getKind()) { + case ApplySiteKind::PartialApplyInst: { + auto *PAI = cast(ApplyInst); + return Builder.createPartialApply( + Apply.getLoc(), FunctionRef, Apply.getSubstitutionMap(), Args, + PAI->getType().getAs()->getCalleeConvention(), + PAI->isOnStack(), + GenericSpecializationInformation::create(ApplyInst, Builder)); + } + case ApplySiteKind::ApplyInst: + return Builder.createApply( + Apply.getLoc(), FunctionRef, Apply.getSubstitutionMap(), Args, + GenericSpecializationInformation::create(ApplyInst, Builder)); + case ApplySiteKind::BeginApplyInst: + return Builder.createBeginApply( + Apply.getLoc(), FunctionRef, Apply.getSubstitutionMap(), Args, + GenericSpecializationInformation::create(ApplyInst, Builder)); + case ApplySiteKind::TryApplyInst: { + auto TAI = cast(Apply); + return Builder.createTryApply( + Apply.getLoc(), FunctionRef, Apply.getSubstitutionMap(), Args, + TAI->getNormalBB(), TAI->getErrorBB(), + GenericSpecializationInformation::create(ApplyInst, Builder)); + } + } } -static void rewritePartialApplies(AllocBoxToStackState &pass) { - llvm::DenseMap IndexMap; +static void rewriteApplySites(AllocBoxToStackState &pass) { + llvm::DenseMap IndexMap; + llvm::SmallVector AppliesToSpecialize; ArgIndexList Indices; - // Build a map from partial_apply to the indices of the operands + // Build a map from the ApplySite to the indices of the operands // that will be promoted in our rewritten version. for (auto *O : pass.PromotedOperands) { - auto CalleeArgIndexNumber = ApplySite(O->getUser()).getCalleeArgIndex(*O); + auto User = O->getUser(); + auto Apply = ApplySite(User); + + auto CalleeArgIndexNumber = Apply.getCalleeArgIndex(*O); Indices.clear(); Indices.push_back(CalleeArgIndexNumber); - auto *PartialApply = cast(O->getUser()); - llvm::DenseMap::iterator It; - bool Inserted; - std::tie(It, Inserted) = IndexMap.insert(std::make_pair(PartialApply, - Indices)); - if (!Inserted) - It->second.push_back(CalleeArgIndexNumber); + auto iterAndSuccess = IndexMap.try_emplace(Apply, Indices); + // AllocBoxStack opt promotes boxes passed to a chain of applies when it is + // safe to do so. All such applies have to be specialized to take pointer + // arguments instead of box arguments. This has to be done in dfs order. + // Since PromotedOperands is already populated in dfs order by + // `recursivelyFindBoxOperandsPromotableToAddress`. Build + // `AppliesToSpecialize` in the same order. + if (iterAndSuccess.second) { + AppliesToSpecialize.push_back(Apply); + } else { + iterAndSuccess.first->second.push_back(CalleeArgIndexNumber); + } } - // Clone the referenced function of each partial_apply, removing the + // Clone the referenced function of each ApplySite, removing the // operands that we will not need, and remove the existing - // partial_apply. + // ApplySite. SILOptFunctionBuilder FuncBuilder(*pass.T); - for (auto &It : IndexMap) { - auto *PartialApply = It.first; - auto &Indices = It.second; + for (auto &Apply : AppliesToSpecialize) { + auto It = IndexMap.find(Apply); + assert(It != IndexMap.end()); + auto &Indices = It->second; // Sort the indices and unique them. - std::sort(Indices.begin(), Indices.end()); - Indices.erase(std::unique(Indices.begin(), Indices.end()), Indices.end()); + sortUnique(Indices); - PartialApplyInst *Replacement = - specializePartialApply(FuncBuilder, PartialApply, Indices, pass); - PartialApply->replaceAllUsesWith(Replacement); + auto *Replacement = specializeApplySite(FuncBuilder, Apply, Indices, pass); + assert(Apply.getKind() == ApplySite(Replacement).getKind()); + Apply.getInstruction()->replaceAllUsesPairwiseWith(Replacement); - auto *FRI = cast(PartialApply->getCallee()); - PartialApply->eraseFromParent(); + auto *FRI = cast(Apply.getCallee()); + Apply.getInstruction()->eraseFromParent(); // TODO: Erase from module if there are no more uses. if (FRI->use_empty()) @@ -895,9 +1035,9 @@ static void rewritePartialApplies(AllocBoxToStackState &pass) { /// Clone closure bodies and rewrite partial applies. Returns the number of /// alloc_box allocations promoted. static unsigned rewritePromotedBoxes(AllocBoxToStackState &pass) { - // First we'll rewrite any partial applies that we can to remove the - // box container pointer from the operands. - rewritePartialApplies(pass); + // First we'll rewrite any ApplySite that we can to remove + // the box container pointer from the operands. + rewriteApplySites(pass); unsigned Count = 0; auto rend = pass.Promotable.rend(); diff --git a/lib/Sema/DerivedConformanceDifferentiable.cpp b/lib/Sema/DerivedConformanceDifferentiable.cpp index 78312f6779e86..f0e6e10b5406b 100644 --- a/lib/Sema/DerivedConformanceDifferentiable.cpp +++ b/lib/Sema/DerivedConformanceDifferentiable.cpp @@ -26,6 +26,7 @@ #include "swift/AST/PropertyWrappers.h" #include "swift/AST/ProtocolConformance.h" #include "swift/AST/Stmt.h" +#include "swift/AST/TypeCheckRequests.h" #include "swift/AST/Types.h" #include "DerivedConformances.h" @@ -646,22 +647,21 @@ getOrSynthesizeTangentVectorStruct(DerivedConformance &derived, Identifier id) { structDecl->setImplicit(); structDecl->copyFormalAccessFrom(nominal, /*sourceIsParentContext*/ true); - // Add members to `TangentVector` struct. + // Add stored properties to the `TangentVector` struct. for (auto *member : diffProperties) { - // Add this member's corresponding `TangentVector` type to the parent's - // `TangentVector` struct. - // Note: `newMember` is not marked as implicit here, because that - // incorrectly affects memberwise initializer synthesis. - auto *newMember = new (C) VarDecl( + // Add a tangent stored property to the `TangentVector` struct, with the + // name and `TangentVector` type of the original property. + auto *tangentProperty = new (C) VarDecl( member->isStatic(), member->getIntroducer(), member->isCaptureList(), /*NameLoc*/ SourceLoc(), member->getName(), structDecl); - + // Note: `tangentProperty` is not marked as implicit here, because that + // incorrectly affects memberwise initializer synthesis. auto memberContextualType = parentDC->mapTypeIntoContext(member->getValueInterfaceType()); auto memberTanType = getTangentVectorInterfaceType(memberContextualType, parentDC); - newMember->setInterfaceType(memberTanType); - Pattern *memberPattern = NamedPattern::createImplicit(C, newMember); + tangentProperty->setInterfaceType(memberTanType); + Pattern *memberPattern = NamedPattern::createImplicit(C, tangentProperty); memberPattern->setType(memberTanType); memberPattern = TypedPattern::createImplicit(C, memberPattern, memberTanType); @@ -669,16 +669,21 @@ getOrSynthesizeTangentVectorStruct(DerivedConformance &derived, Identifier id) { auto *memberBinding = PatternBindingDecl::createImplicit( C, StaticSpellingKind::None, memberPattern, /*initExpr*/ nullptr, structDecl); - structDecl->addMember(newMember); + structDecl->addMember(tangentProperty); structDecl->addMember(memberBinding); - newMember->copyFormalAccessFrom(member, /*sourceIsParentContext*/ true); - newMember->setSetterAccess(member->getFormalAccess()); - - // Now that this member is in the `TangentVector` type, it should be marked - // `@differentiable` so that the differentiation transform will synthesize - // derivative functions for it. We only add this to public stored - // properties, because their access outside the module will go through a - // call to the getter. + tangentProperty->copyFormalAccessFrom(member, + /*sourceIsParentContext*/ true); + tangentProperty->setSetterAccess(member->getFormalAccess()); + + // Cache the tangent property. + C.evaluator.cacheOutput(TangentStoredPropertyRequest{member}, + TangentPropertyInfo(tangentProperty)); + + // Now that the original property has a corresponding tangent property, it + // should be marked `@differentiable` so that the differentiation transform + // will synthesize derivative functions for its accessors. We only add this + // to public stored properties, because their access outside the module will + // go through accessor declarations. if (member->getEffectiveAccess() > AccessLevel::Internal && !member->getAttrs().hasAttribute()) { auto *getter = member->getSynthesizedAccessor(AccessorKind::Get); diff --git a/stdlib/public/core/Sequence.swift b/stdlib/public/core/Sequence.swift index e05cc19eb0892..f926324314e2e 100644 --- a/stdlib/public/core/Sequence.swift +++ b/stdlib/public/core/Sequence.swift @@ -98,7 +98,7 @@ /// } /// } /// print(longestAnimal) -/// // Prints "Butterfly" +/// // Prints Optional("Butterfly") /// /// Using Multiple Iterators /// ======================== diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index e4572d8529479..5703748dfc182 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -1913,6 +1913,33 @@ bool swift::equalContexts(const ContextDescriptor *a, } } +SWIFT_CC(swift) +bool swift::swift_compareTypeContextDescriptors( + const TypeContextDescriptor *a, const TypeContextDescriptor *b) { + // The implementation is the same as the implementation of + // swift::equalContexts except that the handling of non-type + // context descriptors and casts to TypeContextDescriptor are removed. + + // Fast path: pointer equality. + if (a == b) return true; + + // If either context is null, we're done. + if (a == nullptr || b == nullptr) + return false; + + // If either descriptor is known to be unique, we're done. + if (a->isUnique() || b->isUnique()) return false; + + // Do the kinds match? + if (a->getKind() != b->getKind()) return false; + + // Do the parents match? + if (!equalContexts(a->Parent.get(), b->Parent.get())) + return false; + + return TypeContextIdentity(a) == TypeContextIdentity(b); +} + /***************************************************************************/ /*** Common value witnesses ************************************************/ /***************************************************************************/ @@ -4966,6 +4993,12 @@ const WitnessTable *swift::swift_getAssociatedConformanceWitness( assocConformance); } +bool swift::swift_compareProtocolConformanceDescriptors( + const ProtocolConformanceDescriptor *lhs, + const ProtocolConformanceDescriptor *rhs) { + return MetadataCacheKey::compareProtocolConformanceDescriptors(lhs, rhs) == 0; +} + /***************************************************************************/ /*** Recursive metadata dependencies ***************************************/ /***************************************************************************/ diff --git a/stdlib/public/runtime/MetadataCache.h b/stdlib/public/runtime/MetadataCache.h index 047f97816197d..6e471273e4702 100644 --- a/stdlib/public/runtime/MetadataCache.h +++ b/stdlib/public/runtime/MetadataCache.h @@ -386,6 +386,14 @@ class MetadataCacheKey { auto *aDescription = awt->getDescription(); auto *bDescription = bwt->getDescription(); + return compareProtocolConformanceDescriptors(aDescription, bDescription); + } + +public: + /// Compare two conformance descriptors, checking their contents if necessary. + static int compareProtocolConformanceDescriptors( + const ProtocolConformanceDescriptor *aDescription, + const ProtocolConformanceDescriptor *bDescription) { if (aDescription == bDescription) return 0; @@ -405,6 +413,7 @@ class MetadataCacheKey { bDescription->getProtocol()); } +private: /// Compare the content from two keys. static int compareContent(const void * const *adata, const void * const *bdata, diff --git a/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift b/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift index 2aaca3f5f1d3c..db81b705c680c 100644 --- a/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift +++ b/test/AutoDiff/SILOptimizer/differentiation_diagnostics.swift @@ -470,6 +470,178 @@ func testInoutParameterAndFormalResult(_ x: Float) -> Float { return inoutParameterAndFormalResult(&x) } +//===----------------------------------------------------------------------===// +// Stored property access differentiation +//===----------------------------------------------------------------------===// + +// Test differentiation of invalid stored property access instructions: +// `struct_extract`, `struct_element_addr`, `ref_element_addr`. + +struct StructTangentVectorNotStruct: Differentiable { + var x: Float + + enum TangentVector: Differentiable, AdditiveArithmetic { + case x(Float) + typealias TangentVector = Self + static func ==(_: Self, _: Self) -> Bool { fatalError() } + static var zero: Self { fatalError() } + static func +(_: Self, _: Self) -> Self { fatalError() } + static func -(_: Self, _: Self) -> Self { fatalError() } + } + mutating func move(along direction: TangentVector) {} +} + +// expected-error @+2 {{function is not differentiable}} +// expected-note @+3 {{when differentiating this function definition}} +@differentiable +@_silgen_name("test_struct_tangent_vector_not_struct") +func testStructTangentVectorNotStruct(_ s: StructTangentVectorNotStruct) -> Float { + // expected-note @+1 {{cannot differentiate access to property 'StructTangentVectorNotStruct.x' because 'StructTangentVectorNotStruct.TangentVector' is not a struct}} + return s.x +} + +// CHECK-LABEL: sil {{.*}} @test_struct_tangent_vector_not_struct +// CHECK: struct_extract {{%.*}} : $StructTangentVectorNotStruct, #StructTangentVectorNotStruct.x + +struct StructOriginalPropertyNotDifferentiable: Differentiable { + struct Nondiff { + var x: Float + } + var nondiff: Nondiff + + struct TangentVector: Differentiable & AdditiveArithmetic { + var nondiff: Float + } + mutating func move(along direction: TangentVector) {} +} + +// expected-error @+2 {{function is not differentiable}} +// expected-note @+3 {{when differentiating this function definition}} +@differentiable +@_silgen_name("test_struct_original_property_not_differentiable") +func testStructOriginalPropertyNotDifferentiable(_ s: StructOriginalPropertyNotDifferentiable) -> Float { + // expected-note @+1 {{cannot differentiate access to property 'StructOriginalPropertyNotDifferentiable.nondiff' because property type 'StructOriginalPropertyNotDifferentiable.Nondiff' does not conform to 'Differentiable'}} + return s.nondiff.x +} + +// CHECK-LABEL: sil {{.*}} @test_struct_original_property_not_differentiable +// CHECK: struct_extract {{%.*}} : $StructOriginalPropertyNotDifferentiable, #StructOriginalPropertyNotDifferentiable.nondiff + +struct StructTangentVectorPropertyNotFound: Differentiable { + var x: Float + + struct TangentVector: Differentiable, AdditiveArithmetic { + var y: Float + } + mutating func move(along direction: TangentVector) {} +} + +// expected-error @+2 {{function is not differentiable}} +// expected-note @+3 {{when differentiating this function definition}} +@differentiable +@_silgen_name("test_struct_tangent_property_not_found") +func testStructTangentPropertyNotFound(_ s: StructTangentVectorPropertyNotFound) -> Float { + // expected-warning @+1 {{variable 'tmp' was never mutated}} + var tmp = s + // expected-note @+1 {{cannot differentiate access to property 'StructTangentVectorPropertyNotFound.x' because 'StructTangentVectorPropertyNotFound.TangentVector' does not have a stored property named 'x'}} + return tmp.x +} + +// CHECK-LABEL: sil {{.*}} @test_struct_tangent_property_not_found +// CHECK: struct_element_addr {{%.*}} : $*StructTangentVectorPropertyNotFound, #StructTangentVectorPropertyNotFound.x + +struct StructTangentPropertyWrongType: Differentiable { + var x: Float + + struct TangentVector: Differentiable, AdditiveArithmetic { + var x: Double + } + mutating func move(along direction: TangentVector) {} +} + +// expected-error @+2 {{function is not differentiable}} +// expected-note @+3 {{when differentiating this function definition}} +@differentiable +@_silgen_name("test_struct_tangent_property_wrong_type") +func testStructTangentPropertyWrongType(_ s: StructTangentPropertyWrongType) -> Float { + // expected-warning @+1 {{variable 'tmp' was never mutated}} + var tmp = s + // expected-note @+1 {{cannot differentiate access to property 'StructTangentPropertyWrongType.x' because 'StructTangentPropertyWrongType.TangentVector.x' does not have expected type 'Float.TangentVector' (aka 'Float')}} + return tmp.x +} + +// CHECK-LABEL: sil {{.*}} @test_struct_tangent_property_wrong_type +// CHECK: struct_element_addr {{%.*}} : $*StructTangentPropertyWrongType, #StructTangentPropertyWrongType.x + +final class ClassTangentPropertyWrongType: Differentiable { + var x: Float = 0 + + struct TangentVector: Differentiable, AdditiveArithmetic { + var x: Double + } + func move(along direction: TangentVector) {} +} + +// expected-error @+2 {{function is not differentiable}} +// expected-note @+3 {{when differentiating this function definition}} +@differentiable +@_silgen_name("test_class_tangent_property_wrong_type") +func testClassTangentPropertyWrongType(_ c: ClassTangentPropertyWrongType) -> Float { + // expected-warning @+1 {{variable 'tmp' was never mutated}} + var tmp = c + // expected-note @+1 {{cannot differentiate access to property 'ClassTangentPropertyWrongType.x' because 'ClassTangentPropertyWrongType.TangentVector.x' does not have expected type 'Float.TangentVector' (aka 'Float')}} + return tmp.x +} + +// CHECK-LABEL: sil {{.*}} @test_class_tangent_property_wrong_type +// CHECK: ref_element_addr {{%.*}} : $ClassTangentPropertyWrongType, #ClassTangentPropertyWrongType.x + +struct StructTangentPropertyNotStored: Differentiable { + var x: Float + + struct TangentVector: Differentiable, AdditiveArithmetic { + var x: Float { 0 } + } + mutating func move(along direction: TangentVector) {} +} + +// expected-error @+2 {{function is not differentiable}} +// expected-note @+3 {{when differentiating this function definition}} +@differentiable +@_silgen_name("test_struct_tangent_property_not_stored") +func testStructTangentPropertyNotStored(_ s: StructTangentPropertyNotStored) -> Float { + // expected-warning @+1 {{variable 'tmp' was never mutated}} + var tmp = s + // expected-note @+1 {{cannot differentiate access to property 'StructTangentPropertyNotStored.x' because 'StructTangentPropertyNotStored.TangentVector.x' is not a stored property}} + return tmp.x +} + +// CHECK-LABEL: sil {{.*}} @test_struct_tangent_property_not_stored +// CHECK: struct_element_addr {{%.*}} : $*StructTangentPropertyNotStored, #StructTangentPropertyNotStored.x + +final class ClassTangentPropertyNotStored: Differentiable { + var x: Float = 0 + + struct TangentVector: Differentiable, AdditiveArithmetic { + var x: Float { 0 } + } + func move(along direction: TangentVector) {} +} + +// expected-error @+2 {{function is not differentiable}} +// expected-note @+3 {{when differentiating this function definition}} +@differentiable +@_silgen_name("test_class_tangent_property_not_stored") +func testClassTangentPropertyNotStored(_ c: ClassTangentPropertyNotStored) -> Float { + // expected-warning @+1 {{variable 'tmp' was never mutated}} + var tmp = c + // expected-note @+1 {{cannot differentiate access to property 'ClassTangentPropertyNotStored.x' because 'ClassTangentPropertyNotStored.TangentVector.x' is not a stored property}} + return tmp.x +} + +// CHECK-LABEL: sil {{.*}} @test_class_tangent_property_not_stored +// CHECK: ref_element_addr {{%.*}} : $ClassTangentPropertyNotStored, #ClassTangentPropertyNotStored.x + //===----------------------------------------------------------------------===// // Wrapped property differentiation //===----------------------------------------------------------------------===// @@ -508,7 +680,7 @@ extension DifferentiableWrapper: Differentiable where Value: Differentiable {} struct Struct: Differentiable { // expected-error @+2 {{expression is not differentiable}} - // expected-note @+1 {{property cannot be differentiated because 'Struct.TangentVector' does not have a member named '_x'}} + // expected-note @+1 {{cannot differentiate access to property 'Struct._x' because 'Struct.TangentVector' does not have a stored property named '_x'}} @DifferentiableWrapper @DifferentiableWrapper var x: Float = 10 @Wrapper var y: Float = 20 diff --git a/test/AutoDiff/SILOptimizer/forward_mode_diagnostics.swift b/test/AutoDiff/SILOptimizer/forward_mode_diagnostics.swift index a2ab040928b92..f028db4baca4f 100644 --- a/test/AutoDiff/SILOptimizer/forward_mode_diagnostics.swift +++ b/test/AutoDiff/SILOptimizer/forward_mode_diagnostics.swift @@ -148,3 +148,173 @@ func testNoDerivativeParameter(_ f: @differentiable (Float, @noDerivative Float) return derivative(at: 2, 3) { (x, y) in f(x * x, y) } } */ + +//===----------------------------------------------------------------------===// +// Stored property access differentiation +//===----------------------------------------------------------------------===// + +// Test differentiation of invalid stored property access instructions: +// `struct_extract`, `struct_element_addr`, `ref_element_addr`. + +struct StructTangentVectorNotStruct: Differentiable { + var x: Float + + enum TangentVector: Differentiable, AdditiveArithmetic { + case x(Float) + typealias TangentVector = Self + static func ==(_: Self, _: Self) -> Bool { fatalError() } + static var zero: Self { fatalError() } + static func +(_: Self, _: Self) -> Self { fatalError() } + static func -(_: Self, _: Self) -> Self { fatalError() } + } + mutating func move(along direction: TangentVector) {} +} + +// expected-error @+2 {{function is not differentiable}} +// expected-note @+3 {{when differentiating this function definition}} +@differentiable +@_silgen_name("test_struct_tangent_vector_not_struct") +func testStructTangentVectorNotStruct(_ s: StructTangentVectorNotStruct) -> Float { + // expected-note @+1 {{cannot differentiate access to property 'StructTangentVectorNotStruct.x' because 'StructTangentVectorNotStruct.TangentVector' is not a struct}} + return s.x +} + +// CHECK-LABEL: sil {{.*}} @test_struct_tangent_vector_not_struct +// CHECK: struct_extract {{%.*}} : $StructTangentVectorNotStruct, #StructTangentVectorNotStruct.x + +struct StructOriginalPropertyNotDifferentiable: Differentiable { + struct Nondiff { + var x: Float + } + var nondiff: Nondiff + + struct TangentVector: Differentiable & AdditiveArithmetic { + var nondiff: Float + } + mutating func move(along direction: TangentVector) {} +} + +// expected-error @+2 {{function is not differentiable}} +// expected-note @+3 {{when differentiating this function definition}} +@differentiable +@_silgen_name("test_struct_original_property_not_differentiable") +func testStructOriginalPropertyNotDifferentiable(_ s: StructOriginalPropertyNotDifferentiable) -> Float { + // expected-note @+1 {{cannot differentiate access to property 'StructOriginalPropertyNotDifferentiable.nondiff' because property type 'StructOriginalPropertyNotDifferentiable.Nondiff' does not conform to 'Differentiable'}} + return s.nondiff.x +} + +// CHECK-LABEL: sil {{.*}} @test_struct_original_property_not_differentiable +// CHECK: struct_extract {{%.*}} : $StructOriginalPropertyNotDifferentiable, #StructOriginalPropertyNotDifferentiable.nondiff + +struct StructTangentVectorPropertyNotFound: Differentiable { + var x: Float + + struct TangentVector: Differentiable, AdditiveArithmetic { + var y: Float + } + mutating func move(along direction: TangentVector) {} +} + +// expected-error @+2 {{function is not differentiable}} +// expected-note @+3 {{when differentiating this function definition}} +@differentiable +@_silgen_name("test_struct_tangent_property_not_found") +func testStructTangentPropertyNotFound(_ s: StructTangentVectorPropertyNotFound) -> Float { + // expected-warning @+1 {{variable 'tmp' was never mutated}} + var tmp = s + // expected-note @+1 {{cannot differentiate access to property 'StructTangentVectorPropertyNotFound.x' because 'StructTangentVectorPropertyNotFound.TangentVector' does not have a stored property named 'x'}} + return tmp.x +} + +// CHECK-LABEL: sil {{.*}} @test_struct_tangent_property_not_found +// CHECK: struct_element_addr {{%.*}} : $*StructTangentVectorPropertyNotFound, #StructTangentVectorPropertyNotFound.x + +struct StructTangentPropertyWrongType: Differentiable { + var x: Float + + struct TangentVector: Differentiable, AdditiveArithmetic { + var x: Double + } + mutating func move(along direction: TangentVector) {} +} + +// expected-error @+2 {{function is not differentiable}} +// expected-note @+3 {{when differentiating this function definition}} +@differentiable +@_silgen_name("test_struct_tangent_property_wrong_type") +func testStructTangentPropertyWrongType(_ s: StructTangentPropertyWrongType) -> Float { + // expected-warning @+1 {{variable 'tmp' was never mutated}} + var tmp = s + // expected-note @+1 {{cannot differentiate access to property 'StructTangentPropertyWrongType.x' because 'StructTangentPropertyWrongType.TangentVector.x' does not have expected type 'Float.TangentVector' (aka 'Float')}} + return tmp.x +} + +// CHECK-LABEL: sil {{.*}} @test_struct_tangent_property_wrong_type +// CHECK: struct_element_addr {{%.*}} : $*StructTangentPropertyWrongType, #StructTangentPropertyWrongType.x + +final class ClassTangentPropertyWrongType: Differentiable { + var x: Float = 0 + + struct TangentVector: Differentiable, AdditiveArithmetic { + var x: Double + } + func move(along direction: TangentVector) {} +} + +// FIXME(TF-984): Forward-mode crash due to unset tangent buffer. +/* +@differentiable +@_silgen_name("test_class_tangent_property_wrong_type") +func testClassTangentPropertyWrongType(_ c: ClassTangentPropertyWrongType) -> Float { + var tmp = c + return tmp.x +} +*/ + +// CHECK-LABEL: sil {{.*}} @test_class_tangent_property_wrong_type +// CHECK: ref_element_addr {{%.*}} : $ClassTangentPropertyWrongType, #ClassTangentPropertyWrongType.x + +struct StructTangentPropertyNotStored: Differentiable { + var x: Float + + struct TangentVector: Differentiable, AdditiveArithmetic { + var x: Float { 0 } + } + mutating func move(along direction: TangentVector) {} +} + +// expected-error @+2 {{function is not differentiable}} +// expected-note @+3 {{when differentiating this function definition}} +@differentiable +@_silgen_name("test_struct_tangent_property_not_stored") +func testStructTangentPropertyNotStored(_ s: StructTangentPropertyNotStored) -> Float { + // expected-warning @+1 {{variable 'tmp' was never mutated}} + var tmp = s + // expected-note @+1 {{cannot differentiate access to property 'StructTangentPropertyNotStored.x' because 'StructTangentPropertyNotStored.TangentVector.x' is not a stored property}} + return tmp.x +} + +// CHECK-LABEL: sil {{.*}} @test_struct_tangent_property_not_stored +// CHECK: struct_element_addr {{%.*}} : $*StructTangentPropertyNotStored, #StructTangentPropertyNotStored.x + +final class ClassTangentPropertyNotStored: Differentiable { + var x: Float = 0 + + struct TangentVector: Differentiable, AdditiveArithmetic { + var x: Float { 0 } + } + func move(along direction: TangentVector) {} +} + +// FIXME(TF-984): Forward-mode crash due to unset tangent buffer. +/* +@differentiable +@_silgen_name("test_class_tangent_property_not_stored") +func testClassTangentPropertyNotStored(_ c: ClassTangentPropertyNotStored) -> Float { + var tmp = c + return tmp.x +} +*/ + +// CHECK-LABEL: sil {{.*}} @test_class_tangent_property_not_stored +// CHECK: ref_element_addr {{%.*}} : $ClassTangentPropertyNotStored, #ClassTangentPropertyNotStored.x diff --git a/test/DebugInfo/Imports.swift b/test/DebugInfo/Imports.swift index 5ba4bfe8eab22..0beaaf35e429f 100644 --- a/test/DebugInfo/Imports.swift +++ b/test/DebugInfo/Imports.swift @@ -8,7 +8,7 @@ // CHECK-DAG: ![[FOOMODULE:[0-9]+]] = !DIModule({{.*}}, name: "Foo", includePath: "{{.*}}test{{.*}}DebugInfo{{.*}}" // CHECK-DAG: !DIImportedEntity(tag: DW_TAG_imported_module, scope: ![[THISFILE:[0-9]+]], entity: ![[FOOMODULE]] // CHECK-DAG: ![[THISFILE]] = !DIFile(filename: "{{.*}}test{{/|\\\\}}DebugInfo{{/|\\\\}}Imports.swift", -// CHECK-DAG: ![[SWIFTFILE:[0-9]+]] = !DIFile(filename: "{{.*}}Swift.swiftmodule{{(/.+[.]swiftmodule)?}}" +// CHECK-DAG: ![[SWIFTFILE:[0-9]+]] = !DIFile(filename: "{{.*}}Swift.swiftmodule{{([/\\].+[.]swiftmodule)?}}" // CHECK-DAG: ![[SWIFTMODULE:[0-9]+]] = !DIModule({{.*}}, name: "Swift" // CHECK-DAG: !DIImportedEntity(tag: DW_TAG_imported_module, scope: ![[THISFILE]], entity: ![[SWIFTMODULE]] // CHECK-DAG: ![[BASICMODULE:[0-9]+]] = !DIModule({{.*}}, name: "basic" diff --git a/test/DebugInfo/variables.swift b/test/DebugInfo/variables.swift index 196802e6f8df5..502c91e04f18a 100644 --- a/test/DebugInfo/variables.swift +++ b/test/DebugInfo/variables.swift @@ -41,7 +41,7 @@ print(", \(glob_b)", terminator: "") print(", \(glob_s)", terminator: "") var unused: Int32 = -1 -// CHECK-DAG: ![[RT:[0-9]+]] ={{.*}}"{{.*}}Swift.swiftmodule{{(/.+[.]swiftmodule)?}}" +// CHECK-DAG: ![[RT:[0-9]+]] ={{.*}}"{{.*}}Swift.swiftmodule{{([/\\].+[.]swiftmodule)?}}" // Stack variables. diff --git a/test/SILOptimizer/allocbox_to_stack.sil b/test/SILOptimizer/allocbox_to_stack.sil index 991a9e848ec89..89cb9c85a2ec3 100644 --- a/test/SILOptimizer/allocbox_to_stack.sil +++ b/test/SILOptimizer/allocbox_to_stack.sil @@ -395,7 +395,7 @@ bb0(%0 : $Int): return %10 : $() // id: %11 } -// CHECK-LABEL: sil shared @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n +// CHECK-LABEL: sil private @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n // CHECK-LABEL: sil private @$s6struct8useStack1tySi_tFSiycfU_ // struct.(useStack (t : Swift.Int) -> ()).(closure #1) sil private @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int { @@ -513,8 +513,8 @@ bb0(%0 : $*T): return %9 : $() } -// CHECK-LABEL: sil shared @$s21closure_to_specializeTf0ns_n : $@convention(thin) (@inout_aliasable T) -> @out T -sil shared @closure_to_specialize : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> @out T { +// CHECK-LABEL: sil private @$s21closure_to_specializeTf0ns_n : $@convention(thin) (@inout_aliasable T) -> @out T +sil private @closure_to_specialize : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> @out T { // CHECK: bb0 bb0(%0 : $*T, %1 : $<τ_0_0> { var τ_0_0 } ): %2 = project_box %1 : $<τ_0_0> { var τ_0_0 } , 0 diff --git a/test/SILOptimizer/allocbox_to_stack_ownership.sil b/test/SILOptimizer/allocbox_to_stack_ownership.sil index b9e0028a21cb4..8c0027c042700 100644 --- a/test/SILOptimizer/allocbox_to_stack_ownership.sil +++ b/test/SILOptimizer/allocbox_to_stack_ownership.sil @@ -391,7 +391,7 @@ bb0(%0 : $Int): return %10 : $() // id: %11 } -// CHECK-LABEL: sil shared [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n +// CHECK-LABEL: sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_Tf0s_n // CHECK-LABEL: sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ // struct.(useStack (t : Swift.Int) -> ()).(closure #1) sil private [transparent] [ossa] @$s6struct8useStack1tySi_tFSiycfU_ : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } ) -> Int { diff --git a/test/SILOptimizer/allocboxtostack_localapply.sil b/test/SILOptimizer/allocboxtostack_localapply.sil new file mode 100644 index 0000000000000..733459137d665 --- /dev/null +++ b/test/SILOptimizer/allocboxtostack_localapply.sil @@ -0,0 +1,351 @@ +// RUN: %target-sil-opt %s -allocbox-to-stack -sil-deadfuncelim | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + + +sil hidden [noinline] [Onone] @$blackhole : $@convention(thin) (@in_guaranteed T) -> () { +bb0(%0 : $*T): + %2 = tuple () + return %2 : $() +} + +// CHECK-LABEL: sil [noinline] @$testapply : +// CHECK-NOT: alloc_box +// CHECK: [[STK:%.*]] = alloc_stack $Int64, var, name "x" +// CHECK-LABEL: } // end sil function '$testapply' +sil [noinline] @$testapply : $@convention(thin) () -> () { +bb0: + %0 = alloc_box ${ var Int64 }, var, name "x" + %1 = project_box %0 : ${ var Int64 }, 0 + %2 = integer_literal $Builtin.Int64, 0 + %3 = struct $Int64 (%2 : $Builtin.Int64) + store %3 to %1 : $*Int64 + %5 = function_ref @$testapplybas : $@convention(thin) (@guaranteed { var Int64 }) -> () + %6 = apply %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + strong_release %0 : ${ var Int64 } + %8 = tuple () + return %8 : $() +} + +sil private [noinline] @$testapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = begin_access [read] [dynamic] %1 : $*Int64 + %4 = load %3 : $*Int64 + end_access %3 : $*Int64 + %6 = alloc_stack $Int64 + store %4 to %6 : $*Int64 + %8 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %9 = apply %8(%6) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %6 : $*Int64 + %11 = tuple () + return %11 : $() +} + +sil private [noinline] @$testapplybas : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> () + %4 = apply %3(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %5 = tuple () + return %5 : $() +} + +// CHECK-LABEL: sil [noinline] @$testtryapply : +// CHECK-NOT: alloc_box +// CHECK: [[STK:%.*]] = alloc_stack $Int64, var, name "x" +// CHECK-LABEL: } // end sil function '$testtryapply' +sil [noinline] @$testtryapply : $@convention(thin) () -> @error Error { +bb0: + %1 = alloc_box ${ var Int64 }, var, name "x" + %2 = project_box %1 : ${ var Int64 }, 0 + %3 = integer_literal $Builtin.Int64, 0 + %4 = struct $Int64 (%3 : $Builtin.Int64) + store %4 to %2 : $*Int64 + %6 = function_ref @$testtryapplybas : $@convention(thin) (@guaranteed { var Int64 }) -> @error Error + try_apply %6(%1) : $@convention(thin) (@guaranteed { var Int64 }) -> @error Error, normal bb1, error bb2 +bb1(%8 : $()): + strong_release %1 : ${ var Int64 } + %10 = tuple () + return %10 : $() +bb2(%12 : $Error): + strong_release %1 : ${ var Int64 } + throw %12 : $Error +} + +sil private [noinline] @$testtryapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> @error Error { +bb0(%0 : ${ var Int64 }): + %2 = project_box %0 : ${ var Int64 }, 0 + %4 = begin_access [read] [dynamic] %2 : $*Int64 + %5 = load %4 : $*Int64 + end_access %4 : $*Int64 + %7 = alloc_stack $Int64 + store %5 to %7 : $*Int64 + %9 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %10 = apply %9(%7) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %7 : $*Int64 + %12 = tuple () + return %12 : $() +} + +sil private [noinline] @$testtryapplybas : $@convention(thin) (@guaranteed { var Int64 }) -> @error Error { +bb0(%0 : ${ var Int64 }): + %2 = project_box %0 : ${ var Int64 }, 0 + %4 = function_ref @$testtryapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> @error Error + try_apply %4(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> @error Error, normal bb1, error bb2 +bb1(%6 : $()): + %7 = tuple () + return %7 : $() +bb2(%9 : $Error): + throw %9 : $Error +} + + +// CHECK-LABEL: sil [noinline] @$testpartialapply : +// CHECK-NOT: alloc_box +// CHECK: [[STK:%.*]] = alloc_stack $Int64, var, name "x" +// CHECK-LABEL: } // end sil function '$testpartialapply' +sil [noinline] @$testpartialapply : $@convention(thin) () -> () { +bb0: + %0 = alloc_box ${ var Int64 }, var, name "x" + %1 = project_box %0 : ${ var Int64 }, 0 + %2 = integer_literal $Builtin.Int64, 0 + %3 = struct $Int64 (%2 : $Builtin.Int64) + store %3 to %1 : $*Int64 + %5 = function_ref @$testpartialapplyclosure : $@convention(thin) (@guaranteed { var Int64 }) -> () + %6 = apply %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + strong_release %0 : ${ var Int64 } + %8 = tuple () + return %8 : $() +} + +sil private [noinline] @$testpartialapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = begin_access [read] [dynamic] %1 : $*Int64 + %4 = load %3 : $*Int64 + end_access %3 : $*Int64 + %6 = alloc_stack $Int64 + store %4 to %6 : $*Int64 + %8 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %9 = apply %8(%6) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %6 : $*Int64 + %11 = tuple () + return %11 : $() +} + +sil private [noinline] @$testpartialapplybas : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testpartialapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> () + %4 = apply %3(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %5 = tuple () + return %5 : $() +} + +sil private @$testpartialapplyclosure : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testpartialapplybas : $@convention(thin) (@guaranteed { var Int64 }) -> () + %4 = apply %3(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %5 = tuple () + return %5 : $() +} + +// CHECK-LABEL: sil [noinline] @$testtwoboxes : +// CHECK-NOT: alloc_box +// CHECK: [[STK1:%.*]] = alloc_stack $Int64, var, name "x" +// CHECK: [[STK2:%.*]] = alloc_stack $Int64, var, name "y" +// CHECK-LABEL: } // end sil function '$testtwoboxes' +sil [noinline] @$testtwoboxes : $@convention(thin) () -> () { +bb0: + %0 = alloc_box ${ var Int64 }, var, name "x" + %1 = project_box %0 : ${ var Int64 }, 0 + %2 = integer_literal $Builtin.Int64, 0 + %3 = struct $Int64 (%2 : $Builtin.Int64) + store %3 to %1 : $*Int64 + %5 = alloc_box ${ var Int64 }, var, name "y" + %6 = project_box %5 : ${ var Int64 }, 0 + %7 = integer_literal $Builtin.Int64, 0 + %8 = struct $Int64 (%7 : $Builtin.Int64) + store %8 to %6 : $*Int64 + %10 = function_ref @$testtwoboxesbas : $@convention(thin) (@guaranteed { var Int64 }, @guaranteed { var Int64 }) -> () + %11 = apply %10(%0, %5) : $@convention(thin) (@guaranteed { var Int64 }, @guaranteed { var Int64 }) -> () + strong_release %5 : ${ var Int64 } + strong_release %0 : ${ var Int64 } + %14 = tuple () + return %14 : $() +} + +sil private [noinline] @$testtwoboxesbar : $@convention(thin) (@guaranteed { var Int64 }, @guaranteed { var Int64 }) -> () { +bb0(%0 : ${ var Int64 }, %1 : ${ var Int64 }): + %2 = project_box %0 : ${ var Int64 }, 0 + %4 = project_box %1 : ${ var Int64 }, 0 + %6 = begin_access [read] [dynamic] %2 : $*Int64 + %7 = load %6 : $*Int64 + end_access %6 : $*Int64 + %9 = alloc_stack $Int64 + store %7 to %9 : $*Int64 + %11 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %12 = apply %11(%9) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %9 : $*Int64 + %14 = begin_access [read] [dynamic] %4 : $*Int64 + %15 = load %14 : $*Int64 + end_access %14 : $*Int64 + %17 = alloc_stack $Int64 + store %15 to %17 : $*Int64 + %19 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %20 = apply %19(%17) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %17 : $*Int64 + %22 = tuple () + return %22 : $() +} + +sil private [noinline] @$testtwoboxesbas : $@convention(thin) (@guaranteed { var Int64 }, @guaranteed { var Int64 }) -> () { +bb0(%0 : ${ var Int64 }, %1 : ${ var Int64 }): + %2 = project_box %0 : ${ var Int64 }, 0 + %4 = project_box %1 : ${ var Int64 }, 0 + %6 = function_ref @$testtwoboxesbar : $@convention(thin) (@guaranteed { var Int64 }, @guaranteed { var Int64 }) -> () + %7 = apply %6(%0, %1) : $@convention(thin) (@guaranteed { var Int64 }, @guaranteed { var Int64 }) -> () + %8 = tuple () + return %8 : $() +} + +// CHECK-LABEL: sil [noinline] @$testboxescapes : +// CHECK: alloc_box ${ var Int64 }, var, name "x" +// CHECK-LABEL: } // end sil function '$testboxescapes' +sil [noinline] @$testboxescapes : $@convention(thin) () -> @owned @callee_guaranteed () -> () { +bb0: + %0 = alloc_box ${ var Int64 }, var, name "x" + %1 = project_box %0 : ${ var Int64 }, 0 + %2 = integer_literal $Builtin.Int64, 0 + %3 = struct $Int64 (%2 : $Builtin.Int64) + store %3 to %1 : $*Int64 + %5 = function_ref @$testboxescapesbas : $@convention(thin) (@guaranteed { var Int64 }) -> @owned @callee_guaranteed () -> () + %6 = apply %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> @owned @callee_guaranteed () -> () + strong_release %0 : ${ var Int64 } + return %6 : $@callee_guaranteed () -> () +} + +sil private [noinline] @$testboxescapesbar : $@convention(thin) (@guaranteed { var Int64 }) -> @owned @callee_guaranteed () -> () { +bb0(%0 : ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testboxescapesclosure : $@convention(thin) (@guaranteed { var Int64 }) -> () + strong_retain %0 : ${ var Int64 } + %5 = partial_apply [callee_guaranteed] %3(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + return %5 : $@callee_guaranteed () -> () +} + +sil private @$testboxescapesclosure : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = load %1 : $*Int64 + %4 = tuple () + return %4 : $() +} + +sil private [noinline] @$testboxescapesbas : $@convention(thin) (@guaranteed { var Int64 }) -> @owned @callee_guaranteed () -> () { +bb0(%0 : ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testboxescapesbar : $@convention(thin) (@guaranteed { var Int64 }) -> @owned @callee_guaranteed () -> () + %4 = apply %3(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> @owned @callee_guaranteed () -> () + return %4 : $@callee_guaranteed () -> () +} + +// CHECK-LABEL: sil [noinline] @$testrecur : +// CHECK: alloc_box ${ var Int64 }, var, name "x" +// CHECK-LABEL: } // end sil function '$testrecur' +sil [noinline] @$testrecur : $@convention(thin) () -> () { +bb0: + %0 = alloc_box ${ var Int64 }, var, name "x" + %1 = project_box %0 : ${ var Int64 }, 0 + %2 = integer_literal $Builtin.Int64, 0 + %3 = struct $Int64 (%2 : $Builtin.Int64) + store %3 to %1 : $*Int64 + %5 = function_ref @$testrecurbas : $@convention(thin) (@guaranteed { var Int64 }) -> () + %6 = apply %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %7 = function_ref @$testrecurbar : $@convention(thin) (@guaranteed { var Int64 }) -> () + %8 = apply %7(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + strong_release %0 : ${ var Int64 } + %10 = tuple () + return %10 : $() +} + +sil private [noinline] @$testrecurbar : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = begin_access [read] [dynamic] %1 : $*Int64 + %4 = load %3 : $*Int64 + end_access %3 : $*Int64 + %6 = alloc_stack $Int64 + store %4 to %6 : $*Int64 + %8 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %9 = apply %8(%6) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %6 : $*Int64 + %11 = function_ref @$testrecurbas : $@convention(thin) (@guaranteed { var Int64 }) -> () + %12 = apply %11(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %13 = tuple () + return %13 : $() +} + +sil private [noinline] @$testrecurbas : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testrecurbar : $@convention(thin) (@guaranteed { var Int64 }) -> () + %4 = apply %3(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %5 = tuple () + return %5 : $() +} + +// CHECK-LABEL: sil [noinline] @$testbeginapply : +// CHECK-NOT: alloc_box +// CHECK: [[STK:%.*]] = alloc_stack $Int64, var, name "x" +// CHECK-LABEL: } // end sil function '$testbeginapply' +sil [noinline] @$testbeginapply : $@convention(thin) () -> () { +bb0: + %0 = alloc_box ${ var Int64 }, var, name "x" + %1 = project_box %0 : ${ var Int64 }, 0 + %2 = integer_literal $Builtin.Int64, 0 + %3 = struct $Int64 (%2 : $Builtin.Int64) + store %3 to %1 : $*Int64 + %5 = function_ref @$testbeginapplybas : $@yield_once @convention(thin) (@guaranteed { var Int64 }) -> @yields () + (%addr, %token) = begin_apply %5(%0) : $@yield_once @convention(thin) (@guaranteed { var Int64 }) -> @yields () + end_apply %token + strong_release %0 : ${ var Int64 } + %8 = tuple () + return %8 : $() +} + +sil private [noinline] @$testbeginapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = begin_access [read] [dynamic] %1 : $*Int64 + %4 = load %3 : $*Int64 + end_access %3 : $*Int64 + %6 = alloc_stack $Int64 + store %4 to %6 : $*Int64 + %8 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %9 = apply %8(%6) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %6 : $*Int64 + %11 = tuple () + return %11 : $() +} + +sil private [noinline] @$testbeginapplybas : $@yield_once @convention(thin) (@guaranteed { var Int64 }) -> @yields () { +bb0(%0 : ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testbeginapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> () + %4 = apply %3(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %5 = tuple () + yield %5 : $(), resume bb1, unwind bb2 +bb1: + %rv = tuple() + return %rv : $() +bb2: + unwind +} + diff --git a/test/SILOptimizer/allocboxtostack_localapply.swift b/test/SILOptimizer/allocboxtostack_localapply.swift new file mode 100644 index 0000000000000..c9c73539ae6b4 --- /dev/null +++ b/test/SILOptimizer/allocboxtostack_localapply.swift @@ -0,0 +1,120 @@ +// RUN: %target-swift-frontend -emit-sil %s -O | %FileCheck %s + + +@_optimize(none) +@inline(never) +func blackhole(_ x:T) { +} + +// CHECK-LABEL: sil [noinline] @$s26allocboxtostack_localapply9testapplySiyF : +// CHECK-NOT: alloc_box +// CHECK: [[STK:%.*]] = alloc_stack $Int, var, name "x" +// CHECK-LABEL: } // end sil function '$s26allocboxtostack_localapply9testapplySiyF' +@inline(never) +public func testapply() -> Int { + var x = 0 + @inline(never) + func bar() -> Int { + blackhole(x) + return x + 1 + } + @inline(never) + func bas() -> Int { + return bar() + } + return bas() +} + +// CHECK-LABEL: sil [noinline] @$s26allocboxtostack_localapply12testtryapplySiyKF : +// CHECK-NOT: alloc_box +// CHECK: [[STK:%.*]] = alloc_stack $Int, var, name "x" +// CHECK-LABEL: } // end sil function '$s26allocboxtostack_localapply12testtryapplySiyKF' +@inline(never) +public func testtryapply() throws -> Int { + var x = 0 + @inline(never) + func bar() throws -> Int { + blackhole(x) + return x + 1 + } + @inline(never) + func bas() throws -> Int { + return try bar() + } + let res = try bas() + return res +} + +// CHECK-LABEL: sil [noinline] @$s26allocboxtostack_localapply16testpartialapplySiyF : +// CHECK-NOT: alloc_box +// CHECK: [[STK:%.*]] = alloc_stack $Int, var, name "x" +// CHECK-LABEL: } // end sil function '$s26allocboxtostack_localapply16testpartialapplySiyF' +@inline(never) +public func testpartialapply() -> Int { + var x = 0 + @inline(never) + func bar() -> Int { + blackhole(x) + return x + 1 + } + @inline(never) + func bas() -> Int { + return bar() + } + return {bas()}() +} + +// CHECK-LABEL: sil [noinline] @$s26allocboxtostack_localapply12testtwoboxesSiyF : +// CHECK-NOT: alloc_box +// CHECK: [[STK1:%.*]] = alloc_stack $Int, var, name "x" +// CHECK: [[STK2:%.*]] = alloc_stack $Int, var, name "y" +// CHECK-LABEL: } // end sil function '$s26allocboxtostack_localapply12testtwoboxesSiyF' +@inline(never) +public func testtwoboxes() -> Int { + var x = 0 + var y = 0 + @inline(never) + func bar() -> Int { + blackhole(x) + return x + y + } + @inline(never) + func bas() -> Int { + return bar() + } + return bas() +} + +// CHECK-LABEL: sil [noinline] @$s26allocboxtostack_localapply14testboxescapesyycyF : +// CHECK: alloc_box ${ var Int }, var, name "x" +// CHECK-LABEL: } // end sil function '$s26allocboxtostack_localapply14testboxescapesyycyF' +@inline(never) +public func testboxescapes() -> (() -> ()) { + var x = 0 + @inline(never) + func bar() -> (() -> ()) { + return {x + 1} + } + @inline(never) + func bas() -> (() -> ()) { + return bar() + } + return bas() +} + +// CHECK-LABEL: sil [noinline] @$s26allocboxtostack_localapply9testrecurSiyF : +// CHECK: alloc_box ${ var Int }, var, name "x" +// CHECK-LABEL: } // end sil function '$s26allocboxtostack_localapply9testrecurSiyF' +@inline(never) +public func testrecur() -> Int { + var x = 0 + @inline(never) + func bar() -> Int { + return x + bas() + } + @inline(never) + func bas() -> Int { + return bar() + } + return bas() + bar() +} diff --git a/test/SILOptimizer/allocboxtostack_localapply_ossa.sil b/test/SILOptimizer/allocboxtostack_localapply_ossa.sil new file mode 100644 index 0000000000000..6807349240adc --- /dev/null +++ b/test/SILOptimizer/allocboxtostack_localapply_ossa.sil @@ -0,0 +1,351 @@ +// RUN: %target-sil-opt %s -allocbox-to-stack -sil-deadfuncelim | %FileCheck %s + +sil_stage canonical + +import Builtin +import Swift +import SwiftShims + + +sil hidden [noinline] [ossa] [Onone] @$blackhole : $@convention(thin) (@in_guaranteed T) -> () { +bb0(%0 : $*T): + %2 = tuple () + return %2 : $() +} + +// CHECK-LABEL: sil [noinline] [ossa] @$testapply : +// CHECK-NOT: alloc_box +// CHECK: [[STK:%.*]] = alloc_stack $Int64, var, name "x" +// CHECK-LABEL: } // end sil function '$testapply' +sil [noinline] [ossa] @$testapply : $@convention(thin) () -> () { +bb0: + %0 = alloc_box ${ var Int64 }, var, name "x" + %1 = project_box %0 : ${ var Int64 }, 0 + %2 = integer_literal $Builtin.Int64, 0 + %3 = struct $Int64 (%2 : $Builtin.Int64) + store %3 to [trivial] %1 : $*Int64 + %5 = function_ref @$testapplybas : $@convention(thin) (@guaranteed { var Int64 }) -> () + %6 = apply %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + destroy_value %0 : ${ var Int64 } + %8 = tuple () + return %8 : $() +} + +sil private [noinline] [ossa] @$testapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : @guaranteed ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = begin_access [read] [dynamic] %1 : $*Int64 + %4 = load [trivial] %3 : $*Int64 + end_access %3 : $*Int64 + %6 = alloc_stack $Int64 + store %4 to [trivial] %6 : $*Int64 + %8 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %9 = apply %8(%6) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %6 : $*Int64 + %11 = tuple () + return %11 : $() +} + +sil private [noinline] [ossa] @$testapplybas : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : @guaranteed ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> () + %4 = apply %3(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %5 = tuple () + return %5 : $() +} + +// CHECK-LABEL: sil [noinline] [ossa] @$testtryapply : +// CHECK-NOT: alloc_box +// CHECK: [[STK:%.*]] = alloc_stack $Int64, var, name "x" +// CHECK-LABEL: } // end sil function '$testtryapply' +sil [noinline] [ossa] @$testtryapply : $@convention(thin) () -> @error Error { +bb0: + %1 = alloc_box ${ var Int64 }, var, name "x" + %2 = project_box %1 : ${ var Int64 }, 0 + %3 = integer_literal $Builtin.Int64, 0 + %4 = struct $Int64 (%3 : $Builtin.Int64) + store %4 to [trivial] %2 : $*Int64 + %6 = function_ref @$testtryapplybas : $@convention(thin) (@guaranteed { var Int64 }) -> @error Error + try_apply %6(%1) : $@convention(thin) (@guaranteed { var Int64 }) -> @error Error, normal bb1, error bb2 +bb1(%8 : $()): + destroy_value %1 : ${ var Int64 } + %10 = tuple () + return %10 : $() +bb2(%12 : $Error): + destroy_value %1 : ${ var Int64 } + throw %12 : $Error +} + +sil private [noinline] [ossa] @$testtryapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> @error Error { +bb0(%0 : @guaranteed ${ var Int64 }): + %2 = project_box %0 : ${ var Int64 }, 0 + %4 = begin_access [read] [dynamic] %2 : $*Int64 + %5 = load [trivial] %4 : $*Int64 + end_access %4 : $*Int64 + %7 = alloc_stack $Int64 + store %5 to [trivial] %7 : $*Int64 + %9 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %10 = apply %9(%7) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %7 : $*Int64 + %12 = tuple () + return %12 : $() +} + +sil private [noinline] [ossa] @$testtryapplybas : $@convention(thin) (@guaranteed { var Int64 }) -> @error Error { +bb0(%0 : @guaranteed ${ var Int64 }): + %2 = project_box %0 : ${ var Int64 }, 0 + %4 = function_ref @$testtryapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> @error Error + try_apply %4(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> @error Error, normal bb1, error bb2 +bb1(%6 : $()): + %7 = tuple () + return %7 : $() +bb2(%9 : $Error): + throw %9 : $Error +} + + +// CHECK-LABEL: sil [noinline] [ossa] @$testpartialapply : +// CHECK-NOT: alloc_box +// CHECK: [[STK:%.*]] = alloc_stack $Int64, var, name "x" +// CHECK-LABEL: } // end sil function '$testpartialapply' +sil [noinline] [ossa] @$testpartialapply : $@convention(thin) () -> () { +bb0: + %0 = alloc_box ${ var Int64 }, var, name "x" + %1 = project_box %0 : ${ var Int64 }, 0 + %2 = integer_literal $Builtin.Int64, 0 + %3 = struct $Int64 (%2 : $Builtin.Int64) + store %3 to [trivial] %1 : $*Int64 + %5 = function_ref @$testpartialapplyclosure : $@convention(thin) (@guaranteed { var Int64 }) -> () + %6 = apply %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + destroy_value %0 : ${ var Int64 } + %8 = tuple () + return %8 : $() +} + +sil private [noinline] [ossa] @$testpartialapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : @guaranteed ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = begin_access [read] [dynamic] %1 : $*Int64 + %4 = load [trivial] %3 : $*Int64 + end_access %3 : $*Int64 + %6 = alloc_stack $Int64 + store %4 to [trivial] %6 : $*Int64 + %8 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %9 = apply %8(%6) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %6 : $*Int64 + %11 = tuple () + return %11 : $() +} + +sil private [noinline] [ossa] @$testpartialapplybas : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : @guaranteed ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testpartialapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> () + %4 = apply %3(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %5 = tuple () + return %5 : $() +} + +sil private [ossa] @$testpartialapplyclosure : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : @guaranteed ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testpartialapplybas : $@convention(thin) (@guaranteed { var Int64 }) -> () + %4 = apply %3(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %5 = tuple () + return %5 : $() +} + +// CHECK-LABEL: sil [noinline] [ossa] @$testtwoboxes : +// CHECK-NOT: alloc_box +// CHECK: [[STK1:%.*]] = alloc_stack $Int64, var, name "x" +// CHECK: [[STK2:%.*]] = alloc_stack $Int64, var, name "y" +// CHECK-LABEL: } // end sil function '$testtwoboxes' +sil [noinline] [ossa] @$testtwoboxes : $@convention(thin) () -> () { +bb0: + %0 = alloc_box ${ var Int64 }, var, name "x" + %1 = project_box %0 : ${ var Int64 }, 0 + %2 = integer_literal $Builtin.Int64, 0 + %3 = struct $Int64 (%2 : $Builtin.Int64) + store %3 to [trivial] %1 : $*Int64 + %5 = alloc_box ${ var Int64 }, var, name "y" + %6 = project_box %5 : ${ var Int64 }, 0 + %7 = integer_literal $Builtin.Int64, 0 + %8 = struct $Int64 (%7 : $Builtin.Int64) + store %8 to [trivial] %6 : $*Int64 + %10 = function_ref @$testtwoboxesbas : $@convention(thin) (@guaranteed { var Int64 }, @guaranteed { var Int64 }) -> () + %11 = apply %10(%0, %5) : $@convention(thin) (@guaranteed { var Int64 }, @guaranteed { var Int64 }) -> () + destroy_value %5 : ${ var Int64 } + destroy_value %0 : ${ var Int64 } + %14 = tuple () + return %14 : $() +} + +sil private [noinline] [ossa] @$testtwoboxesbar : $@convention(thin) (@guaranteed { var Int64 }, @guaranteed { var Int64 }) -> () { +bb0(%0 : @guaranteed ${ var Int64 }, %1 : @guaranteed ${ var Int64 }): + %2 = project_box %0 : ${ var Int64 }, 0 + %4 = project_box %1 : ${ var Int64 }, 0 + %6 = begin_access [read] [dynamic] %2 : $*Int64 + %7 = load [trivial] %6 : $*Int64 + end_access %6 : $*Int64 + %9 = alloc_stack $Int64 + store %7 to [trivial] %9 : $*Int64 + %11 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %12 = apply %11(%9) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %9 : $*Int64 + %14 = begin_access [read] [dynamic] %4 : $*Int64 + %15 = load [trivial] %14 : $*Int64 + end_access %14 : $*Int64 + %17 = alloc_stack $Int64 + store %15 to [trivial] %17 : $*Int64 + %19 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %20 = apply %19(%17) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %17 : $*Int64 + %22 = tuple () + return %22 : $() +} + +sil private [noinline] [ossa] @$testtwoboxesbas : $@convention(thin) (@guaranteed { var Int64 }, @guaranteed { var Int64 }) -> () { +bb0(%0 : @guaranteed ${ var Int64 }, %1 : @guaranteed ${ var Int64 }): + %2 = project_box %0 : ${ var Int64 }, 0 + %4 = project_box %1 : ${ var Int64 }, 0 + %6 = function_ref @$testtwoboxesbar : $@convention(thin) (@guaranteed { var Int64 }, @guaranteed { var Int64 }) -> () + %7 = apply %6(%0, %1) : $@convention(thin) (@guaranteed { var Int64 }, @guaranteed { var Int64 }) -> () + %8 = tuple () + return %8 : $() +} + +// CHECK-LABEL: sil [noinline] [ossa] @$testboxescapes : +// CHECK: alloc_box ${ var Int64 }, var, name "x" +// CHECK-LABEL: } // end sil function '$testboxescapes' +sil [noinline] [ossa] @$testboxescapes : $@convention(thin) () -> @owned @callee_guaranteed () -> () { +bb0: + %0 = alloc_box ${ var Int64 }, var, name "x" + %1 = project_box %0 : ${ var Int64 }, 0 + %2 = integer_literal $Builtin.Int64, 0 + %3 = struct $Int64 (%2 : $Builtin.Int64) + store %3 to [trivial] %1 : $*Int64 + %5 = function_ref @$testboxescapesbas : $@convention(thin) (@guaranteed { var Int64 }) -> @owned @callee_guaranteed () -> () + %6 = apply %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> @owned @callee_guaranteed () -> () + destroy_value %0 : ${ var Int64 } + return %6 : $@callee_guaranteed () -> () +} + +sil private [noinline] [ossa] @$testboxescapesbar : $@convention(thin) (@guaranteed { var Int64 }) -> @owned @callee_guaranteed () -> () { +bb0(%0 : @guaranteed ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testboxescapesclosure : $@convention(thin) (@guaranteed { var Int64 }) -> () + %copy = copy_value %0 : ${ var Int64 } + %5 = partial_apply [callee_guaranteed] %3(%copy) : $@convention(thin) (@guaranteed { var Int64 }) -> () + return %5 : $@callee_guaranteed () -> () +} + +sil private [ossa] @$testboxescapesclosure : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : @guaranteed ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = load [trivial] %1 : $*Int64 + %4 = tuple () + return %4 : $() +} + +sil private [noinline] [ossa] @$testboxescapesbas : $@convention(thin) (@guaranteed { var Int64 }) -> @owned @callee_guaranteed () -> () { +bb0(%0 : @guaranteed ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testboxescapesbar : $@convention(thin) (@guaranteed { var Int64 }) -> @owned @callee_guaranteed () -> () + %4 = apply %3(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> @owned @callee_guaranteed () -> () + return %4 : $@callee_guaranteed () -> () +} + +// CHECK-LABEL: sil [noinline] [ossa] @$testrecur : +// CHECK: alloc_box ${ var Int64 }, var, name "x" +// CHECK-LABEL: } // end sil function '$testrecur' +sil [noinline] [ossa] @$testrecur : $@convention(thin) () -> () { +bb0: + %0 = alloc_box ${ var Int64 }, var, name "x" + %1 = project_box %0 : ${ var Int64 }, 0 + %2 = integer_literal $Builtin.Int64, 0 + %3 = struct $Int64 (%2 : $Builtin.Int64) + store %3 to [trivial] %1 : $*Int64 + %5 = function_ref @$testrecurbas : $@convention(thin) (@guaranteed { var Int64 }) -> () + %6 = apply %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %7 = function_ref @$testrecurbar : $@convention(thin) (@guaranteed { var Int64 }) -> () + %8 = apply %7(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + destroy_value %0 : ${ var Int64 } + %10 = tuple () + return %10 : $() +} + +sil private [noinline] [ossa] @$testrecurbar : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : @guaranteed ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = begin_access [read] [dynamic] %1 : $*Int64 + %4 = load [trivial] %3 : $*Int64 + end_access %3 : $*Int64 + %6 = alloc_stack $Int64 + store %4 to [trivial] %6 : $*Int64 + %8 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %9 = apply %8(%6) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %6 : $*Int64 + %11 = function_ref @$testrecurbas : $@convention(thin) (@guaranteed { var Int64 }) -> () + %12 = apply %11(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %13 = tuple () + return %13 : $() +} + +sil private [noinline] [ossa] @$testrecurbas : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : @guaranteed ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testrecurbar : $@convention(thin) (@guaranteed { var Int64 }) -> () + %4 = apply %3(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %5 = tuple () + return %5 : $() +} + +// CHECK-LABEL: sil [noinline] [ossa] @$testbeginapply : +// CHECK-NOT: alloc_box +// CHECK: [[STK:%.*]] = alloc_stack $Int64, var, name "x" +// CHECK-LABEL: } // end sil function '$testbeginapply' +sil [noinline] [ossa] @$testbeginapply : $@convention(thin) () -> () { +bb0: + %0 = alloc_box ${ var Int64 }, var, name "x" + %1 = project_box %0 : ${ var Int64 }, 0 + %2 = integer_literal $Builtin.Int64, 0 + %3 = struct $Int64 (%2 : $Builtin.Int64) + store %3 to [trivial] %1 : $*Int64 + %5 = function_ref @$testbeginapplybas : $@yield_once @convention(thin) (@guaranteed { var Int64 }) -> @yields () + (%addr, %token) = begin_apply %5(%0) : $@yield_once @convention(thin) (@guaranteed { var Int64 }) -> @yields () + end_apply %token + destroy_value %0 : ${ var Int64 } + %8 = tuple () + return %8 : $() +} + +sil private [noinline] [ossa] @$testbeginapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> () { +bb0(%0 : @guaranteed ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = begin_access [read] [dynamic] %1 : $*Int64 + %4 = load [trivial] %3 : $*Int64 + end_access %3 : $*Int64 + %6 = alloc_stack $Int64 + store %4 to [trivial] %6 : $*Int64 + %8 = function_ref @$blackhole : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + %9 = apply %8(%6) : $@convention(thin) <τ_0_0> (@in_guaranteed τ_0_0) -> () + dealloc_stack %6 : $*Int64 + %11 = tuple () + return %11 : $() +} + +sil private [noinline] [ossa] @$testbeginapplybas : $@yield_once @convention(thin) (@guaranteed { var Int64 }) -> @yields () { +bb0(%0 : @guaranteed ${ var Int64 }): + %1 = project_box %0 : ${ var Int64 }, 0 + %3 = function_ref @$testbeginapplybar : $@convention(thin) (@guaranteed { var Int64 }) -> () + %4 = apply %3(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> () + %5 = tuple () + yield %5 : $(), resume bb1, unwind bb2 +bb1: + %rv = tuple() + return %rv : $() +bb2: + unwind +} +