diff --git a/CMakeLists.txt b/CMakeLists.txt index 54597829e7218..ee70c474647bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -646,6 +646,11 @@ option(SWIFT_THREADING_PACKAGE Valid package names are 'pthreads', 'darwin', 'linux', 'win32', 'c11', 'none' or the empty string for the SDK default.") +option(SWIFT_THREADING_HAS_DLSYM + "Enable the use of the dlsym() function. This gets used to provide TSan + support on some platforms." + TRUE) + option(SWIFT_ENABLE_MACCATALYST "Build the Standard Library and overlays with MacCatalyst support" FALSE) diff --git a/cmake/modules/AddSwiftUnittests.cmake b/cmake/modules/AddSwiftUnittests.cmake index e7a98367337e9..a4573981c225c 100644 --- a/cmake/modules/AddSwiftUnittests.cmake +++ b/cmake/modules/AddSwiftUnittests.cmake @@ -49,12 +49,19 @@ function(add_swift_unittest test_dirname) endif() # some headers switch their inline implementations based on - # SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY and + # SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY, SWIFT_STDLIB_HAS_DLSYM and # SWIFT_THREADING_PACKAGE definitions if(SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY) target_compile_definitions("${test_dirname}" PRIVATE SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY) endif() + if(SWIFT_STDLIB_HAS_DLSYM) + target_compile_definitions("${test_dirname}" PRIVATE + "SWIFT_STDLIB_HAS_DLSYM=1") + else() + target_compile_definitions("${test_dirname}" PRIVATE + "SWIFT_STDLIB_HAS_DLSYM=0") + endif() string(TOUPPER "${SWIFT_SDK_${SWIFT_HOST_VARIANT_SDK}_THREADING_PACKAGE}" _threading_package) target_compile_definitions("${test_dirname}" PRIVATE diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 556ee0f456948..138ae8605f63a 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3995,6 +3995,10 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// this type. ArrayRef getInitAccessorProperties() const; + /// Return a collection of all properties that will be part of the memberwise + /// initializer. + ArrayRef getMemberwiseInitProperties() const; + /// Establish a mapping between properties that could be iniitalized /// via other properties by means of init accessors. This mapping is /// one-to-many because we allow intersecting `initializes(...)`. diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index 889ba60edf92a..bf6f4401c0149 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -850,9 +850,23 @@ class IterableDeclContext { HasNestedClassDeclarations = 1; } - /// Retrieve the set of members in this context. + /// Retrieve the current set of members in this context. + /// + /// NOTE: This operation is an alias of \c getCurrentMembers() that is considered + /// deprecated. Most clients should not use this or \c getCurrentMembers(), but + /// instead should use one of the projections of members that provides a semantic + /// view, e.g., \c getParsedMembers(), \c getABIMembers(), \c getAllMembers(), and so + /// on. DeclRange getMembers() const; + /// Retrieve the current set of members in this context, without triggering the + /// creation of new members via code synthesis, macro expansion, etc. + /// + /// This operation should only be used in narrow places where any side-effect + /// producing operations have been done earlier. For the most part, this means that + /// it should only be used in the implementation of + DeclRange getCurrentMembers() const; + /// Get the members that were syntactically present in the source code, /// and will not contain any members that are implicitly synthesized by /// the implementation. diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 80fcb212739e1..0a7ad45e021ba 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -1693,6 +1693,28 @@ class InitAccessorPropertiesRequest : bool isCached() const { return true; } }; +/// Request to obtain a list of properties that will be reflected in the parameters of a +/// memberwise initializer. +class MemberwiseInitPropertiesRequest : + public SimpleRequest(NominalTypeDecl *), + RequestFlags::Cached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + ArrayRef + evaluate(Evaluator &evaluator, NominalTypeDecl *decl) const; + + // Evaluation. + bool evaluate(Evaluator &evaluator, AbstractStorageDecl *decl) const; + +public: + bool isCached() const { return true; } +}; + class HasStorageRequest : public SimpleRequest(NominalTypeDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, MemberwiseInitPropertiesRequest, + ArrayRef(NominalTypeDecl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, StructuralTypeRequest, Type(TypeAliasDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, SuperclassTypeRequest, diff --git a/include/swift/AST/TypeMemberVisitor.h b/include/swift/AST/TypeMemberVisitor.h index c9952ddae652f..e2faced14ce91 100644 --- a/include/swift/AST/TypeMemberVisitor.h +++ b/include/swift/AST/TypeMemberVisitor.h @@ -61,10 +61,7 @@ class TypeMemberVisitor : public DeclVisitor { /// A convenience method to visit all the members. void visitMembers(NominalTypeDecl *D) { - for (Decl *member : D->getMembers()) { - member->visitAuxiliaryDecls([&](Decl *decl) { - asImpl().visit(decl); - }); + for (Decl *member : D->getAllMembers()) { asImpl().visit(member); } } diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index bb47fbacc7ee6..679c58cc1c779 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -2315,6 +2315,16 @@ FUNCTION(GenericInitWithTake, ATTRS(NoUnwind), EFFECT(Refcounting)) +// unsigned swift_enumFn_getEnumTag(swift::OpaqueValue *address, +// const Metadata *metadata); +FUNCTION(EnumFnGetEnumTag, + swift_enumFn_getEnumTag, + C_CC, AlwaysAvailable, + RETURNS(Int32Ty), + ARGS(Int8PtrTy, TypeMetadataPtrTy), + ATTRS(NoUnwind), + EFFECT(NoEffect)) + // unsigned swift_multiPayloadEnumGeneric_getEnumTag(opaque* address, // const Metadata *type); FUNCTION(MultiPayloadEnumGenericGetEnumTag, @@ -2325,7 +2335,18 @@ FUNCTION(MultiPayloadEnumGenericGetEnumTag, ATTRS(NoUnwind), EFFECT(NoEffect)) -// void swift_generic_instantiateLayoutString(const uint8_t* layoutStr, Metadata* type); +// unsigned swift_singlePayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address, +// const Metadata *metadata); +FUNCTION(SinglePayloadEnumGenericGetEnumTag, + swift_singlePayloadEnumGeneric_getEnumTag, + C_CC, AlwaysAvailable, + RETURNS(Int32Ty), + ARGS(Int8PtrTy, TypeMetadataPtrTy), + ATTRS(NoUnwind), + EFFECT(NoEffect)) + +// void swift_generic_instantiateLayoutString(const uint8_t* layoutStr, +// Metadata* type); FUNCTION(GenericInstantiateLayoutString, swift_generic_instantiateLayoutString, C_CC, AlwaysAvailable, diff --git a/include/swift/SIL/FieldSensitivePrunedLiveness.h b/include/swift/SIL/FieldSensitivePrunedLiveness.h index a28019fc90492..eb42ee56e9135 100644 --- a/include/swift/SIL/FieldSensitivePrunedLiveness.h +++ b/include/swift/SIL/FieldSensitivePrunedLiveness.h @@ -571,19 +571,22 @@ class FieldSensitivePrunedLiveBlocks { } /// Update this liveness result for a single use. - IsLive updateForUse(SILInstruction *user, unsigned bitNo) { + IsLive updateForUse(SILInstruction *user, unsigned bitNo, + bool isUserBeforeDef) { assert(isInitialized()); auto *block = user->getParent(); - auto liveness = getBlockLiveness(block, bitNo); - if (liveness != Dead) - return liveness; + if (!isUserBeforeDef) { + auto liveness = getBlockLiveness(block, bitNo); + if (liveness != Dead) + return liveness; + } computeScalarUseBlockLiveness(block, bitNo); return getBlockLiveness(block, bitNo); } /// Update this range of liveness results for a single use. void updateForUse(SILInstruction *user, unsigned startBitNo, - unsigned endBitNo, + unsigned endBitNo, SmallBitVector const &useBeforeDefBits, SmallVectorImpl &resultingLiveness); IsLive getBlockLiveness(SILBasicBlock *bb, unsigned bitNo) const { @@ -854,10 +857,12 @@ class FieldSensitivePrunedLiveness { /// Also for flexibility, \p affectedAddress must be a derived projection from /// the base that \p user is affecting. void updateForUse(SILInstruction *user, TypeTreeLeafTypeRange span, - bool lifetimeEnding); + bool lifetimeEnding, + SmallBitVector const &useBeforeDefBits); void updateForUse(SILInstruction *user, SmallBitVector const &bits, - bool lifetimeEnding); + bool lifetimeEnding, + SmallBitVector const &useBeforeDefBits); void getBlockLiveness(SILBasicBlock *bb, TypeTreeLeafTypeRange span, SmallVectorImpl @@ -1160,6 +1165,12 @@ class FieldSensitiveSSAPrunedLiveRange return def.first->getParentBlock() == block && def.second->contains(bit); } + template + void isUserBeforeDef(SILInstruction *user, Iterable const &iterable, + SmallBitVector &useBeforeDefBits) const { + assert(useBeforeDefBits.none()); + } + void findBoundariesInBlock(SILBasicBlock *block, unsigned bitNo, bool isLiveOut, FieldSensitivePrunedLivenessBoundary &boundary) const; @@ -1243,6 +1254,36 @@ class FieldSensitiveMultiDefPrunedLiveRange }); } + bool isDefBlock(SILBasicBlock *block, SmallBitVector const &bits) const { + assert(isInitialized()); + auto iter = defBlocks.find(block); + if (!iter) + return false; + return llvm::any_of(*iter, [&](TypeTreeLeafTypeRange storedSpan) { + return storedSpan.intersects(bits); + }); + } + + /// Return true if \p user occurs before the first def in the same basic + /// block. In classical liveness dataflow terms, gen/kill conditions over all + /// users in 'bb' are: + /// + /// Gen(bb) |= !isDefBlock(bb) || isUserBeforeDef(bb) + /// Kill(bb) &= isDefBlock(bb) && !isUserBeforeDef(bb) + /// + /// If 'bb' has no users, it is neither a Gen nor Kill. Otherwise, Gen and + /// Kill are complements. + bool isUserBeforeDef(SILInstruction *user, unsigned element) const; + template + void isUserBeforeDef(SILInstruction *user, Iterable const &iterable, + SmallBitVector &useBeforeDefBits) const { + for (auto bit : iterable) { + if (isUserBeforeDef(user, bit)) { + useBeforeDefBits.set(bit); + } + } + } + bool isDef(SILInstruction *inst, unsigned bit) const { assert(isInitialized()); auto iter = defs.find(cast(inst)); diff --git a/include/swift/SIL/SILBasicBlock.h b/include/swift/SIL/SILBasicBlock.h index b44c3c528616d..a44adee4c0164 100644 --- a/include/swift/SIL/SILBasicBlock.h +++ b/include/swift/SIL/SILBasicBlock.h @@ -516,6 +516,17 @@ public SwiftObjectHeader { void printAsOperand(raw_ostream &OS, bool PrintType = true); +#ifndef NDEBUG + /// Print the ID of the block, bbN. + void dumpID() const; + + /// Print the ID of the block with \p OS, bbN. + void printID(llvm::raw_ostream &OS) const; + + /// Print the ID of the block with \p Ctx, bbN. + void printID(SILPrintContext &Ctx) const; +#endif + /// getSublistAccess() - returns pointer to member of instruction list static InstListType SILBasicBlock::*getSublistAccess() { return &SILBasicBlock::InstList; diff --git a/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h b/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h index d9786c4f3458f..1ae6d8ebd359f 100644 --- a/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h +++ b/include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h @@ -135,7 +135,9 @@ bool canCloneTerminator(TermInst *termInst); /// BasicBlockCloner handles this internally. class SinkAddressProjections { // Projections ordered from last to first in the chain. - SmallVector projections; + SmallVector oldProjections; + // Cloned projections to avoid address phis. + SmallVectorImpl *newProjections; SmallSetVector inBlockDefs; // Transient per-projection data for use during cloning. @@ -143,6 +145,10 @@ class SinkAddressProjections { llvm::SmallDenseMap firstBlockUse; public: + SinkAddressProjections( + SmallVectorImpl *newProjections = nullptr) + : newProjections(newProjections) {} + /// Check for an address projection chain ending at \p inst. Return true if /// the given instruction is successfully analyzed. /// @@ -163,6 +169,7 @@ class SinkAddressProjections { ArrayRef getInBlockDefs() const { return inBlockDefs.getArrayRef(); } + /// Clone the chain of projections at their use sites. /// /// Return true if anything was done. diff --git a/include/swift/Threading/ThreadSanitizer.h b/include/swift/Threading/ThreadSanitizer.h index 0a6a8028ea183..a50a554f08c40 100644 --- a/include/swift/Threading/ThreadSanitizer.h +++ b/include/swift/Threading/ThreadSanitizer.h @@ -24,7 +24,10 @@ namespace swift { -#if defined(_WIN32) || defined(__wasi__) || !__has_include() +#if SWIFT_THREADING_NONE \ + || defined(_WIN32) || defined(__wasi__) \ + || !__has_include() \ + || (defined(SWIFT_STDLIB_HAS_DLSYM) && !SWIFT_STDLIB_HAS_DLSYM) #define SWIFT_THREADING_TSAN_SUPPORT 0 @@ -35,6 +38,7 @@ template T *acquire(T *ptr) { return ptr; } template T *release(T *ptr) { return ptr; } } // namespace tsan + #else #define SWIFT_THREADING_TSAN_SUPPORT 1 diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index daee76ed386e6..4f5bd9e9f7f83 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1392,8 +1392,9 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, SmallString<32> buffer; StringRef nameText = name.getName().getString(buffer); bool shouldEscape = - escapeKeywordInContext(nameText, PrintNameContext::Normal) || - nameText == "$"; + !name.getName().isSpecial() && + (escapeKeywordInContext(nameText, PrintNameContext::Normal) || + nameText == "$"); Printer << "("; if (shouldEscape) Printer << "`"; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 61e4f39837c12..780f723bcfa2b 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -4924,6 +4924,16 @@ NominalTypeDecl::getInitAccessorProperties() const { {}); } +ArrayRef +NominalTypeDecl::getMemberwiseInitProperties() const { + auto &ctx = getASTContext(); + auto mutableThis = const_cast(this); + return evaluateOrDefault( + ctx.evaluator, + MemberwiseInitPropertiesRequest{mutableThis}, + {}); +} + void NominalTypeDecl::collectPropertiesInitializableByInitAccessors( std::multimap &result) const { for (auto *property : getInitAccessorProperties()) { @@ -7181,8 +7191,10 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const { // If this is a computed property with `init` accessor, it's // memberwise initializable when it could be used to initialize // other stored properties. - if (auto *init = getAccessor(AccessorKind::Init)) - return init->getAttrs().hasAttribute(); + if (hasInitAccessor()) { + if (auto *init = getAccessor(AccessorKind::Init)) + return init->getAttrs().hasAttribute(); + } // If this is a computed property, it's not memberwise initialized unless // the caller has asked for the declared properties and it is either a diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 88e9cf8da265b..6455e1ffff25f 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -920,6 +920,10 @@ DeclRange IterableDeclContext::getCurrentMembersWithoutLoading() const { } DeclRange IterableDeclContext::getMembers() const { + return getCurrentMembers(); +} + +DeclRange IterableDeclContext::getCurrentMembers() const { loadAllMembers(); return getCurrentMembersWithoutLoading(); diff --git a/lib/ClangImporter/ClangAdapter.cpp b/lib/ClangImporter/ClangAdapter.cpp index 06da77966d051..bbc4335ca9a4b 100644 --- a/lib/ClangImporter/ClangAdapter.cpp +++ b/lib/ClangImporter/ClangAdapter.cpp @@ -554,7 +554,7 @@ clang::TypedefNameDecl *importer::findSwiftNewtype(const clang::NamedDecl *decl, clang::LookupResult lookupResult(clangSema, notificationName, clang::SourceLocation(), clang::Sema::LookupOrdinaryName); - if (!clangSema.LookupName(lookupResult, nullptr)) + if (!clangSema.LookupName(lookupResult, clangSema.TUScope)) return nullptr; auto nsDecl = lookupResult.getAsSingle(); if (!nsDecl) diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index c5038e7e1480a..56fce770ffad7 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -2757,7 +2757,7 @@ ClangImporter::Implementation::lookupTypedef(clang::DeclarationName name) { clang::SourceLocation(), clang::Sema::LookupOrdinaryName); - if (sema.LookupName(lookupResult, /*scope=*/nullptr)) { + if (sema.LookupName(lookupResult, sema.TUScope)) { for (auto decl : lookupResult) { if (auto typedefDecl = dyn_cast(decl->getUnderlyingDecl())) diff --git a/lib/ClangImporter/ImportMacro.cpp b/lib/ClangImporter/ImportMacro.cpp index e50d9e82f8671..bc6f65dc95ac5 100644 --- a/lib/ClangImporter/ImportMacro.cpp +++ b/lib/ClangImporter/ImportMacro.cpp @@ -328,6 +328,8 @@ static Optional> } // Macro identifier. + // TODO: for some reason when in C++ mode, "hasMacroDefinition" is often + // false: rdar://110071334 } else if (token.is(clang::tok::identifier) && token.getIdentifierInfo()->hasMacroDefinition()) { @@ -422,7 +424,7 @@ static ValueDecl *importMacro(ClangImporter::Implementation &impl, auto diagState = impl.getClangSema().DelayedDiagnostics.push(diagPool); auto parsedType = impl.getClangSema().getTypeName(identifier, clang::SourceLocation(), - /*scope*/nullptr); + impl.getClangSema().TUScope); impl.getClangSema().DelayedDiagnostics.popWithoutEmitting(diagState); if (parsedType && diagPool.empty()) { diff --git a/lib/IRGen/GenValueWitness.cpp b/lib/IRGen/GenValueWitness.cpp index c5b823daf0ab3..27d25c8fad3a5 100644 --- a/lib/IRGen/GenValueWitness.cpp +++ b/lib/IRGen/GenValueWitness.cpp @@ -885,8 +885,8 @@ void addStride(ConstantStructBuilder &B, const TypeInfo *TI, IRGenModule &IGM) { } } // end anonymous namespace -bool isRuntimeInstatiatedLayoutString(IRGenModule &IGM, - const TypeLayoutEntry *typeLayoutEntry) { +static bool isRuntimeInstatiatedLayoutString(IRGenModule &IGM, + const TypeLayoutEntry *typeLayoutEntry) { if (IGM.Context.LangOpts.hasFeature(Feature::LayoutStringValueWitnesses) && IGM.Context.LangOpts.hasFeature( Feature::LayoutStringValueWitnessesInstantiation) && @@ -899,6 +899,36 @@ bool isRuntimeInstatiatedLayoutString(IRGenModule &IGM, return false; } +static llvm::Constant *getEnumTagFunction(IRGenModule &IGM, + const EnumTypeLayoutEntry *typeLayoutEntry) { + if (typeLayoutEntry->isSingleton()) { + return nullptr; + } else if (!typeLayoutEntry->isFixedSize(IGM)) { + if (typeLayoutEntry->isMultiPayloadEnum()) { + return IGM.getMultiPayloadEnumGenericGetEnumTagFn(); + } else { + // TODO: implement support for single payload generic enums + return IGM.getSinglePayloadEnumGenericGetEnumTagFn(); + } + } else if (typeLayoutEntry->isMultiPayloadEnum()) { + return IGM.getEnumFnGetEnumTagFn(); + } else { + auto &payloadTI = **(typeLayoutEntry->cases[0]->getFixedTypeInfo()); + auto mask = payloadTI.getFixedExtraInhabitantMask(IGM); + auto tzCount = mask.countTrailingZeros(); + auto shiftedMask = mask.lshr(tzCount); + auto toCount = shiftedMask.countTrailingOnes(); + if (payloadTI.mayHaveExtraInhabitants(IGM) && + (mask.countPopulation() > 64 || + toCount != mask.countPopulation() || + (tzCount % toCount != 0))) { + return IGM.getEnumFnGetEnumTagFn(); + } else { + return nullptr; + } + } +} + static bool valueWitnessRequiresCopyability(ValueWitness index) { switch (index) { @@ -1127,9 +1157,8 @@ static void addValueWitness(IRGenModule &IGM, ConstantStructBuilder &B, if (auto *typeLayoutEntry = typeInfo.buildTypeLayoutEntry( IGM, ty, /*useStructLayouts*/ true)) { if (auto *enumLayoutEntry = typeLayoutEntry->getAsEnum()) { - if (enumLayoutEntry->isMultiPayloadEnum() && - !typeLayoutEntry->isFixedSize(IGM)) { - return addFunction(IGM.getMultiPayloadEnumGenericGetEnumTagFn()); + if (auto *fn = getEnumTagFunction(IGM, enumLayoutEntry)) { + return addFunction(fn); } } } diff --git a/lib/Index/Index.cpp b/lib/Index/Index.cpp index 90c862a75a30e..e93e965d2b068 100644 --- a/lib/Index/Index.cpp +++ b/lib/Index/Index.cpp @@ -706,14 +706,7 @@ class IndexSwiftASTWalker : public SourceEntityWalker { return; unsigned CurLabel = 0; - for (auto Member : TypeContext->getMembers()) { - auto Prop = dyn_cast(Member); - if (!Prop) - continue; - - if (!Prop->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) - continue; - + for (auto Prop : TypeContext->getMemberwiseInitProperties()) { if (CurLabel == Args.size()) break; diff --git a/lib/Refactoring/Refactoring.cpp b/lib/Refactoring/Refactoring.cpp index 5cda1ef0ccf58..274d921c2ead7 100644 --- a/lib/Refactoring/Refactoring.cpp +++ b/lib/Refactoring/Refactoring.cpp @@ -3415,7 +3415,7 @@ collectMembersForInit(ResolvedCursorInfoPtr CursorInfo, SourceManager &SM = nominalDecl->getASTContext().SourceMgr; - for (auto member : nominalDecl->getMembers()) { + for (auto member : nominalDecl->getMemberwiseInitProperties()) { auto varDecl = dyn_cast(member); if (!varDecl) { continue; @@ -3428,10 +3428,6 @@ collectMembersForInit(ResolvedCursorInfoPtr CursorInfo, continue; } - if (!varDecl->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) { - continue; - } - auto patternBinding = varDecl->getParentPatternBinding(); if (!patternBinding) continue; diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 1cc62284d50a9..d9c0b0adc2539 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -855,6 +855,12 @@ class SILPrinter : public SILInstructionVisitor { *this << ')'; } +#ifndef NDEBUG + void printID(const SILBasicBlock *BB) { + *this << Ctx.getID(BB) << "\n"; + } +#endif + void print(const SILBasicBlock *BB) { // Output uses for BB arguments. These are put into place as comments before // the block header. @@ -3086,6 +3092,21 @@ void SILBasicBlock::print(SILPrintContext &Ctx) const { SILPrinter(Ctx).print(this); } +#ifndef NDEBUG +void SILBasicBlock::dumpID() const { + printID(llvm::errs()); +} + +void SILBasicBlock::printID(llvm::raw_ostream &OS) const { + SILPrintContext Ctx(OS); + printID(Ctx); +} + +void SILBasicBlock::printID(SILPrintContext &Ctx) const { + SILPrinter(Ctx).printID(this); +} +#endif + /// Pretty-print the SILFunction to errs. void SILFunction::dump(bool Verbose) const { SILPrintContext Ctx(llvm::errs(), Verbose); diff --git a/lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp b/lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp index 4dd92a03571b7..a2b0a4cf090fa 100644 --- a/lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp +++ b/lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp @@ -514,6 +514,7 @@ void FieldSensitivePrunedLiveBlocks::computeScalarUseBlockLiveness( /// Terminators are not live out of the block. void FieldSensitivePrunedLiveBlocks::updateForUse( SILInstruction *user, unsigned startBitNo, unsigned endBitNo, + SmallBitVector const &useBeforeDefBits, SmallVectorImpl &resultingLivenessInfo) { assert(isInitialized()); resultingLivenessInfo.clear(); @@ -526,10 +527,15 @@ void FieldSensitivePrunedLiveBlocks::updateForUse( for (unsigned index : indices(resultingLivenessInfo)) { unsigned specificBitNo = startBitNo + index; + auto isUseBeforeDef = useBeforeDefBits.test(specificBitNo); switch (resultingLivenessInfo[index]) { case LiveOut: case LiveWithin: - continue; + if (!isUseBeforeDef) { + continue; + } else { + LLVM_FALLTHROUGH; + } case Dead: { // This use block has not yet been marked live. Mark it and its // predecessor blocks live. @@ -608,21 +614,21 @@ void FieldSensitivePrunedLivenessBoundary::dump() const { // MARK: FieldSensitiveLiveness //===----------------------------------------------------------------------===// -void FieldSensitivePrunedLiveness::updateForUse(SILInstruction *user, - TypeTreeLeafTypeRange range, - bool lifetimeEnding) { +void FieldSensitivePrunedLiveness::updateForUse( + SILInstruction *user, TypeTreeLeafTypeRange range, bool lifetimeEnding, + SmallBitVector const &useBeforeDefBits) { SmallVector resultingLiveness; liveBlocks.updateForUse(user, range.startEltOffset, range.endEltOffset, - resultingLiveness); + useBeforeDefBits, resultingLiveness); addInterestingUser(user, range, lifetimeEnding); } -void FieldSensitivePrunedLiveness::updateForUse(SILInstruction *user, - SmallBitVector const &bits, - bool lifetimeEnding) { +void FieldSensitivePrunedLiveness::updateForUse( + SILInstruction *user, SmallBitVector const &bits, bool lifetimeEnding, + SmallBitVector const &useBeforeDefBits) { for (auto bit : bits.set_bits()) { - liveBlocks.updateForUse(user, bit); + liveBlocks.updateForUse(user, bit, useBeforeDefBits.test(bit)); } addInterestingUser(user, bits, lifetimeEnding); @@ -807,74 +813,46 @@ void FieldSensitivePrunedLiveRange::computeBoundary( } } +bool FieldSensitiveMultiDefPrunedLiveRange::isUserBeforeDef( + SILInstruction *user, unsigned element) const { + auto *block = user->getParent(); + if (!isDefBlock(block, element)) + return false; + + if (llvm::any_of(block->getArguments(), [this, element](SILArgument *arg) { + return isDef(arg, element); + })) { + return false; + } + + auto *current = user; + while (true) { + // If user is also a def, then the use is considered before the def. + current = current->getPreviousInstruction(); + if (!current) + return true; + + if (isDef(current, element)) + return false; + } +} + template void FieldSensitivePrunedLiveRange::updateForUse( SILInstruction *user, TypeTreeLeafTypeRange range, bool lifetimeEnding) { - PRUNED_LIVENESS_LOG( - llvm::dbgs() - << "Begin FieldSensitivePrunedLiveRange::updateForUse " - "for: " - << *user); - PRUNED_LIVENESS_LOG( - llvm::dbgs() << "Looking for def instruction earlier in the block!\n"); - - auto *parentBlock = user->getParent(); - for (auto ii = std::next(user->getReverseIterator()), - ie = parentBlock->rend(); - ii != ie; ++ii) { - // If we find the def, just mark this instruction as being an interesting - // instruction. - if (asImpl().isDef(&*ii, range)) { - PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found def: " << *ii); - PRUNED_LIVENESS_LOG(llvm::dbgs() - << " Marking inst as interesting user and returning!\n"); - addInterestingUser(user, range, lifetimeEnding); - return; - } - } - - // Otherwise, just delegate to our parent class's update for use. This will - // update liveness for our predecessor blocks and add this instruction as an - // interesting user. - PRUNED_LIVENESS_LOG(llvm::dbgs() << "No defs found! Delegating to " - "FieldSensitivePrunedLiveness::updateForUse.\n"); - FieldSensitivePrunedLiveness::updateForUse(user, range, lifetimeEnding); + SmallBitVector useBeforeDefBits(getNumSubElements()); + asImpl().isUserBeforeDef(user, range.getRange(), useBeforeDefBits); + FieldSensitivePrunedLiveness::updateForUse(user, range, lifetimeEnding, + useBeforeDefBits); } template void FieldSensitivePrunedLiveRange::updateForUse( SILInstruction *user, SmallBitVector const &bits, bool lifetimeEnding) { - PRUNED_LIVENESS_LOG( - llvm::dbgs() - << "Begin FieldSensitivePrunedLiveRange::updateForUse " - "for: " - << *user); - PRUNED_LIVENESS_LOG(llvm::dbgs() - << "Looking for def instruction earlier in the block!\n"); - - auto *parentBlock = user->getParent(); - for (auto ii = std::next(user->getReverseIterator()), - ie = parentBlock->rend(); - ii != ie; ++ii) { - // If we find the def, just mark this instruction as being an interesting - // instruction. - if (asImpl().isDef(&*ii, bits)) { - PRUNED_LIVENESS_LOG(llvm::dbgs() << " Found def: " << *ii); - PRUNED_LIVENESS_LOG( - llvm::dbgs() - << " Marking inst as interesting user and returning!\n"); - addInterestingUser(user, bits, lifetimeEnding); - return; - } - } - - // Otherwise, just delegate to our parent class's update for use. This will - // update liveness for our predecessor blocks and add this instruction as an - // interesting user. - PRUNED_LIVENESS_LOG(llvm::dbgs() - << "No defs found! Delegating to " - "FieldSensitivePrunedLiveness::updateForUse.\n"); - FieldSensitivePrunedLiveness::updateForUse(user, bits, lifetimeEnding); + SmallBitVector useBeforeDefBits(getNumSubElements()); + asImpl().isUserBeforeDef(user, bits.set_bits(), useBeforeDefBits); + FieldSensitivePrunedLiveness::updateForUse(user, bits, lifetimeEnding, + useBeforeDefBits); } //===----------------------------------------------------------------------===// diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 85797b65dab02..2fddc359af885 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -1065,24 +1065,6 @@ class SILVerifier : public SILVerifierBase { return InstNumbers[a] < InstNumbers[b]; } - // FIXME: For sanity, address-type phis should be prohibited at all SIL - // stages. However, the optimizer currently breaks the invariant in three - // places: - // 1. Normal Simplify CFG during conditional branch simplification - // (sneaky jump threading). - // 2. Simplify CFG via Jump Threading. - // 3. Loop Rotation. - // - // BasicBlockCloner::canCloneInstruction and sinkAddressProjections is - // designed to avoid this issue, we just need to make sure all passes use it - // correctly. - // - // Minimally, we must prevent address-type phis as long as access markers are - // preserved. A goal is to preserve access markers in OSSA. - bool prohibitAddressPhis() { - return F.hasOwnership(); - } - void visitSILPhiArgument(SILPhiArgument *arg) { // Verify that the `isPhiArgument` property is sound: // - Phi arguments come from branches. @@ -1106,7 +1088,7 @@ class SILVerifier : public SILVerifierBase { "All phi argument inputs must be from branches."); } } - if (arg->isPhi() && prohibitAddressPhis()) { + if (arg->isPhi()) { // As a property of well-formed SIL, we disallow address-type // phis. Supporting them would prevent reliably reasoning about the // underlying storage of memory access. This reasoning is important for diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index eab5c61a53d75..98f620866effa 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -406,7 +406,7 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF, storedProperties.insert(properties.begin(), properties.end()); } - for (auto *member : decl->getMembers()) { + for (auto *member : decl->getAllMembers()) { auto *field = dyn_cast(member); if (!field) continue; diff --git a/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp index 92c5b607745eb..cb9badf559f8e 100644 --- a/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp +++ b/lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp @@ -53,19 +53,19 @@ #define DEBUG_TYPE "array-property-opt" #include "ArrayOpt.h" +#include "swift/SIL/CFG.h" +#include "swift/SIL/DebugUtils.h" +#include "swift/SIL/InstructionUtils.h" +#include "swift/SIL/LoopInfo.h" +#include "swift/SIL/Projection.h" +#include "swift/SIL/SILCloner.h" #include "swift/SILOptimizer/Analysis/ArraySemantic.h" #include "swift/SILOptimizer/Analysis/LoopAnalysis.h" #include "swift/SILOptimizer/PassManager/Transforms.h" +#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h" #include "swift/SILOptimizer/Utils/CFGOptUtils.h" #include "swift/SILOptimizer/Utils/LoopUtils.h" #include "swift/SILOptimizer/Utils/SILSSAUpdater.h" -#include "swift/SIL/CFG.h" -#include "swift/SIL/DebugUtils.h" -#include "swift/SIL/InstructionUtils.h" -#include "swift/SIL/Projection.h" -#include "swift/SIL/LoopInfo.h" -#include "swift/SIL/BasicBlockBits.h" -#include "swift/SIL/SILCloner.h" #include "llvm/ADT/SmallSet.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -86,6 +86,8 @@ class ArrayPropertiesAnalysis { SILBasicBlock *Preheader; DominanceInfo *DomTree; + SinkAddressProjections sinkProj; + llvm::DenseMap InstCountCache; llvm::SmallSet HoistableArray; @@ -169,6 +171,7 @@ class ArrayPropertiesAnalysis { bool FoundHoistable = false; uint32_t LoopInstCount = 0; + for (auto *BB : Loop->getBlocks()) { for (auto &Inst : *BB) { // Can't clone alloc_stack instructions whose dealloc_stack is outside @@ -176,6 +179,10 @@ class ArrayPropertiesAnalysis { if (!canDuplicateLoopInstruction(Loop, &Inst)) return false; + if (!sinkProj.analyzeAddressProjections(&Inst)) { + return false; + } + ArraySemanticsCall ArrayPropsInst(&Inst, "array.props", true); if (!ArrayPropsInst) continue; @@ -512,10 +519,11 @@ class RegionCloner : public SILCloner { SILSSAUpdater &SSAUp) { // Collect outside uses. SmallVector UseList; - for (auto Use : V->getUses()) + for (auto Use : V->getUses()) { if (!isBlockCloned(Use->getUser()->getParent())) { UseList.push_back(UseWrapper(Use)); } + } if (UseList.empty()) return; @@ -532,15 +540,40 @@ class RegionCloner : public SILCloner { void updateSSAForm() { SILSSAUpdater SSAUp; + SmallVector newProjections; + SinkAddressProjections sinkProj(&newProjections); + for (auto *origBB : originalPreorderBlocks()) { // Update outside used phi values. - for (auto *arg : origBB->getArguments()) + for (auto *arg : origBB->getArguments()) { updateSSAForValue(origBB, arg, SSAUp); + } // Update outside used instruction values. for (auto &inst : *origBB) { - for (auto result : inst.getResults()) - updateSSAForValue(origBB, result, SSAUp); + for (auto result : inst.getResults()) { + bool success = sinkProj.analyzeAddressProjections(&inst); + assert(success); + // Sink address projections by cloning to avoid address phis. + sinkProj.cloneProjections(); + + // If no new projections were created, update ssa for the result only. + if (newProjections.empty()) { + updateSSAForValue(origBB, result, SSAUp); + continue; + } + + for (auto *newProj : newProjections) { + // Operand values of new projections may need ssa update. + for (auto opVal : newProj->getOperandValues()) { + if (!isBlockCloned(opVal->getParentBlock())) { + continue; + } + updateSSAForValue(origBB, opVal, SSAUp); + } + } + newProjections.clear(); + } } } } diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp index e1dc49438725b..695f3ec663d12 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp @@ -1075,11 +1075,11 @@ void UseState::initializeLiveness( // Now at this point, we have defined all of our defs so we can start adding // uses to the liveness. for (auto reinitInstAndValue : reinitInsts) { + recordConsumingBlock(reinitInstAndValue.first->getParent(), + reinitInstAndValue.second); if (!isReinitToInitConvertibleInst(reinitInstAndValue.first)) { liveness.updateForUse(reinitInstAndValue.first, reinitInstAndValue.second, false /*lifetime ending*/); - recordConsumingBlock(reinitInstAndValue.first->getParent(), - reinitInstAndValue.second); LLVM_DEBUG(llvm::dbgs() << "Added liveness for reinit: " << *reinitInstAndValue.first; liveness.print(llvm::dbgs())); @@ -1419,6 +1419,8 @@ class ExtendUnconsumedLiveness { private: bool hasDefAfter(SILInstruction *inst, unsigned element); + bool isLiveAtBegin(SILBasicBlock *block, unsigned element, bool isLiveAtEnd, + DestroysCollection const &destroys); bool shouldAddDestroyToLiveness(SILInstruction *destroy, unsigned element, @@ -2979,7 +2981,14 @@ void ExtendUnconsumedLiveness::runOnField( // as the other consuming uses). BasicBlockWorklist worklist(currentDef->getFunction()); for (auto *consumingBlock : consumingBlocks) { - worklist.push(consumingBlock); + if (!originalLiveBlocks.insert(consumingBlock) + // Don't walk into the predecessors of blocks which kill liveness. + && !isLiveAtBegin(consumingBlock, element, /*isLiveAtEnd=*/true, destroys)) { + continue; + } + for (auto *predecessor : consumingBlock->getPredecessorBlocks()) { + worklist.pushIfNotVisited(predecessor); + } } // Walk backwards from consuming blocks. @@ -2988,10 +2997,6 @@ void ExtendUnconsumedLiveness::runOnField( continue; } for (auto *predecessor : block->getPredecessorBlocks()) { - // If the block was discovered by liveness, we already added it to the - // set. - if (originalLiveBlocks.contains(predecessor)) - continue; worklist.pushIfNotVisited(predecessor); } } @@ -3108,6 +3113,37 @@ bool ExtendUnconsumedLiveness::shouldAddDestroyToLiveness( return !consumedAtEntryBlocks.contains(block); } +/// Compute the block's effect on liveness and apply it to \p isLiveAtEnd. +bool ExtendUnconsumedLiveness::isLiveAtBegin(SILBasicBlock *block, + unsigned element, bool isLiveAtEnd, + DestroysCollection const &destroys) { + enum class Effect { + None, // 0 + Kill, // 1 + Gen, // 2 + }; + auto effect = Effect::None; + for (auto &instruction : llvm::reverse(*block)) { + // An instruction can be both a destroy and a def. If it is, its + // behavior is first to destroy and then to init. So when walking + // backwards, its last action is to destroy, so its effect is that of any + // destroy. + if (destroys.find(&instruction) != destroys.end()) { + effect = Effect::Gen; + } else if (liveness.isDef(&instruction, element)) { + effect = Effect::Kill; + } + } + switch (effect) { + case Effect::None: + return isLiveAtEnd; + case Effect::Kill: + return false; + case Effect::Gen: + return true; + } +} + bool ExtendUnconsumedLiveness::hasDefAfter(SILInstruction *start, unsigned element) { // NOTE: Start iteration at \p start, not its sequel, because diff --git a/lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp b/lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp index 64a30d1f03ee5..24657caf873eb 100644 --- a/lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp +++ b/lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp @@ -73,6 +73,7 @@ #include "swift/SIL/OwnershipLiveness.h" #include "swift/SIL/OwnershipUtils.h" #include "swift/SIL/PrunedLiveness.h" +#include "swift/SIL/FieldSensitivePrunedLiveness.h" #include "swift/SIL/SILArgumentArrayRef.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILBridging.h" @@ -494,6 +495,85 @@ struct MultiDefUseLivenessTest : UnitTest { } }; +// Arguments: +// - value: entity whose fields' livenesses are being computed +// - string: "defs:" +// - variadic list of triples consisting of +// - value: a live-range defining value +// - int: the beginning of the range of fields defined by the value +// - int: the end of the range of the fields defined by the value +// - the string "uses:" +// - variadic list of quadruples consisting of +// - instruction: a live-range user +// - bool: whether the user is lifetime-ending +// - int: the beginning of the range of fields used by the instruction +// - int: the end of the range of fields used by the instruction +// Dumps: +// - the liveness result and boundary +// +// Computes liveness for the specified def nodes by considering the +// specified uses. The actual uses of the def nodes are ignored. +// +// This is useful for testing non-ssa liveness, for example, of memory +// locations. In that case, the def nodes may be stores and the uses may be +// destroy_addrs. +struct FieldSensitiveMultiDefUseLiveRangeTest : UnitTest { + FieldSensitiveMultiDefUseLiveRangeTest(UnitTestRunner *pass) : UnitTest(pass) {} + + void invoke(Arguments &arguments) override { + SmallVector discoveredBlocks; + auto value = arguments.takeValue(); + FieldSensitiveMultiDefPrunedLiveRange liveness(getFunction(), value, &discoveredBlocks); + + llvm::outs() << "FieldSensitive MultiDef lifetime analysis:\n"; + if (arguments.takeString() != "defs:") { + llvm::report_fatal_error( + "test specification expects the 'defs:' label\n"); + } + while (true) { + auto argument = arguments.takeArgument(); + if (isa(argument)) { + if(cast(argument).getValue() != "uses:") { + llvm::report_fatal_error( + "test specification expects the 'uses:' label\n"); + } + break; + } + auto begin = arguments.takeUInt(); + auto end = arguments.takeUInt(); + TypeTreeLeafTypeRange range(begin, end); + if (isa(argument)) { + auto *instruction = cast(argument).getValue(); + llvm::outs() << " def in range [" << begin << ", " << end << ") instruction: " << *instruction; + liveness.initializeDef(instruction, range); + continue; + } + if (isa(argument)) { + SILValue value = cast(argument).getValue(); + llvm::outs() << " def in range [" << begin << ", " << end << ") value: " << value; + liveness.initializeDef(value, range); + continue; + } + llvm::report_fatal_error( + "test specification expects the 'uses:' label\n"); + } + liveness.finishedInitializationOfDefs(); + while (arguments.hasUntaken()) { + auto *inst = arguments.takeInstruction(); + auto lifetimeEnding = arguments.takeBool(); + auto begin = arguments.takeUInt(); + auto end = arguments.takeUInt(); + TypeTreeLeafTypeRange range(begin, end); + liveness.updateForUse(inst, range, lifetimeEnding); + } + liveness.print(llvm::errs()); + + FieldSensitivePrunedLivenessBoundary boundary(liveness.getNumSubElements()); + liveness.computeBoundary(boundary); + boundary.print(llvm::errs()); + } +}; + // Arguments: // - bool: pruneDebug // - bool: maximizeLifetimes @@ -921,6 +1001,7 @@ void UnitTestRunner::withTest(StringRef name, Doit doit) { ADD_UNIT_TEST_SUBCLASS("linear-liveness", LinearLivenessTest) ADD_UNIT_TEST_SUBCLASS("multidef-liveness", MultiDefLivenessTest) ADD_UNIT_TEST_SUBCLASS("multidefuse-liveness", MultiDefUseLivenessTest) + ADD_UNIT_TEST_SUBCLASS("fieldsensitive-multidefuse-liverange", FieldSensitiveMultiDefUseLiveRangeTest) ADD_UNIT_TEST_SUBCLASS("ossa-lifetime-completion", OSSALifetimeCompletionTest) ADD_UNIT_TEST_SUBCLASS("pruned-liveness-boundary-with-list-of-last-users-insertion-points", PrunedLivenessBoundaryWithListOfLastUsersInsertionPointsTest) ADD_UNIT_TEST_SUBCLASS("shrink-borrow-scope", ShrinkBorrowScopeTest) diff --git a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp index a404bb16ebc9b..09c871fb683cb 100644 --- a/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp +++ b/lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp @@ -223,7 +223,7 @@ void BasicBlockCloner::sinkAddressProjections() { // // Return true on success, even if projections is empty. bool SinkAddressProjections::analyzeAddressProjections(SILInstruction *inst) { - projections.clear(); + oldProjections.clear(); inBlockDefs.clear(); SILBasicBlock *bb = inst->getParent(); @@ -237,7 +237,7 @@ bool SinkAddressProjections::analyzeAddressProjections(SILInstruction *inst) { } if (auto *addressProj = dyn_cast(def)) { if (addressProj->isPure()) { - projections.push_back(addressProj); + oldProjections.push_back(addressProj); return true; } } @@ -252,12 +252,12 @@ bool SinkAddressProjections::analyzeAddressProjections(SILInstruction *inst) { return false; } // Recurse upward through address projections. - for (unsigned idx = 0; idx < projections.size(); ++idx) { + for (unsigned idx = 0; idx < oldProjections.size(); ++idx) { // Only one address result/operand can be handled per instruction. - if (projections.size() != idx + 1) + if (oldProjections.size() != idx + 1) return false; - for (SILValue operandVal : projections[idx]->getOperandValues()) + for (SILValue operandVal : oldProjections[idx]->getOperandValues()) if (!pushOperandVal(operandVal)) return false; } @@ -267,13 +267,13 @@ bool SinkAddressProjections::analyzeAddressProjections(SILInstruction *inst) { // Clone the projections gathered by 'analyzeAddressProjections' at // their use site outside this block. bool SinkAddressProjections::cloneProjections() { - if (projections.empty()) + if (oldProjections.empty()) return false; - SILBasicBlock *bb = projections.front()->getParent(); + SILBasicBlock *bb = oldProjections.front()->getParent(); // Clone projections in last-to-first order. - for (unsigned idx = 0; idx < projections.size(); ++idx) { - auto *oldProj = projections[idx]; + for (unsigned idx = 0; idx < oldProjections.size(); ++idx) { + auto *oldProj = oldProjections[idx]; assert(oldProj->getParent() == bb); // Reset transient per-projection sets. usesToReplace.clear(); @@ -297,9 +297,12 @@ bool SinkAddressProjections::cloneProjections() { auto *useBB = use->getUser()->getParent(); auto *firstUse = firstBlockUse.lookup(useBB); SingleValueInstruction *newProj; - if (use == firstUse) + if (use == firstUse) { newProj = cast(oldProj->clone(use->getUser())); - else { + if (newProjections) { + newProjections->push_back(newProj); + } + } else { newProj = cast(firstUse->get()); assert(newProj->getParent() == useBB); newProj->moveFront(useBB); diff --git a/lib/SILOptimizer/Utils/SILSSAUpdater.cpp b/lib/SILOptimizer/Utils/SILSSAUpdater.cpp index b3f6084d42cc7..2badec4371fc8 100644 --- a/lib/SILOptimizer/Utils/SILSSAUpdater.cpp +++ b/lib/SILOptimizer/Utils/SILSSAUpdater.cpp @@ -86,20 +86,30 @@ areIdentical(llvm::DenseMap &availableValues) { return true; } - auto *mvir = - dyn_cast(availableValues.begin()->second); - if (!mvir) - return false; + if (auto *mvir = dyn_cast( + availableValues.begin()->second)) { + for (auto value : availableValues) { + auto *result = dyn_cast(value.second); + if (!result) + return false; + if (!result->getParent()->isIdenticalTo(mvir->getParent()) || + result->getIndex() != mvir->getIndex()) { + return false; + } + } + return true; + } + auto *firstArg = cast(availableValues.begin()->second); for (auto value : availableValues) { - auto *result = dyn_cast(value.second); - if (!result) + auto *arg = dyn_cast(value.second); + if (!arg) return false; - if (!result->getParent()->isIdenticalTo(mvir->getParent()) || - result->getIndex() != mvir->getIndex()) { + if (arg != firstArg) { return false; } } + return true; } diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 53f73bbe6354f..e151060e3aab3 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -294,20 +294,7 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, if (ICK == ImplicitConstructorKind::Memberwise) { assert(isa(decl) && "Only struct have memberwise constructor"); - std::multimap initializedViaAccessor; - decl->collectPropertiesInitializableByInitAccessors(initializedViaAccessor); - - for (auto member : decl->getMembers()) { - auto var = dyn_cast(member); - if (!var) - continue; - - if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) - continue; - - if (initializedViaAccessor.count(var)) - continue; - + for (auto var : decl->getMemberwiseInitProperties()) { accessLevel = std::min(accessLevel, var->getFormalAccess()); params.push_back(createMemberwiseInitParameter(decl, Loc, var)); } @@ -1408,8 +1395,7 @@ ResolveEffectiveMemberwiseInitRequest::evaluate(Evaluator &evaluator, auto accessLevel = AccessLevel::Public; for (auto *member : decl->getMembers()) { auto *var = dyn_cast(member); - if (!var || - !var->isMemberwiseInitialized(/*preferDeclaredProperties*/ true)) + if (!var || !var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) continue; accessLevel = std::min(accessLevel, var->getFormalAccess()); } diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 4e00cd1925eb5..db524bfc06e4f 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -333,6 +333,18 @@ bool HasInitAccessorRequest::evaluate(Evaluator &evaluator, ArrayRef InitAccessorPropertiesRequest::evaluate(Evaluator &evaluator, NominalTypeDecl *decl) const { + SmallVector results; + for (auto var : decl->getMemberwiseInitProperties()) { + if (var->hasInitAccessor()) + results.push_back(var); + } + + return decl->getASTContext().AllocateCopy(results); +} + +ArrayRef +MemberwiseInitPropertiesRequest::evaluate(Evaluator &evaluator, + NominalTypeDecl *decl) const { IterableDeclContext *implDecl = decl->getImplementationContext(); if (!hasStoredProperties(decl, implDecl)) @@ -342,13 +354,43 @@ InitAccessorPropertiesRequest::evaluate(Evaluator &evaluator, computeLoweredProperties(decl, implDecl, LoweredPropertiesReason::Memberwise); SmallVector results; - for (auto *member : decl->getMembers()) { - auto *var = dyn_cast(member); - if (!var || var->isStatic() || !var->hasInitAccessor()) { - continue; - } + SmallPtrSet subsumedViaInitAccessor; + auto maybeAddProperty = [&](VarDecl *var) { + // We only care about properties that are memberwise initialized. + if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) + return; + + // Add this property. results.push_back(var); + + // If this property has an init accessor, it subsumes all of the stored properties + // that the accessor initializes. Mark those stored properties as being subsumed; we'll + // get back to them later. + if (auto initAccessor = var->getAccessor(AccessorKind::Init)) { + if (auto initAttr = initAccessor->getAttrs().getAttribute()) { + for (auto subsumed : initAttr->getPropertyDecls(initAccessor)) + subsumedViaInitAccessor.insert(subsumed); + } + } + }; + + for (auto *member : decl->getCurrentMembers()) { + if (auto *var = dyn_cast(member)) + maybeAddProperty(var); + + member->visitAuxiliaryDecls([&](Decl *auxDecl) { + if (auto auxVar = dyn_cast(auxDecl)) + maybeAddProperty(auxVar); + }); + } + + // If any properties were subsumed via init accessors, drop them from the list. + if (!subsumedViaInitAccessor.empty()) { + results.erase(std::remove_if(results.begin(), results.end(), [&](VarDecl *var) { + return subsumedViaInitAccessor.contains(var); + }), + results.end()); } return decl->getASTContext().AllocateCopy(results); diff --git a/lib/Threading/ThreadSanitizer.cpp b/lib/Threading/ThreadSanitizer.cpp index a103c161dce91..36a64b166e799 100644 --- a/lib/Threading/ThreadSanitizer.cpp +++ b/lib/Threading/ThreadSanitizer.cpp @@ -23,6 +23,8 @@ #include "swift/shims/Visibility.h" +#include + #include namespace swift { @@ -32,9 +34,6 @@ SWIFT_THREADING_EXPORT bool _swift_tsan_enabled = false; SWIFT_THREADING_EXPORT void (*_swift_tsan_acquire)(const void *) = nullptr; SWIFT_THREADING_EXPORT void (*_swift_tsan_release)(const void *) = nullptr; -#if __has_include() -#include - // The TSan library code will call this function when it starts up extern "C" SWIFT_ATTRIBUTE_FOR_EXPORTS void __tsan_on_initialize() { @@ -53,7 +52,6 @@ void __tsan_on_initialize() { next_init(); } } -#endif // __has_include() } // namespace threading_impl } // namespace swift diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index f5a77c4c81a02..00a87fad69b94 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -352,6 +352,16 @@ function(_add_target_variant_c_compile_flags) list(APPEND result "-DSWIFT_STDLIB_HAS_DLADDR") endif() + if(SWIFT_STDLIB_HAS_DLSYM) + list(APPEND result "-DSWIFT_STDLIB_HAS_DLSYM=1") + else() + list(APPEND result "-DSWIFT_STDLIB_HAS_DLSYM=0") + endif() + + if(SWIFT_STDLIB_HAS_FILESYSTEM) + list(APPEND result "-DSWIFT_STDLIB_HAS_FILESYSTEM") + endif() + if(SWIFT_RUNTIME_STATIC_IMAGE_INSPECTION) list(APPEND result "-DSWIFT_RUNTIME_STATIC_IMAGE_INSPECTION") endif() diff --git a/stdlib/cmake/modules/StdlibOptions.cmake b/stdlib/cmake/modules/StdlibOptions.cmake index 2e946e7cdb927..5547ca1b6a4a0 100644 --- a/stdlib/cmake/modules/StdlibOptions.cmake +++ b/stdlib/cmake/modules/StdlibOptions.cmake @@ -135,7 +135,15 @@ option(SWIFT_STDLIB_BUILD_PRIVATE TRUE) option(SWIFT_STDLIB_HAS_DLADDR - "Build stdlib assuming the runtime environment runtime environment provides dladdr API." + "Build stdlib assuming the runtime environment provides the dladdr API." + TRUE) + +option(SWIFT_STDLIB_HAS_DLSYM + "Build stdlib assuming the runtime environment provides the dlsym API." + TRUE) + +option(SWIFT_STDLIB_HAS_FILESYSTEM + "Build stdlib assuming the runtime environment has a filesystem." TRUE) option(SWIFT_RUNTIME_STATIC_IMAGE_INSPECTION diff --git a/stdlib/public/Concurrency/TaskGroup.swift b/stdlib/public/Concurrency/TaskGroup.swift index 35414ec9d2072..942cd48da6ca0 100644 --- a/stdlib/public/Concurrency/TaskGroup.swift +++ b/stdlib/public/Concurrency/TaskGroup.swift @@ -149,14 +149,14 @@ public func withTaskGroup( /// For example, in the code below, /// nothing is canceled and the group doesn't throw an error: /// -/// try await withThrowingTaskGroup { group in +/// try await withThrowingTaskGroup(of: Void.self) { group in /// group.addTask { throw SomeError() } /// } /// /// In contrast, this example throws `SomeError` /// and cancels all of the tasks in the group: /// -/// try await withThrowingTaskGroup { group in +/// try await withThrowingTaskGroup(of: Void.self) { group in /// group.addTask { throw SomeError() } /// try await group.next() /// } diff --git a/stdlib/public/runtime/BytecodeLayouts.cpp b/stdlib/public/runtime/BytecodeLayouts.cpp index ad3cb4123c35f..4d9c255108a5b 100644 --- a/stdlib/public/runtime/BytecodeLayouts.cpp +++ b/stdlib/public/runtime/BytecodeLayouts.cpp @@ -652,6 +652,17 @@ swift_generic_assignWithTake(swift::OpaqueValue *dest, swift::OpaqueValue *src, return swift_generic_initWithTake(dest, src, metadata); } +extern "C" +unsigned swift_enumFn_getEnumTag(swift::OpaqueValue *address, + const Metadata *metadata) { + auto addr = reinterpret_cast(address); + LayoutStringReader reader{metadata->getLayoutString(), + layoutStringHeaderSize + sizeof(uint64_t)}; + auto getEnumTag = readRelativeFunctionPointer(reader); + + return getEnumTag(addr); +} + extern "C" unsigned swift_multiPayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address, const Metadata *metadata) { @@ -681,6 +692,51 @@ swift_multiPayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address, } } +extern "C" unsigned +swift_singlePayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address, + const Metadata *metadata) { + auto addr = reinterpret_cast(address); + LayoutStringReader reader{metadata->getLayoutString(), + layoutStringHeaderSize + sizeof(uint64_t)}; + + auto tagBytesAndOffset = reader.readBytes(); + auto extraTagBytesPattern = (uint8_t)(tagBytesAndOffset >> 62); + auto xiTagBytesOffset = + tagBytesAndOffset & std::numeric_limits::max(); + const Metadata *xiType = nullptr; + + if (extraTagBytesPattern) { + auto extraTagBytes = 1 << (extraTagBytesPattern - 1); + auto payloadSize = reader.readBytes(); + auto tagBytes = readTagBytes(addr + payloadSize, extraTagBytes); + if (tagBytes) { + xiType = reader.readBytes(); + unsigned payloadNumExtraInhabitants = + xiType ? xiType->vw_getNumExtraInhabitants() : 0; + unsigned caseIndexFromExtraTagBits = + payloadSize >= 4 ? 0 : (tagBytes - 1U) << (payloadSize * 8U); + unsigned caseIndexFromValue = loadEnumElement(addr, payloadSize); + unsigned noPayloadIndex = + (caseIndexFromExtraTagBits | caseIndexFromValue) + + payloadNumExtraInhabitants; + return noPayloadIndex + 1; + } + } else { + reader.skip(sizeof(size_t)); + } + + xiType = reader.readBytes(); + + if (xiType) { + auto numEmptyCases = reader.readBytes(); + + return xiType->vw_getEnumTagSinglePayload( + (const OpaqueValue *)(addr + xiTagBytesOffset), numEmptyCases); + } + + return 0; +} + void swift::swift_resolve_resilientAccessors(uint8_t *layoutStr, size_t layoutStrOffset, const uint8_t *fieldLayoutStr, diff --git a/stdlib/public/runtime/BytecodeLayouts.h b/stdlib/public/runtime/BytecodeLayouts.h index 6a1b83f01f50c..0678697da0c20 100644 --- a/stdlib/public/runtime/BytecodeLayouts.h +++ b/stdlib/public/runtime/BytecodeLayouts.h @@ -113,9 +113,15 @@ swift::OpaqueValue *swift_generic_initWithTake(swift::OpaqueValue *dest, swift::OpaqueValue *src, const Metadata *metadata); SWIFT_RUNTIME_EXPORT +unsigned swift_enumFn_getEnumTag(swift::OpaqueValue *address, + const Metadata *metadata); +SWIFT_RUNTIME_EXPORT unsigned swift_multiPayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address, const Metadata *metadata); SWIFT_RUNTIME_EXPORT +unsigned swift_singlePayloadEnumGeneric_getEnumTag(swift::OpaqueValue *address, + const Metadata *metadata); +SWIFT_RUNTIME_EXPORT void swift_generic_instantiateLayoutString(const uint8_t *layoutStr, Metadata *type); diff --git a/stdlib/public/runtime/Paths.cpp b/stdlib/public/runtime/Paths.cpp index 5ea45bd9fa166..bba1b6e2db7e4 100644 --- a/stdlib/public/runtime/Paths.cpp +++ b/stdlib/public/runtime/Paths.cpp @@ -53,6 +53,28 @@ #include #include +#if !SWIFT_STDLIB_HAS_FILESYSTEM + +SWIFT_RUNTIME_EXPORT +const char * +swift_getRuntimeLibraryPath() { + return nullptr; +} + +SWIFT_RUNTIME_EXPORT +const char * +swift_getRootPath() { + return nullptr; +} + +SWIFT_RUNTIME_EXPORT +char * +swift_copyAuxiliaryExecutablePath(const char *name) { + return nullptr; +} + +#else // SWIFT_STDLIB_HAS_FILESYSTEM + namespace { swift::once_t runtimePathToken; @@ -578,3 +600,5 @@ bool _swift_exists(const char *path) } } + +#endif // SWIFT_STDLIB_HAS_FILESYSTEM diff --git a/test/ClangImporter/cf.swift b/test/ClangImporter/cf.swift index a2e1967b80fb2..019b10f8e7680 100644 --- a/test/ClangImporter/cf.swift +++ b/test/ClangImporter/cf.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -typecheck -verify -import-cf-types -I %S/Inputs/custom-modules %s +// RUN: %target-swift-frontend -disable-objc-attr-requires-foundation-module -typecheck -verify -import-cf-types -enable-experimental-cxx-interop -I %S/Inputs/custom-modules %s + // REQUIRES: objc_interop import CoreCooling diff --git a/test/ClangImporter/macros.swift b/test/ClangImporter/macros.swift index eeccd9fd80a02..6a9f07be9f54c 100644 --- a/test/ClangImporter/macros.swift +++ b/test/ClangImporter/macros.swift @@ -1,5 +1,8 @@ // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-objc-interop -typecheck -verify %s +// Most of these don't pass: rdar://110071334 +// %target-swift-frontend(mock-sdk: %clang-importer-sdk) -enable-experimental-cxx-interop -enable-objc-interop -typecheck -verify %s + @_exported import macros func circle_area(_ radius: CDouble) -> CDouble { diff --git a/test/Interop/Cxx/objc-correctness/Inputs/module.modulemap b/test/Interop/Cxx/objc-correctness/Inputs/module.modulemap index 0d922784daf2a..8f346bf3072d6 100644 --- a/test/Interop/Cxx/objc-correctness/Inputs/module.modulemap +++ b/test/Interop/Cxx/objc-correctness/Inputs/module.modulemap @@ -12,3 +12,9 @@ module CxxClassWithNSStringInit [extern_c] { module NSOptionsMangling { header "NSOptionsMangling.h" } + +module NSNofiticationBridging { + header "nsnotification-bridging.h" + requires objc + requires cplusplus +} diff --git a/test/Interop/Cxx/objc-correctness/Inputs/nsnotification-bridging.h b/test/Interop/Cxx/objc-correctness/Inputs/nsnotification-bridging.h new file mode 100644 index 0000000000000..47435b83b6ac8 --- /dev/null +++ b/test/Interop/Cxx/objc-correctness/Inputs/nsnotification-bridging.h @@ -0,0 +1,3 @@ +#import + +extern NSString * const SpaceShipNotification; diff --git a/test/Interop/Cxx/objc-correctness/nsnotification-bridging-ide-test.swift b/test/Interop/Cxx/objc-correctness/nsnotification-bridging-ide-test.swift new file mode 100644 index 0000000000000..8804fc6c455d7 --- /dev/null +++ b/test/Interop/Cxx/objc-correctness/nsnotification-bridging-ide-test.swift @@ -0,0 +1,7 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=NSNofiticationBridging -I %S/Inputs -source-filename=x -enable-experimental-cxx-interop -enable-objc-interop | %FileCheck %s + +// REQUIRES: objc_interop + +// CHECK: import Foundation + +// CHECK: let SpaceShipNotification: String diff --git a/test/Interpreter/Inputs/layout_string_witnesses_types_resilient.swift b/test/Interpreter/Inputs/layout_string_witnesses_types_resilient.swift index 581c6058d87b2..cc4d1fddd841e 100644 --- a/test/Interpreter/Inputs/layout_string_witnesses_types_resilient.swift +++ b/test/Interpreter/Inputs/layout_string_witnesses_types_resilient.swift @@ -19,13 +19,44 @@ public struct GenericResilient { } } -public enum ResilientMultiPayloadEnum { +public enum ResilientSinglePayloadEnumGeneric { + case empty0 + case empty1 + case nonEmpty0(T) +} + +public enum ResilientMultiPayloadEnumGeneric { case empty0 case empty1 case nonEmpty0(AnyObject) case nonEmpty1(T) } -public func getResilientMultiPayloadEnumEmpty0(_ t: T.Type) -> ResilientMultiPayloadEnum { +public enum ResilientMultiPayloadEnum { + case empty0 + case empty1 + case nonEmpty0(AnyObject, Bool) + case nonEmpty1(AnyObject) +} + +public enum ResilientSinglePayloadEnumComplex { + case empty0 + case empty1 + case nonEmpty(ResilientMultiPayloadEnum) +} + +public func getResilientSinglePayloadEnumGenericEmpty0(_ t: T.Type) -> ResilientSinglePayloadEnumGeneric { + return .empty0 +} + +public func getResilientMultiPayloadEnumGenericEmpty0(_ t: T.Type) -> ResilientMultiPayloadEnumGeneric { + return .empty0 +} + +public func getResilientMultiPayloadEnumEmpty0() -> ResilientMultiPayloadEnum { + return .empty0 +} + +public func getResilientSinglePayloadEnumComplexEmpty0() -> ResilientSinglePayloadEnumComplex { return .empty0 } diff --git a/test/Interpreter/layout_string_witnesses_dynamic.swift b/test/Interpreter/layout_string_witnesses_dynamic.swift index 43e528be75bd0..8a70436928b3c 100644 --- a/test/Interpreter/layout_string_witnesses_dynamic.swift +++ b/test/Interpreter/layout_string_witnesses_dynamic.swift @@ -516,8 +516,21 @@ func testGenericSinglePayloadEnumManyXI() { testGenericSinglePayloadEnumManyXI() +func testResilientSinglePayloadEnumComplexTag() { + let x = switch getResilientSinglePayloadEnumComplexEmpty0() { + case .nonEmpty: 0 + case .empty0: 1 + case .empty1: 2 + } + + // CHECK: Enum case: 1 + print("Enum case: \(x)") +} + +testResilientSinglePayloadEnumComplexTag() + func testResilientMultiPayloadEnumTag() { - let x = switch getResilientMultiPayloadEnumEmpty0(AnyObject.self) { + let x = switch getResilientMultiPayloadEnumEmpty0() { case .nonEmpty0: 0 case .nonEmpty1: 1 case .empty0: 2 @@ -530,6 +543,33 @@ func testResilientMultiPayloadEnumTag() { testResilientMultiPayloadEnumTag() +func testResilientSinglePayloadEnumGenericTag() { + let x = switch getResilientSinglePayloadEnumGenericEmpty0(AnyObject.self) { + case .nonEmpty0: 0 + case .empty0: 1 + case .empty1: 2 + } + + // CHECK: Enum case: 1 + print("Enum case: \(x)") +} + +testResilientSinglePayloadEnumGenericTag() + +func testResilientMultiPayloadEnumGenericTag() { + let x = switch getResilientMultiPayloadEnumGenericEmpty0(AnyObject.self) { + case .nonEmpty0: 0 + case .nonEmpty1: 1 + case .empty0: 2 + case .empty1: 3 + } + + // CHECK: Enum case: 2 + print("Enum case: \(x)") +} + +testResilientMultiPayloadEnumGenericTag() + #if os(macOS) import Foundation diff --git a/test/Interpreter/moveonly_loop.swift b/test/Interpreter/moveonly_loop.swift new file mode 100644 index 0000000000000..d631d5f138185 --- /dev/null +++ b/test/Interpreter/moveonly_loop.swift @@ -0,0 +1,32 @@ +// RUN: %target-run-simple-swift(-Xfrontend -sil-verify-all) | %FileCheck %s +// RUN: %target-run-simple-swift(-O -Xfrontend -sil-verify-all) | %FileCheck %s + +// REQUIRES: executable_test + +struct S: ~Copyable { + let s: String + init(_ s: String) { self.s = s } + deinit { print("deiniting \(s)") } +} + +func use(_ s: borrowing S) { + print("using: \(s.s)") +} + +@_silgen_name("f") +func f(_ c: consuming S) { + repeat { + use(c) + c = S("2") + } while false +} + +func doit() { + let s = S("1") + f(s) +} + +// CHECK: using: 1 +// CHECK-NEXT: deiniting 1 +// CHECK-NEXT: deiniting 2 +doit() diff --git a/test/Macros/macro_expand_peers.swift b/test/Macros/macro_expand_peers.swift index 240a2a6e51f5c..9749c84f98bbe 100644 --- a/test/Macros/macro_expand_peers.swift +++ b/test/Macros/macro_expand_peers.swift @@ -221,8 +221,7 @@ macro AddPeerStoredProperty() = struct SomeStructWithPeerProperties { @AddPeerStoredProperty - - func foo() {} + var foo: String = "hello" } func testStructWithPeers() { diff --git a/test/ModuleInterface/macros.swift b/test/ModuleInterface/macros.swift index 63ea8332a7ad3..d100fee0288a2 100644 --- a/test/ModuleInterface/macros.swift +++ b/test/ModuleInterface/macros.swift @@ -35,9 +35,19 @@ @attached(accessor) public macro myWrapper() = #externalMacro(module: "SomeModule", type: "Wrapper") // CHECK: #if compiler(>=5.3) && $Macros && $AttachedMacros -// CHECK: @attached(member, names: named(`init`), prefixed(`$`)) public macro MemberwiseInit() = #externalMacro(module: "SomeModule", type: "MemberwiseInitMacro") +// CHECK: @attached(member, names: named(init), prefixed(`$`)) public macro MemberwiseInit() = #externalMacro(module: "SomeModule", type: "MemberwiseInitMacro") // CHECK-NEXT: #endif @attached(member, names: named(init), prefixed(`$`)) public macro MemberwiseInit() = #externalMacro(module: "SomeModule", type: "MemberwiseInitMacro") +// CHECK: #if compiler(>=5.3) && $Macros && $AttachedMacros +// CHECK: @attached(member, names: named(`init`), prefixed(`$`)) public macro MemberwiseInitFunc() = #externalMacro(module: "SomeModule", type: "MemberwiseInitFuncMacro") +// CHECK-NEXT: #endif +@attached(member, names: named(`init`), prefixed(`$`)) public macro MemberwiseInitFunc() = #externalMacro(module: "SomeModule", type: "MemberwiseInitFuncMacro") + +// CHECK: #if compiler(>=5.3) && $Macros && $AttachedMacros +// CHECK: @attached(accessor, names: named(init)) public macro AccessorInitFunc() = #externalMacro(module: "SomeModule", type: "AccessorInitFuncMacro") +// CHECK-NEXT: #endif +@attached(accessor, names: named(init)) public macro AccessorInitFunc() = #externalMacro(module: "SomeModule", type: "AccessorInitFuncMacro") + // CHECK-NOT: internalStringify @freestanding(expression) macro internalStringify(_ value: T) -> (T, String) = #externalMacro(module: "SomeModule", type: "StringifyMacro") diff --git a/test/SILOptimizer/array_property_opt.sil b/test/SILOptimizer/array_property_opt.sil index 0af8872442a94..bffc6d3c9a0c9 100644 --- a/test/SILOptimizer/array_property_opt.sil +++ b/test/SILOptimizer/array_property_opt.sil @@ -1,5 +1,9 @@ // RUN: %target-sil-opt -parse-serialized-sil -enable-sil-verify-all %s -array-property-opt | %FileCheck %s +// Linux doesn't have the same symbol name for _ArrayBuffer, which is part of +// the ObjC runtime interop. Use `_ContiguousArrayBuffer instead. +// REQUIRES: objc_interop + sil_stage canonical import Builtin @@ -258,3 +262,132 @@ bb10: // Exit dominated by bb3 bb11: // Non-exit dominated by bb1 return %4 : $Builtin.Int1 } + +class Klass { + var val: Optional +} + +struct WrapperStruct { + var val: Klass +} + +sil @use_klass : $@convention(thin) (@in_guaranteed Klass) -> () + +sil @test_sink_address_proj : $@convention(thin) (@inout MyArray, @in_guaranteed WrapperStruct) -> () { +bb0(%0 : $*MyArray, %1 : $*WrapperStruct): + %3 = load %0 : $*MyArray + br bb1 + +bb1: + %2 = function_ref @arrayPropertyIsNative : $@convention(method) (@owned MyArray) -> Bool + retain_value %3 : $MyArray + %5 = apply %2(%3) : $@convention(method) (@owned MyArray) -> Bool + %ele = struct_element_addr %1 : $*WrapperStruct, #WrapperStruct.val + cond_br undef, bb5, bb2 + +bb2: + %6 = integer_literal $Builtin.Int1, -1 + cond_br %6, bb3, bb4 + +bb3: + br bb1 + +bb4: + br bb6 + +bb5: + %f = function_ref @use_klass : $@convention(thin) (@in_guaranteed Klass) -> () + %a = apply %f(%ele) : $@convention(thin) (@in_guaranteed Klass) -> () + br bb6 + +bb6: + %t = tuple () + return %t : $() +} + +sil [_semantics "array.props.isNativeTypeChecked"] @hoistableIsNativeTypeChecked : $@convention(method) (@guaranteed Array) -> Bool +sil [_semantics "array.get_element"] @getElement : $@convention(method) (Int, Bool, _DependenceToken, @guaranteed Array) -> @owned Klass +sil [_semantics "array.get_count"] @getCount : $@convention(method) (@guaranteed Array) -> Int + +sil hidden @test_array_prop_opt : $@convention(thin) (@guaranteed Optional>) -> Int { +bb0(%0 : $Optional>): + %4 = integer_literal $Builtin.Int64, 0 + switch_enum %0 : $Optional>, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb1 + +bb1: + br bb12(%4 : $Builtin.Int64) + +bb2(%12 : $Array): + %14 = function_ref @getCount : $@convention(method) (@guaranteed Array) -> Int + retain_value %0 : $Optional> + retain_value %0 : $Optional> + %17 = apply %14(%12) : $@convention(method) (@guaranteed Array) -> Int + %18 = struct_extract %17 : $Int, #Int._value + %19 = builtin "cmp_eq_Int64"(%18 : $Builtin.Int64, %4 : $Builtin.Int64) : $Builtin.Int1 + cond_br %19, bb3, bb4 + +bb3: + release_value %0 : $Optional> + %22 = unchecked_enum_data %0 : $Optional>, #Optional.some!enumelt + %23 = struct_extract %22 : $Array, #Array._buffer + %24 = struct_extract %23 : $_ArrayBuffer, #_ArrayBuffer._storage + %25 = struct_extract %24 : $_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue + strong_release %25 : $Builtin.BridgeObject + br bb12(%4 : $Builtin.Int64) + +bb4: + %28 = function_ref @hoistableIsNativeTypeChecked : $@convention(method) (@guaranteed Array) -> Bool + %29 = function_ref @getElement : $@convention(method) (Int, Bool, _DependenceToken, @guaranteed Array) -> @owned Klass + %30 = integer_literal $Builtin.Int64, 1 + %31 = integer_literal $Builtin.Int1, -1 + %32 = struct $_DependenceToken () + br bb5(%4 : $Builtin.Int64) + +bb5(%34 : $Builtin.Int64): + %35 = struct $Int (%34 : $Builtin.Int64) + %36 = apply %28(%12) : $@convention(method) (@guaranteed Array) -> Bool + %37 = apply %29(%35, %36, %32, %12) : $@convention(method) (Int, Bool, _DependenceToken, @guaranteed Array) -> @owned Klass + %38 = builtin "sadd_with_overflow_Int64"(%34 : $Builtin.Int64, %30 : $Builtin.Int64, %31 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) + %39 = tuple_extract %38 : $(Builtin.Int64, Builtin.Int1), 0 + %40 = tuple_extract %38 : $(Builtin.Int64, Builtin.Int1), 1 + cond_fail %40 : $Builtin.Int1, "arithmetic overflow" + %43 = ref_element_addr %37 : $Klass, #Klass.val + %44 = begin_access [read] [dynamic] [no_nested_conflict] %43 : $*Optional + %45 = load %44 : $*Optional + end_access %44 : $*Optional + switch_enum %45 : $Optional, case #Optional.some!enumelt: bb9, case #Optional.none!enumelt: bb6 + +bb6: + strong_release %37 : $Klass + %49 = builtin "cmp_eq_Int64"(%39 : $Builtin.Int64, %18 : $Builtin.Int64) : $Builtin.Int1 + cond_br %49, bb7, bb8 + +bb7: + release_value %0 : $Optional> + release_value %0 : $Optional> + br bb12(%4 : $Builtin.Int64) + +bb8: + br bb5(%39 : $Builtin.Int64) + +bb9: + release_value %0 : $Optional> + %57 = begin_access [read] [dynamic] [no_nested_conflict] %43 : $*Optional + %58 = load %57 : $*Optional + end_access %57 : $*Optional + switch_enum %58 : $Optional, case #Optional.some!enumelt: bb11, case #Optional.none!enumelt: bb10 + +bb10: + cond_fail %31 : $Builtin.Int1, "Unexpectedly found nil while unwrapping an Optional value" + unreachable + +bb11(%63 : $Int): + release_value %0 : $Optional> + strong_release %37 : $Klass + %66 = struct_extract %63 : $Int, #Int._value + br bb12(%66 : $Builtin.Int64) + +bb12(%69 : $Builtin.Int64): + %70 = struct $Int (%69 : $Builtin.Int64) + return %70 : $Int +} diff --git a/test/SILOptimizer/discard_checking.swift b/test/SILOptimizer/discard_checking.swift index 69334b6283c36..98589911a7383 100644 --- a/test/SILOptimizer/discard_checking.swift +++ b/test/SILOptimizer/discard_checking.swift @@ -473,14 +473,11 @@ struct Basics: ~Copyable { } consuming func reinitAfterDiscard3_bad(_ c: Color) throws { - // expected-error@-1 {{must consume 'self' before exiting method that discards self}} - // FIXME: ^ this error is related to rdar://110239087 - repeat { self = Basics() // expected-error {{cannot reinitialize 'self' after 'discard self'}} discard self // expected-note 2{{discarded self here}} } while false - } + } // expected-error {{must consume 'self' before exiting method that discards self}} consuming func reinitAfterDiscard3_ok(_ c: Color) throws { self = Basics() diff --git a/test/SILOptimizer/field_sensitive_liverange_unit.sil b/test/SILOptimizer/field_sensitive_liverange_unit.sil new file mode 100644 index 0000000000000..d7f20e1648f12 --- /dev/null +++ b/test/SILOptimizer/field_sensitive_liverange_unit.sil @@ -0,0 +1,185 @@ +// RUN: %target-sil-opt -unit-test-runner %s -o /dev/null 2>&1 | %FileCheck %s + +class C {} +sil @getC : $@convention(thin) () -> (@owned C) + +// Test a live range that is extended through reborrows, +// considering them new defs. +// (e.g. BorrowedValue::visitTransitiveLifetimeEndingUses) +// +// This live range is not dominated by the original borrow. +// +// CHECK-LABEL: testReborrow: fieldsensitive-multidefuse-liverange +// CHECK: FieldSensitive MultiDef lifetime analysis: +// CHECK: def in range [0, 1) value: [[B:%.*]] = load_borrow %0 : $*C +// CHECK: def in range [0, 1) value: [[RB:%.*]] = argument of bb3 : $C +// CHECK-NEXT: bb2: LiveWithin +// CHECK-NEXT: bb3: LiveWithin +// CHECK-NEXT: last user: br bb3([[B]] : $C) +// CHECK-NEXT: at 1 +// CHECK-NEXT: last user: end_borrow [[RB]] : $C +// CHECK-NEXT: at 1 +sil [ossa] @testReborrow : $@convention(thin) () -> () { +bb0: + test_specification """ + fieldsensitive-multidefuse-liverange + @instruction + defs: + @trace[0] 0 1 + @trace[1] 0 1 + uses: + @block[2].instruction[3] true 0 1 + @block[3].instruction[0] true 0 1 + """ + %stack = alloc_stack $C + %getC = function_ref @getC : $@convention(thin) () -> (@owned C) + cond_br undef, bb1, bb2 + +bb1: + %c1 = apply %getC() : $@convention(thin) () -> (@owned C) + store %c1 to [init] %stack : $*C + %borrow1 = load_borrow %stack : $*C + br bb3(%borrow1 : $C) + +bb2: + %c2 = apply %getC() : $@convention(thin) () -> (@owned C) + store %c2 to [init] %stack : $*C + %borrow2 = load_borrow %stack : $*C + debug_value [trace] %borrow2 : $C + br bb3(%borrow2 : $C) + +bb3(%reborrow : @guaranteed $C): + debug_value [trace] %reborrow : $C + end_borrow %reborrow : $C + br bb4 + +bb4: + destroy_addr %stack : $*C + dealloc_stack %stack : $*C + %99 = tuple() + return %99 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on testMultiDefUseAddressReinit +// CHECK: MultiDef lifetime analysis: +// CHECK: def in range [0, 1) instruction: store %{{.*}} to [init] [[ADDR:%.*]] : $*C +// CHECK: def in range [0, 1) instruction: store %0 to [init] [[ADDR]] : $*C +// CHECK: bb0: LiveOut +// CHECK: bb1: LiveWithin +// CHECK: last user: %{{.*}} = load [copy] [[ADDR]] : $*C +// CHECK: boundary edge: bb2 +// CHECK: dead def: store %0 to [init] %1 : $*C +// CHECK-LABEL: end running test 1 of 1 on testMultiDefUseAddressReinit +sil [ossa] @testMultiDefUseAddressReinit : $@convention(thin) (@owned C) -> () { +bb0(%0: @owned $C): + test_specification """ + fieldsensitive-multidefuse-liverange + @instruction + defs: + @instruction[+2] 0 1 + @block[1].instruction[2] 0 1 + uses: + @block[1].instruction[0] false 0 1 + """ + %1 = alloc_stack $C + %2 = copy_value %0 : $C + store %2 to [init] %1 : $*C + cond_br undef, bb1, bb2 + +bb1: + %5 = load [copy] %1 : $*C + destroy_addr %1 : $*C + store %0 to [init] %1 : $*C + destroy_value %5 : $C + br bb3 + +bb2: + destroy_value %0 : $C + br bb3 + +bb3: + destroy_addr %1 : $*C + dealloc_stack %1 : $*C + %9999 = tuple () + return %9999 : $() +} + +// A single instruction occurs twice on the same liverange +// boundary. Once as a last use, and once as a dead def. +// This is a particularly problematic corner case. +// +// CHECK-LABEL: testDeadSelfKill: fieldsensitive-multidefuse-liverange +// CHECK: FieldSensitive MultiDef lifetime analysis: +// CHECK: def in range [0, 1) instruction: store {{%[^,]+}} to [init] [[STACK:%[^,]+]] : +// CHECK: def in range [0, 1) instruction: store {{%[^,]+}} to [assign] [[STACK]] +// CHECK: bb1: LiveWithin +// CHECK: last user: store {{%[^,]+}} to [assign] [[STACK]] +// CHECK: dead def: store {{%[^,]+}} to [assign] [[STACK]] +sil [ossa] @testDeadSelfKill : $@convention(thin) () -> () { +bb0: + br bb3 + +bb1(%1 : @owned $C, %2 : @owned $C): + test_specification """ + fieldsensitive-multidefuse-liverange + @instruction + defs: + @instruction[+1] 0 1 + @instruction[+2] 0 1 + uses: + @instruction[+2] true 0 1 + """ + %stack = alloc_stack $C + store %1 to [init] %stack : $*C + store %2 to [assign] %stack : $*C + unreachable + +bb3: + %99 = tuple() + return %99 : $() +} + +// A dead-end block with a def can still be a boundary edge. This can +// only happen in OSSA with incomplete lifetimes. +// +// CHECK-LABEL: testMultiDefDeadDefBoundaryEdge: fieldsensitive-multidefuse-liverange +// CHECK: FieldSensitive MultiDef lifetime analysis: +// CHECK: def in range [0, 1) instruction: store {{%[^,]+}} to [init] [[STACK:%[^,]+]] : +// CHECK: def in range [0, 1) instruction: store {{%[^,]+}} to [assign] [[STACK]] +// CHECK: bb0: LiveOut +// CHECK: bb1: LiveWithin +// CHECK: bb2: LiveWithin +// CHECK: last user: destroy_addr [[STACK]] +// CHECK-NEXT: at 1 +// CHECK-NEXT: boundary edge: bb1 +// CHECK-NEXT: at 1 +// CHECK-NEXT: dead def: store {{%[^,]+}} to [assign] [[STACK]] +// CHECK-NEXT: at 1 +sil [ossa] @testMultiDefDeadDefBoundaryEdge : $@convention(thin) () -> () { +bb0: + %getC = function_ref @getC : $@convention(thin) () -> (@owned C) + %stack = alloc_stack $C + %c = apply %getC() : $@convention(thin) () -> (@owned C) + store %c to [init] %stack : $*C + test_specification """ + fieldsensitive-multidefuse-liverange + @instruction[-3] + defs: + @instruction[-1] 0 1 + @block[1].instruction[1] 0 1 + uses: + @block[2].instruction[0] true 0 1 + """ + cond_br undef, bb1, bb3 + +bb1: + %c2 = apply %getC() : $@convention(thin) () -> (@owned C) + store %c2 to [assign] %stack : $*C + unreachable + +bb3: + destroy_addr %stack : $*C + dealloc_stack %stack : $*C + %99 = tuple() + return %99 : $() +} diff --git a/test/stdlib/Observation/ObservableDidSetWillSet.swift b/test/stdlib/Observation/ObservableDidSetWillSet.swift new file mode 100644 index 0000000000000..6004bdc56ce4e --- /dev/null +++ b/test/stdlib/Observation/ObservableDidSetWillSet.swift @@ -0,0 +1,44 @@ +// REQUIRES: swift_swift_parser, executable_test + +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -enable-experimental-feature InitAccessors -enable-experimental-feature Macros -Xfrontend -plugin-path -Xfrontend %swift-host-lib-dir/plugins) | %FileCheck %s + +// Asserts is required for '-enable-experimental-feature InitAccessors'. +// REQUIRES: asserts + +// REQUIRES: observation +// REQUIRES: concurrency +// REQUIRES: objc_interop +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +import _Observation + +@Observable +public class Model { + public enum State { + case initializing + case running + case complete + } + + public var state: State = .initializing { + willSet { + print("new state=\(String(describing: newValue))") + } + + didSet { + guard oldValue != state else { return } + print("old state=\(String(describing: oldValue))") + } + } +} + + +let m = Model() + +// CHECK: new state=running +// CHECK: old state=initializing +m.state = .running +// CHECK: new state=complete +// CHECK: old state=running +m.state = .complete diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 2e3d5a4d97a43..f1831349c9b6d 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -2622,6 +2622,8 @@ swift-enable-reflection=0 swift-stdlib-reflection-metadata=debugger-only swift-stdlib-stable-abi=0 swift-stdlib-has-dladdr=0 +swift-stdlib-has-dlsym=0 +swift-stdlib-has-filesystem=0 swift-stdlib-supports-backtrace-reporting=0 swift-stdlib-has-darwin-libmalloc=0 swift-stdlib-has-asl=0 diff --git a/utils/build-script-impl b/utils/build-script-impl index b7907324ac03c..b51ec47fc83c6 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -208,6 +208,8 @@ KNOWN_SETTINGS=( swift-enable-reflection "1" "whether to support reflection and mirrors" swift-stdlib-reflection-metadata "enabled" "whether to build stdlib with runtime metadata (valid options are 'enabled', 'disabled' and 'debugger-only')" swift-stdlib-has-dladdr "1" "whether to build stdlib assuming the runtime environment provides dladdr API" + swift-stdlib-has-dlsym "1" "whether to build stdlib assuming the runtime environment provides the dlsym API" + swift-stdlib-has-filesystem "1" "whether to build stdlib assuming the runtime environment provides a filesystem" swift-stdlib-supports-backtrace-reporting "" "whether to build stdlib assuming the runtime environment provides the backtrace(3) API, if not set defaults to true on all platforms except for Cygwin, Haiku and wasm" swift-runtime-static-image-inspection "0" "whether to build stdlib assuming the runtime environment only supports a single runtime image with Swift code" swift-threading-package "" "override the threading package for the host build; this is either a single package or a semicolon-separated list of sdk:package pairs. Valid packages are empty string (no override), 'pthreads', 'darwin', 'linux', 'win32', 'c11', 'none'" @@ -1832,6 +1834,8 @@ for host in "${ALL_HOSTS[@]}"; do -DSWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY:BOOL=$(true_false "${SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY}") -DSWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS:BOOL=$(true_false "${SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS}") -DSWIFT_STDLIB_HAS_DLADDR:BOOL=$(true_false "${SWIFT_STDLIB_HAS_DLADDR}") + -DSWIFT_STDLIB_HAS_DLSYM:BOOL=$(true_false "${SWIFT_STDLIB_HAS_DLSYM}") + -DSWIFT_STDLIB_HAS_FILESYSTEM:BOOL=$(true_false "${SWIFT_STDLIB_HAS_FILESYSTEM}") -DSWIFT_RUNTIME_STATIC_IMAGE_INSPECTION:BOOL=$(true_false "${SWIFT_RUNTIME_STATIC_IMAGE_INSPECTION}") -DSWIFT_STDLIB_OS_VERSIONING:BOOL=$(true_false "${SWIFT_STDLIB_OS_VERSIONING}") -DSWIFT_STDLIB_HAS_COMMANDLINE:BOOL=$(true_false "${SWIFT_STDLIB_HAS_COMMANDLINE}") diff --git a/utils/check_freestanding_dependencies.py b/utils/check_freestanding_dependencies.py index f30142779d2ca..93ff06f8cd890 100755 --- a/utils/check_freestanding_dependencies.py +++ b/utils/check_freestanding_dependencies.py @@ -56,7 +56,6 @@ "_posix_memalign", "_putc", "_read", "_realloc", "_snprintf", "_strchr", "_strcmp", "_strdup", "_strlen", "_strncmp", "_strtod", "_strtof", "_strtol", "_strtold", "_vprintf", "_vsnprintf", "_write", - "_stat", "_stat$INODE64", ] + cxx_dependencies + math_dependencies vendor_apple_specific_dependencies = [ "___stack_chk_fail", "___stack_chk_guard", diff --git a/utils/swift_build_support/swift_build_support/products/minimalstdlib.py b/utils/swift_build_support/swift_build_support/products/minimalstdlib.py index 8d70554d6fe10..332049888e685 100644 --- a/utils/swift_build_support/swift_build_support/products/minimalstdlib.py +++ b/utils/swift_build_support/swift_build_support/products/minimalstdlib.py @@ -140,7 +140,9 @@ def build(self, host_target): self.cmake_options.define( 'SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC:BOOL', 'FALSE') self.cmake_options.define('SWIFT_STDLIB_HAS_DLADDR:BOOL', 'FALSE') + self.cmake_options.define('SWIFT_STDLIB_HAS_DLSYM:BOOL', 'FALSE') self.cmake_options.define('SWIFT_STDLIB_HAS_ENVIRON:BOOL', 'FALSE') + self.cmake_options.define('SWIFT_STDLIB_HAS_FILESYSTEM:BOOL', 'FALSE') self.cmake_options.define('SWIFT_STDLIB_HAS_LOCALE:BOOL', 'FALSE') self.cmake_options.define('SWIFT_STDLIB_HAS_STDIN:BOOL', 'FALSE') self.cmake_options.define(