From 60bf5447ade2aa0a39fac91363c62f7a078d21da Mon Sep 17 00:00:00 2001 From: Anthony Latsis Date: Mon, 6 Feb 2023 22:40:57 +0300 Subject: [PATCH 01/98] [docs] GettingStarted: Bump required CMake version for `--xcode` once more 2.24.2 is the oldest version that didn't reproduce the code signing issue from https://github.com/apple/swift/issues/62023 with the latest Xcode release (14.2). --- docs/HowToGuides/GettingStarted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/HowToGuides/GettingStarted.md b/docs/HowToGuides/GettingStarted.md index d35fc463b9bbb..4977b3079f672 100644 --- a/docs/HowToGuides/GettingStarted.md +++ b/docs/HowToGuides/GettingStarted.md @@ -187,7 +187,7 @@ toolchain as a one-off, there are a couple of differences: ### Spot check dependencies -* Run `cmake --version`; this should be at least 3.19.6 (3.22.2 if you want to generate an Xcode project on macOS). +* Run `cmake --version`; this should be at least 3.19.6 (3.24.2 if you want to use Xcode for editing on macOS). * Run `python3 --version`; check that this succeeds. * Run `ninja --version`; check that this succeeds. * If you installed and want to use Sccache: Run `sccache --version`; check From fc3ebd1995645be1cbda0f07a18f83b2dd74353b Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Mon, 6 Feb 2023 14:54:24 -0800 Subject: [PATCH 02/98] Partially re-enable some @_objcImpl tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These were disabled becuase they didn’t work properly in bots testing iOS or other platforms. Re-enable them for macOS until they can be fully repaired. --- test/IRGen/objc_implementation.swift | 3 ++- test/Interpreter/objc_implementation_swift_client.swift | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/IRGen/objc_implementation.swift b/test/IRGen/objc_implementation.swift index eaef86e6e590d..e6f068c5bb172 100644 --- a/test/IRGen/objc_implementation.swift +++ b/test/IRGen/objc_implementation.swift @@ -1,4 +1,5 @@ -// REQUIRES: rdar101420862 +// Test doesn't pass on all platforms (rdar://101420862) +// REQUIRES: OS=macosx // RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -I %S/Inputs/abi -F %clang-importer-sdk-path/frameworks %s -import-objc-header %S/Inputs/objc_implementation.h -emit-ir > %t.ir // RUN: %FileCheck --input-file %t.ir %s diff --git a/test/Interpreter/objc_implementation_swift_client.swift b/test/Interpreter/objc_implementation_swift_client.swift index 962497452bc9f..89cc0d2f7498e 100644 --- a/test/Interpreter/objc_implementation_swift_client.swift +++ b/test/Interpreter/objc_implementation_swift_client.swift @@ -1,4 +1,5 @@ -// REQUIRES: rdar101543397 +// Test doesn't pass on all platforms (rdar://101543397) +// REQUIRES: OS=macosx // // Build objc_implementation.framework From aaf497e0db332b57768744013c1c7bbc5e2c8f27 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 6 Feb 2023 15:01:35 -0800 Subject: [PATCH 03/98] Add the test that I forgot to add in 3c976e9d79986de3bd6ecf313b1d746ac125c3ce. --- .../moveonly_nonfinal_class_restriction.swift | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 test/Sema/moveonly_nonfinal_class_restriction.swift diff --git a/test/Sema/moveonly_nonfinal_class_restriction.swift b/test/Sema/moveonly_nonfinal_class_restriction.swift new file mode 100644 index 0000000000000..fbca98436fb9c --- /dev/null +++ b/test/Sema/moveonly_nonfinal_class_restriction.swift @@ -0,0 +1,33 @@ +// RUN: %target-typecheck-verify-swift -enable-experimental-move-only + +// This test validates that we do not allow for non-final classes to contain +// move only fields. This is just a temporary measure. + +@_moveOnly +struct S { + var i: Int = 5 +} + +class C { + var s = S() // expected-error {{non-final classes containing move only fields is not yet supported}} + let s1 = S() // expected-error {{non-final classes containing move only fields is not yet supported}} + var s2: S // expected-error {{non-final classes containing move only fields is not yet supported}} + let s3: S // expected-error {{non-final classes containing move only fields is not yet supported}} + + init() { + s2 = S() + s3 = S() + } +} + +final class C2 { + var s = S() + let s1 = S() + var s2: S + let s3: S + + init() { + s2 = S() + s3 = S() + } +} From 6ee695a395e342d4f2f660b7d667f59dab763ced Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 2 Dec 2022 14:31:50 +0100 Subject: [PATCH 04/98] [CursorInfo] Implement a few expression references as solver-based MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implements cursor info resolving for a few expression types using the constraint system. This allows us to detect ambiguous results – we cannot deliver them yet but that will be done in a follow-up PR. --- lib/IDE/CursorInfo.cpp | 154 +++++++++++++++++- .../include/SourceKit/Core/LangSupport.h | 14 +- 2 files changed, 161 insertions(+), 7 deletions(-) diff --git a/lib/IDE/CursorInfo.cpp b/lib/IDE/CursorInfo.cpp index 58084529425ea..99defbcf52c19 100644 --- a/lib/IDE/CursorInfo.cpp +++ b/lib/IDE/CursorInfo.cpp @@ -16,6 +16,7 @@ #include "swift/AST/GenericEnvironment.h" #include "swift/AST/NameLookup.h" #include "swift/AST/USRGeneration.h" +#include "swift/IDE/SelectedOverloadInfo.h" #include "swift/IDE/TypeCheckCompletionCallback.h" #include "swift/Parse/IDEInspectionCallbacks.h" #include "swift/Sema/ConstraintSystem.h" @@ -51,9 +52,11 @@ void typeCheckDeclAndParentClosures(ValueDecl *VD) { TypeCheckASTNodeAtLocContext::declContext(VD->getDeclContext()), VD->getLoc()); if (auto VarD = dyn_cast(VD)) { - // Type check any attached property wrappers so the annotated declaration - // can refer to their USRs. - (void)VarD->getPropertyWrapperBackingPropertyType(); + if (VarD->hasAttachedPropertyWrapper()) { + // Type check any attached property wrappers so the annotated declaration + // can refer to their USRs. + (void)VarD->getPropertyWrapperBackingPropertyType(); + } // Visit emitted accessors so we generated accessors from property wrappers. VarD->visitEmittedAccessors([&](AccessorDecl *accessor) {}); } @@ -61,7 +64,7 @@ void typeCheckDeclAndParentClosures(ValueDecl *VD) { // MARK: - NodeFinderResults -enum class NodeFinderResultKind { Decl }; +enum class NodeFinderResultKind { Decl, Expr }; class NodeFinderResult { NodeFinderResultKind Kind; @@ -87,6 +90,24 @@ class NodeFinderDeclResult : public NodeFinderResult { } }; +class NodeFinderExprResult : public NodeFinderResult { + Expr *E; + /// The \c DeclContext in which \c E occurs. + DeclContext *DC; + +public: + NodeFinderExprResult(Expr *E, DeclContext *DC) + : NodeFinderResult(NodeFinderResultKind::Expr), E(E), DC(DC) {} + + Expr *getExpr() const { return E; } + + DeclContext *getDeclContext() const { return DC; } + + static bool classof(const NodeFinderResult *Res) { + return Res->getKind() == NodeFinderResultKind::Expr; + } +}; + // MARK: - NodeFinder /// Walks the AST, looking for a node at \c LocToResolve. While walking the @@ -192,6 +213,23 @@ class NodeFinder : ASTWalker { } } + if (E->getLoc() != LocToResolve) { + return Action::Continue(E); + } + + switch (E->getKind()) { + case ExprKind::DeclRef: + case ExprKind::UnresolvedDot: + case ExprKind::UnresolvedDeclRef: { + assert(Result == nullptr); + Result = + std::make_unique(E, getCurrentDeclContext()); + return Action::Stop(); + } + default: + break; + } + return Action::Continue(E); } @@ -215,6 +253,57 @@ class NodeFinder : ASTWalker { } }; +// MARK: - Solver-based expression analysis + +class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback { +public: + struct CursorInfoDeclReference { + /// If the referenced declaration is a member reference, the type of the + /// member's base, otherwise \c null. + Type BaseType; + /// Whether the reference is dynamic (see \c ide::isDynamicRef) + bool IsDynamicRef; + /// The declaration that is being referenced. Will never be \c nullptr. + ValueDecl *ReferencedDecl; + }; + +private: + /// The expression for which we want to provide cursor info results. + Expr *ResolveExpr; + + SmallVector Results; + + void sawSolutionImpl(const Solution &S) override { + auto &CS = S.getConstraintSystem(); + + auto Locator = CS.getConstraintLocator(ResolveExpr); + auto CalleeLocator = S.getCalleeLocator(Locator); + auto OverloadInfo = getSelectedOverloadInfo(S, CalleeLocator); + if (!OverloadInfo.Value) { + // We could not resolve the referenced declaration. Skip the solution. + return; + } + + bool IsDynamicRef = false; + auto BaseLocator = + CS.getConstraintLocator(Locator, ConstraintLocator::MemberRefBase); + if (auto BaseExpr = + simplifyLocatorToAnchor(BaseLocator).dyn_cast()) { + IsDynamicRef = + ide::isDynamicRef(BaseExpr, OverloadInfo.Value, + [&S](Expr *E) { return S.getResolvedType(E); }); + } + + Results.push_back({OverloadInfo.BaseTy, IsDynamicRef, OverloadInfo.Value}); + } + +public: + CursorInfoTypeCheckSolutionCallback(Expr *ResolveExpr) + : ResolveExpr(ResolveExpr) {} + + ArrayRef getResults() const { return Results; } +}; + // MARK: - CursorInfoDoneParsingCallback class CursorInfoDoneParsingCallback : public IDEInspectionCallbacks { @@ -242,6 +331,59 @@ class CursorInfoDoneParsingCallback : public IDEInspectionCallbacks { return CursorInfo; } + std::unique_ptr + getExprResult(NodeFinderExprResult *ExprResult, SourceFile *SrcFile, + NodeFinder &Finder) const { + Expr *E = ExprResult->getExpr(); + DeclContext *DC = ExprResult->getDeclContext(); + + // Type check the statemnt containing E and listen for solutions. + CursorInfoTypeCheckSolutionCallback Callback(E); + llvm::SaveAndRestore CompletionCollector( + DC->getASTContext().SolutionCallback, &Callback); + typeCheckASTNodeAtLoc(TypeCheckASTNodeAtLocContext::declContext(DC), + E->getLoc()); + + if (Callback.getResults().empty()) { + // No results. + return nullptr; + } + + for (auto Info : Callback.getResults()) { + // Type check the referenced decls so that all their parent closures are + // type-checked (see comment in typeCheckDeclAndParentClosures). + typeCheckDeclAndParentClosures(Info.ReferencedDecl); + } + + if (Callback.getResults().size() != 1) { + // FIXME: We need to be able to report multiple results. + return nullptr; + } + + // Deliver results + + auto Res = Callback.getResults()[0]; + auto CursorInfo = std::make_unique( + ResolvedCursorInfo(SrcFile), Res.ReferencedDecl, /*CtorTyRef=*/nullptr, + /*ExtTyRef=*/nullptr, /*IsRef=*/true, /*Ty=*/Type(), + /*ContainerType=*/Res.BaseType); + CursorInfo->setLoc(RequestedLoc); + CursorInfo->setIsDynamic(Res.IsDynamicRef); + if (Res.IsDynamicRef && Res.BaseType) { + if (auto ReceiverType = Res.BaseType->getAnyNominal()) { + CursorInfo->setReceiverTypes({ReceiverType}); + } else if (auto MT = Res.BaseType->getAs()) { + // Look through metatypes to get the nominal type decl. + if (auto ReceiverType = MT->getInstanceType()->getAnyNominal()) { + CursorInfo->setReceiverTypes({ReceiverType}); + } + } + } + CursorInfo->setShorthandShadowedDecls( + Finder.getShorthandShadowedDecls(Res.ReferencedDecl)); + return CursorInfo; + } + void doneParsing(SourceFile *SrcFile) override { if (!SrcFile) { return; @@ -258,6 +400,10 @@ class CursorInfoDoneParsingCallback : public IDEInspectionCallbacks { CursorInfo = getDeclResult(cast(Result.get()), SrcFile, Finder); break; + case NodeFinderResultKind::Expr: + CursorInfo = getExprResult(cast(Result.get()), + SrcFile, Finder); + break; } if (Result) { Consumer.handleResults(*CursorInfo); diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index 918b12229f374..fba1a5cb63090 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -519,7 +519,8 @@ struct CursorSymbolInfo { llvm::Optional ParentNameOffset; - void print(llvm::raw_ostream &OS, std::string Indentation) const { + void print(llvm::raw_ostream &OS, std::string Indentation, + bool ForSolverBasedCursorInfoVerification = false) const { OS << Indentation << "CursorSymbolInfo" << '\n'; OS << Indentation << " Kind: " << Kind.getName() << '\n'; OS << Indentation << " DeclarationLang: " << DeclarationLang.getName() @@ -528,7 +529,14 @@ struct CursorSymbolInfo { OS << Indentation << " USR: " << USR << '\n'; OS << Indentation << " TypeName: " << TypeName << '\n'; OS << Indentation << " TypeUSR: " << TypeUSR << '\n'; - OS << Indentation << " ContainerTypeUSR: " << ContainerTypeUSR << '\n'; + // The ContainerTypeUSR varies too much between the solver-based and + // AST-based implementation. A few manual inspections showed that the + // solver-based container is usually more correct than the old. Instead of + // fixing the AST-based container type computation, exclude the container + // type from the verification. + if (!ForSolverBasedCursorInfoVerification) { + OS << Indentation << " ContainerTypeUSR: " << ContainerTypeUSR << '\n'; + } OS << Indentation << " DocComment: " << DocComment << '\n'; OS << Indentation << " GroupName: " << GroupName << '\n'; OS << Indentation << " LocalizationKey: " << LocalizationKey << '\n'; @@ -600,7 +608,7 @@ struct CursorInfoData { OS << Indentation << "CursorInfoData" << '\n'; OS << Indentation << " Symbols:" << '\n'; for (auto Symbol : Symbols) { - Symbol.print(OS, Indentation + " "); + Symbol.print(OS, Indentation + " ", ForSolverBasedCursorInfoVerification); } OS << Indentation << " AvailableActions:" << '\n'; for (auto AvailableAction : AvailableActions) { From fcc5d98f1cb2bac42f2af3d7bd127b5016ce99cf Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Tue, 7 Feb 2023 10:55:51 +0100 Subject: [PATCH 05/98] [CursorInfo] Deliver results from solver-based cursor info Running the SourceKit stress tester with verification of solver-based cursor info returned quite a few differences but in all of them, the old AST-based implementation was actually incorrect. So, instead of verifying the results, deliver the results from solver-baesd cursor info and only fall back to AST-based cursor info if the solver-based implementation returned no results. rdar://103369449 --- .../CompileNotifications/cursor-info.swift | 5 +- .../CursorInfo/cursor_in_pound_if.swift | 3 +- test/SourceKit/CursorInfo/discriminator.swift | 6 +- .../include/SourceKit/Core/LangSupport.h | 25 +-- .../lib/SwiftLang/SwiftLangSupport.h | 1 - .../lib/SwiftLang/SwiftSourceDocInfo.cpp | 174 ++++-------------- .../tools/sourcekitd-test/sourcekitd-test.cpp | 2 - .../tools/sourcekitd/lib/Service/Requests.cpp | 5 +- .../SourceKit/SwiftLang/CursorInfoTest.cpp | 3 - utils/gyb_sourcekit_support/UIDs.py | 1 - 10 files changed, 50 insertions(+), 175 deletions(-) diff --git a/test/SourceKit/CompileNotifications/cursor-info.swift b/test/SourceKit/CompileNotifications/cursor-info.swift index c7ba760d57b56..078d0f315bb24 100644 --- a/test/SourceKit/CompileNotifications/cursor-info.swift +++ b/test/SourceKit/CompileNotifications/cursor-info.swift @@ -1,6 +1,3 @@ -// rdar://103369449 -// REQUIRES: asserts - // RUN: %sourcekitd-test -req=track-compiles == -req=cursor %s -offset=0 -- %s | %FileCheck %s -check-prefix=COMPILE_1 --enable-yaml-compatibility // COMPILE_1: // COMPILE_1: { @@ -12,7 +9,7 @@ // COMPILE_1: key.notification: source.notification.compile-did-finish, // COMPILE_1: key.compileid: [[CID1]] // COMPILE_1: } -// FIXME: Once we switch to only run solver-based cursor info, we should only receive a single compile notification +// FIXME: Once all cursor info kinds are migrated to solver-based and we remove the fallback path to AST-based cursor info, we should only receive a single compile notification // COMPILE_1: { // COMPILE_1: key.notification: source.notification.compile-will-start, // COMPILE_1: key.filepath: "SOURCE_DIR{{.*}}cursor-info.swift", diff --git a/test/SourceKit/CursorInfo/cursor_in_pound_if.swift b/test/SourceKit/CursorInfo/cursor_in_pound_if.swift index ea796273edeb9..4cdc98a166197 100644 --- a/test/SourceKit/CursorInfo/cursor_in_pound_if.swift +++ b/test/SourceKit/CursorInfo/cursor_in_pound_if.swift @@ -8,6 +8,5 @@ func foo() { let xxx = "hello" #endif } -// TODO: Once we switch to use the solver-based cursor info implementation, we also receive results for the int case -// CHECK-INT: Unable to resolve cursor info +// CHECK-INT: let xxx: Int // CHECK-STR: let xxx: String diff --git a/test/SourceKit/CursorInfo/discriminator.swift b/test/SourceKit/CursorInfo/discriminator.swift index 661858aa19fab..431887dfddebe 100644 --- a/test/SourceKit/CursorInfo/discriminator.swift +++ b/test/SourceKit/CursorInfo/discriminator.swift @@ -35,12 +35,12 @@ func testNestedClosures() { } func testReuseAST(bytes: Int) { - // RUN: %sourcekitd-test -req=cursor -pos=%(line + 2):7 -req-opts=verifysolverbasedcursorinfo=1 %s -- %s == \ - // RUN: -req=cursor -pos=%(line + 2):7 -req-opts=verifysolverbasedcursorinfo=1 %s -- %s | %FileCheck %s --check-prefix=REUSE_AST + // RUN: %sourcekitd-test -req=cursor -pos=%(line + 2):7 %s -- %s == \ + // RUN: -req=cursor -pos=%(line + 2):7 %s -- %s | %FileCheck %s --check-prefix=REUSE_AST let size = 3 var bytes = 6 // REUSE_AST: source.lang.swift.decl.var.local (40:7-40:11) // REUSE_AST: s:13discriminator12testReuseAST5bytesySi_tF4sizeL_Sivp // REUSE_AST: source.lang.swift.decl.var.local (41:7-41:12) // REUSE_AST: s:13discriminator12testReuseAST5bytesySi_tFACL0_Sivp -} \ No newline at end of file +} diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index fba1a5cb63090..90003b6a7a259 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -519,8 +519,7 @@ struct CursorSymbolInfo { llvm::Optional ParentNameOffset; - void print(llvm::raw_ostream &OS, std::string Indentation, - bool ForSolverBasedCursorInfoVerification = false) const { + void print(llvm::raw_ostream &OS, std::string Indentation) const { OS << Indentation << "CursorSymbolInfo" << '\n'; OS << Indentation << " Kind: " << Kind.getName() << '\n'; OS << Indentation << " DeclarationLang: " << DeclarationLang.getName() @@ -529,14 +528,7 @@ struct CursorSymbolInfo { OS << Indentation << " USR: " << USR << '\n'; OS << Indentation << " TypeName: " << TypeName << '\n'; OS << Indentation << " TypeUSR: " << TypeUSR << '\n'; - // The ContainerTypeUSR varies too much between the solver-based and - // AST-based implementation. A few manual inspections showed that the - // solver-based container is usually more correct than the old. Instead of - // fixing the AST-based container type computation, exclude the container - // type from the verification. - if (!ForSolverBasedCursorInfoVerification) { - OS << Indentation << " ContainerTypeUSR: " << ContainerTypeUSR << '\n'; - } + OS << Indentation << " ContainerTypeUSR: " << ContainerTypeUSR << '\n'; OS << Indentation << " DocComment: " << DocComment << '\n'; OS << Indentation << " GroupName: " << GroupName << '\n'; OS << Indentation << " LocalizationKey: " << LocalizationKey << '\n'; @@ -600,23 +592,17 @@ struct CursorInfoData { /// Whether the ASTContext was reused for this cursor info. bool DidReuseAST = false; - /// If \p ForSolverBasedCursorInfoVerification is \c true, fields that are - /// acceptable to differ between the AST-based and the solver-based result, - /// will be excluded. - void print(llvm::raw_ostream &OS, std::string Indentation, - bool ForSolverBasedCursorInfoVerification = false) const { + void print(llvm::raw_ostream &OS, std::string Indentation) const { OS << Indentation << "CursorInfoData" << '\n'; OS << Indentation << " Symbols:" << '\n'; for (auto Symbol : Symbols) { - Symbol.print(OS, Indentation + " ", ForSolverBasedCursorInfoVerification); + Symbol.print(OS, Indentation + " "); } OS << Indentation << " AvailableActions:" << '\n'; for (auto AvailableAction : AvailableActions) { AvailableAction.print(OS, Indentation + " "); } - if (!ForSolverBasedCursorInfoVerification) { - OS << Indentation << "DidReuseAST: " << DidReuseAST << '\n'; - } + OS << Indentation << "DidReuseAST: " << DidReuseAST << '\n'; } SWIFT_DEBUG_DUMP { print(llvm::errs(), ""); } @@ -973,7 +959,6 @@ class LangSupport { bool SymbolGraph, bool CancelOnSubsequentRequest, ArrayRef Args, Optional vfsOptions, SourceKitCancellationToken CancellationToken, - bool VerifySolverBasedCursorInfo, std::function &)> Receiver) = 0; virtual void diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 82f145df163cc..4c162abb0e80c 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -637,7 +637,6 @@ class SwiftLangSupport : public LangSupport { ArrayRef Args, Optional vfsOptions, SourceKitCancellationToken CancellationToken, - bool VerifySolverBasedCursorInfo, std::function &)> Receiver) override; diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp index 13ce18bf015df..70befbc3614c4 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp @@ -1913,7 +1913,6 @@ void SwiftLangSupport::getCursorInfo( bool SymbolGraph, bool CancelOnSubsequentRequest, ArrayRef Args, Optional vfsOptions, SourceKitCancellationToken CancellationToken, - bool VerifySolverBasedCursorInfo, std::function &)> Receiver) { std::string error; @@ -1963,141 +1962,46 @@ void SwiftLangSupport::getCursorInfo( return; } - /// Counts how many symbols \p Res contains. - auto ResultCount = [](const RequestResult &Res) -> size_t { - if (Res.isValue()) { - return Res.value().Symbols.size(); - } else { - return 0; - } - }; - - /// Serializes \c CursorInfoData into a string. - auto ResultDescription = - [](const RequestResult &Res) -> std::string { - if (Res.isCancelled()) { - return "cancelled"; - } else if (Res.isError()) { - return Res.getError().str(); - } else { - std::string Description; - llvm::raw_string_ostream OS(Description); - Res.value().print(OS, /*Indentation=*/"", - /*ForSolverBasedCursorInfoVerification=*/true); - return OS.str(); - } - }; - - // Currently, we only verify that the solver-based cursor implementation - // produces the same results as the AST-based implementation. Only enable it - // in assert builds for now. - - // If solver based completion is enabled, a string description of the cursor - // info result produced by the solver-based implementation. Once the AST-based - // result is produced, we verify that the solver-based result matches the - // AST-based result. - std::string SolverBasedResultDescription; - size_t SolverBasedResultCount = 0; - bool SolverBasedReusedAST = false; - if (VerifySolverBasedCursorInfo) { - std::string InputFileError; - llvm::SmallString<64> RealInputFilePath; - fileSystem->getRealPath(InputFile, RealInputFilePath); - std::unique_ptr UnresolvedInputFile = - getASTManager()->getMemoryBuffer(RealInputFilePath, fileSystem, - InputFileError); - if (UnresolvedInputFile) { - auto SolverBasedReceiver = [&](const RequestResult &Res) { - SolverBasedResultCount = ResultCount(Res); - SolverBasedResultDescription = ResultDescription(Res); - if (Res.isValue()) { - SolverBasedReusedAST = Res.value().DidReuseAST; - } - }; - - CompilerInvocation CompInvok; - Invok->applyTo(CompInvok); - - performWithParamsToCompletionLikeOperation( - UnresolvedInputFile.get(), Offset, - /*InsertCodeCompletionToken=*/false, Args, fileSystem, - CancellationToken, - [&](CancellableResult ParmsResult) { - ParmsResult.mapAsync( - [&](auto &Params, auto DeliverTransformed) { - getIDEInspectionInstance()->cursorInfo( - Params.Invocation, Args, fileSystem, - Params.completionBuffer, Offset, Params.DiagC, - Params.CancellationFlag, DeliverTransformed); - }, - [&](auto Result) { - deliverCursorInfoResults(SolverBasedReceiver, Result, *this, - CompInvok, Actionables, SymbolGraph); - }); - }); - } - } - - /// If the solver-based implementation returned a different result than the - /// AST-based implementation, return an error message, describing the - /// difference. Otherwise, return an empty string. - auto VerifySolverBasedResult = - [ResultCount, ResultDescription, SolverBasedResultCount, - SolverBasedResultDescription]( - const RequestResult &ASTBasedResult) -> std::string { - if (SolverBasedResultDescription.empty()) { - // We did not run the solver-based implementation. Nothing to check. - return ""; - } - auto ASTResultDescription = ResultDescription(ASTBasedResult); - auto ASTResultCount = ResultCount(ASTBasedResult); - if (ASTResultCount == 0 && SolverBasedResultCount > 0) { - // The AST-based implementation did not return any results but the - // solver-based did. That's an improvement. Success. - return ""; - } - if (SolverBasedResultDescription == ASTResultDescription) { - // The solver-based and AST-based implementation produced the same - // results. Success. - return ""; - } - // The solver-based implementation differed from the AST-based - // implementation. Report a failure. - std::string ErrorMessage; - llvm::raw_string_ostream OS(ErrorMessage); - OS << "The solver-based implementation returned a different result than " - "the AST-based implementation:\n"; - OS << SolverBasedResultDescription << "\n"; - OS << "===== (solver-based vs. AST-based) =====\n"; - OS << ASTResultDescription << "\n"; - return OS.str(); - }; - - // Thunk around `Receiver` that, if solver-based cursor info is enabled, - // verifies that the solver-based cursor info result matches the AST-based - // result. - auto ReceiverThunk = - [Receiver, VerifySolverBasedResult, - SolverBasedReusedAST](const RequestResult &Res) { - auto VerificationError = VerifySolverBasedResult(Res); - if (VerificationError.empty()) { - if (Res.isValue()) { - // Report whether the solver-based implemenatation reused the AST so - // we can check it in test cases. - auto Value = Res.value(); - Value.DidReuseAST = SolverBasedReusedAST; - Receiver(RequestResult::fromResult(Value)); - } else { - Receiver(Res); - } - } else { - Receiver(RequestResult::fromError(VerificationError)); - } - }; + bool SolverBasedProducedResult = false; + std::string InputFileError; + llvm::SmallString<64> RealInputFilePath; + fileSystem->getRealPath(InputFile, RealInputFilePath); + std::unique_ptr UnresolvedInputFile = + getASTManager()->getMemoryBuffer(RealInputFilePath, fileSystem, + InputFileError); + if (UnresolvedInputFile) { + auto SolverBasedReceiver = [&](const RequestResult &Res) { + SolverBasedProducedResult = true; + Receiver(Res); + }; - resolveCursor(*this, InputFile, Offset, Length, Actionables, SymbolGraph, - Invok, /*TryExistingAST=*/true, CancelOnSubsequentRequest, - fileSystem, CancellationToken, ReceiverThunk); + CompilerInvocation CompInvok; + Invok->applyTo(CompInvok); + + performWithParamsToCompletionLikeOperation( + UnresolvedInputFile.get(), Offset, + /*InsertCodeCompletionToken=*/false, Args, fileSystem, + CancellationToken, + [&](CancellableResult ParmsResult) { + ParmsResult.mapAsync( + [&](auto &Params, auto DeliverTransformed) { + getIDEInspectionInstance()->cursorInfo( + Params.Invocation, Args, fileSystem, + Params.completionBuffer, Offset, Params.DiagC, + Params.CancellationFlag, DeliverTransformed); + }, + [&](auto Result) { + deliverCursorInfoResults(SolverBasedReceiver, Result, *this, + CompInvok, Actionables, SymbolGraph); + }); + }); + } + + if (!SolverBasedProducedResult) { + resolveCursor(*this, InputFile, Offset, Length, Actionables, SymbolGraph, + Invok, /*TryExistingAST=*/true, CancelOnSubsequentRequest, + fileSystem, CancellationToken, Receiver); + } } void SwiftLangSupport::getDiagnostics( diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index db4aec98279a0..14fec387f03c0 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -772,8 +772,6 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { } else { sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset); } - sourcekitd_request_dictionary_set_int64(Req, KeyVerifySolverBasedCursorInfo, - true); addRequestOptionsDirect(Req, Opts); break; case SourceKitRequest::RangeInfo: { diff --git a/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp index aa8e9e3c4bf83..b98c894bced7c 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp @@ -1393,13 +1393,10 @@ handleRequestCursorInfo(const RequestDict &Req, Req.getInt64(KeyRetrieveRefactorActions, Actionables, /*isOptional=*/true); int64_t SymbolGraph = false; Req.getInt64(KeyRetrieveSymbolGraph, SymbolGraph, /*isOptional=*/true); - int64_t VerifySolverBasedCursorInfo = false; - Req.getInt64(KeyVerifySolverBasedCursorInfo, VerifySolverBasedCursorInfo, - /*isOptional=*/true); return Lang.getCursorInfo( *SourceFile, Offset, Length, Actionables, SymbolGraph, CancelOnSubsequentRequest, Args, std::move(vfsOptions), - CancellationToken, VerifySolverBasedCursorInfo, + CancellationToken, [Rec](const RequestResult &Result) { reportCursorInfo(Result, Rec); }); diff --git a/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp b/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp index 622450b571a96..ff2fef79f1036 100644 --- a/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp +++ b/unittests/SourceKit/SwiftLang/CursorInfoTest.cpp @@ -166,7 +166,6 @@ class CursorInfoTest : public ::testing::Test { DocName, Offset, /*Length=*/0, /*Actionables=*/false, /*SymbolGraph=*/false, CancelOnSubsequentRequest, Args, /*vfsOptions=*/None, CancellationToken, - /*VerifySolverBasedCursorInfo=*/true, [&](const RequestResult &Result) { assert(!Result.isCancelled()); if (Result.isError()) { @@ -452,7 +451,6 @@ TEST_F(CursorInfoTest, CursorInfoCancelsPreviousRequest) { SlowDocName, SlowOffset, /*Length=*/0, /*Actionables=*/false, /*SymbolGraph=*/false, /*CancelOnSubsequentRequest=*/true, ArgsForSlow, /*vfsOptions=*/None, /*CancellationToken=*/nullptr, - /*VerifySolverBasedCursorInfo=*/true, [&](const RequestResult &Result) { EXPECT_TRUE(Result.isCancelled()); FirstCursorInfoSema.signal(); @@ -496,7 +494,6 @@ TEST_F(CursorInfoTest, CursorInfoCancellation) { SlowDocName, SlowOffset, /*Length=*/0, /*Actionables=*/false, /*SymbolGraph=*/false, /*CancelOnSubsequentRequest=*/false, ArgsForSlow, /*vfsOptions=*/None, /*CancellationToken=*/CancellationToken, - /*VerifySolverBasedCursorInfo=*/true, [&](const RequestResult &Result) { EXPECT_TRUE(Result.isCancelled()); CursorInfoSema.signal(); diff --git a/utils/gyb_sourcekit_support/UIDs.py b/utils/gyb_sourcekit_support/UIDs.py index 0cf5b11e5179b..c19e2e62fdc8a 100644 --- a/utils/gyb_sourcekit_support/UIDs.py +++ b/utils/gyb_sourcekit_support/UIDs.py @@ -189,7 +189,6 @@ def __init__(self, internal_name, external_name): KEY('OptimizeForIDE', 'key.optimize_for_ide'), KEY('RequiredBystanders', 'key.required_bystanders'), KEY('ReusingASTContext', 'key.reusingastcontext'), - KEY('VerifySolverBasedCursorInfo', 'key.verifysolverbasedcursorinfo'), KEY('CompletionMaxASTContextReuseCount', 'key.completion_max_astcontext_reuse_count'), KEY('CompletionCheckDependencyInterval', From c1599fede4d1568cbef1bfb4e9e9c0dfbb6e46dc Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Fri, 3 Feb 2023 15:08:12 -0800 Subject: [PATCH 06/98] Add -swift-module-file frontend flag for explicit Swift module dependencies --- include/swift/AST/DiagnosticsFrontend.def | 2 ++ include/swift/AST/SearchPathOptions.h | 4 ++++ .../swift/Frontend/ModuleInterfaceLoader.h | 3 ++- include/swift/Option/FrontendOptions.td | 4 ++++ lib/Frontend/CompilerInvocation.cpp | 24 +++++++++++++++++++ lib/Frontend/Frontend.cpp | 4 +++- lib/Frontend/ModuleInterfaceLoader.cpp | 16 +++++++++++++ 7 files changed, 55 insertions(+), 2 deletions(-) diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 47a0726993762..3b63919722fdf 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -110,6 +110,8 @@ ERROR(error_cannot_direct_cc1_pcm_build_in_mode,none, "'-direct-clang-cc1-module-build' only supported when building a PCM ('-emit-pcm')'", ()) ERROR(error_unsupported_option_argument,none, "unsupported argument '%1' to option '%0'", (StringRef, StringRef)) +ERROR(error_swift_module_file_requires_delimeter,none, + "-swift-module-file= argument requires format =, got: '%0'", (StringRef)) ERROR(error_immediate_mode_missing_stdlib,none, "could not load the swift standard library", ()) ERROR(error_immediate_mode_missing_library,none, diff --git a/include/swift/AST/SearchPathOptions.h b/include/swift/AST/SearchPathOptions.h index ab8b0d23ff3d5..e1d68dc50c2fa 100644 --- a/include/swift/AST/SearchPathOptions.h +++ b/include/swift/AST/SearchPathOptions.h @@ -355,6 +355,10 @@ class SearchPathOptions { /// A map of explicit Swift module information. std::string ExplicitSwiftModuleMap; + /// Module inputs specified with -swift-module-input, + /// + std::vector> ExplicitSwiftModuleInputs; + /// A map of placeholder Swift module dependency information. std::string PlaceholderDependencyModuleMap; diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index ee16da87208ca..757fbbe53708a 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -129,7 +129,7 @@ class SearchPathOptions; class CompilerInvocation; /// A ModuleLoader that loads explicitly built Swift modules specified via -/// -swift-module-file +/// -swift-module-file or modules found in class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase { explicit ExplicitSwiftModuleLoader(ASTContext &ctx, DependencyTracker *tracker, ModuleLoadingMode loadMode, @@ -164,6 +164,7 @@ class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase { create(ASTContext &ctx, DependencyTracker *tracker, ModuleLoadingMode loadMode, StringRef ExplicitSwiftModuleMap, + const std::vector> &ExplicitSwiftModuleInputs, bool IgnoreSwiftSourceInfoFile); /// Append visible module names to \p names. Note that names are possibly diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 6938ce5fa4cbc..a4294a76de8db 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -185,6 +185,10 @@ def disable_typo_correction : Flag<["-"], "disable-typo-correction">, def disable_implicit_swift_modules: Flag<["-"], "disable-implicit-swift-modules">, HelpText<"Disable building Swift modules implicitly by the compiler">; +def swift_module_file: Joined<["-"], "swift-module-file=">, + MetaVarName<"=">, + HelpText<"Specify Swift module input explicitly built from textual interface">; + def explicit_swift_module_map : Separate<["-"], "explicit-swift-module-map-file">, MetaVarName<"">, HelpText<"Specify a JSON file containing information of explicit Swift modules">; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 6e83660f1253c..58bf5e0dc902d 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1363,6 +1363,25 @@ static void ParseSymbolGraphArgs(symbolgraphgen::SymbolGraphOptions &Opts, Opts.IncludeClangDocs = false; } +static bool validateSwiftModuleFileArgumentAndAdd(const std::string &swiftModuleArgument, + DiagnosticEngine &Diags, + std::vector> &ExplicitSwiftModuleInputs) { + std::size_t foundDelimeterPos = swiftModuleArgument.find_first_of("="); + if (foundDelimeterPos == std::string::npos) { + Diags.diagnose(SourceLoc(), diag::error_swift_module_file_requires_delimeter, + swiftModuleArgument); + return true; + } + std::string moduleName = swiftModuleArgument.substr(0, foundDelimeterPos), + modulePath = swiftModuleArgument.substr(foundDelimeterPos+1); + if (!Lexer::isIdentifier(moduleName)) { + Diags.diagnose(SourceLoc(), diag::error_bad_module_name, moduleName, false); + return true; + } + ExplicitSwiftModuleInputs.emplace_back(std::make_pair(moduleName, modulePath)); + return false; +} + static bool ParseSearchPathArgs(SearchPathOptions &Opts, ArgList &Args, DiagnosticEngine &Diags, @@ -1415,6 +1434,11 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts, if (const Arg *A = Args.getLastArg(OPT_explicit_swift_module_map)) Opts.ExplicitSwiftModuleMap = A->getValue(); + for (auto A : Args.getAllArgValues(options::OPT_swift_module_file)) { + if (validateSwiftModuleFileArgumentAndAdd(A, Diags, + Opts.ExplicitSwiftModuleInputs)) + return true; + } for (auto A: Args.filtered(OPT_candidate_module_file)) { Opts.CandidateCompiledModules.push_back(resolveSearchPath(A->getValue())); } diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index c83b6986a9d03..e15e81b133a02 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -566,10 +566,12 @@ bool CompilerInstance::setUpModuleLoaders() { bool ExplicitModuleBuild = Invocation.getFrontendOptions().DisableImplicitModules; if (ExplicitModuleBuild || - !Invocation.getSearchPathOptions().ExplicitSwiftModuleMap.empty()) { + !Invocation.getSearchPathOptions().ExplicitSwiftModuleMap.empty() || + !Invocation.getSearchPathOptions().ExplicitSwiftModuleInputs.empty()) { ESML = ExplicitSwiftModuleLoader::create( *Context, getDependencyTracker(), MLM, Invocation.getSearchPathOptions().ExplicitSwiftModuleMap, + Invocation.getSearchPathOptions().ExplicitSwiftModuleInputs, IgnoreSourceInfoFile); } diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index e1ca8adfc760d..977098482f7d4 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -1897,6 +1897,16 @@ struct ExplicitSwiftModuleLoader::Implementation { } } } + + void addCommandLineExplicitInputs( + const std::vector> + &commandLineExplicitInputs) { + for (const auto &moduleInput : commandLineExplicitInputs) { + ExplicitModuleInfo entry; + entry.modulePath = moduleInput.second; + ExplicitModuleMap.try_emplace(moduleInput.first, std::move(entry)); + } + } }; ExplicitSwiftModuleLoader::ExplicitSwiftModuleLoader( @@ -2057,6 +2067,7 @@ std::unique_ptr ExplicitSwiftModuleLoader::create(ASTContext &ctx, DependencyTracker *tracker, ModuleLoadingMode loadMode, StringRef ExplicitSwiftModuleMap, + const std::vector> &ExplicitSwiftModuleInputs, bool IgnoreSwiftSourceInfoFile) { auto result = std::unique_ptr( new ExplicitSwiftModuleLoader(ctx, tracker, loadMode, @@ -2067,6 +2078,11 @@ ExplicitSwiftModuleLoader::create(ASTContext &ctx, // Parse a JSON file to collect explicitly built modules. Impl.parseSwiftExplicitModuleMap(ExplicitSwiftModuleMap); } + // If some modules are provided with explicit + // '-swift-module-file' options, add those as well. + if (!ExplicitSwiftModuleInputs.empty()) { + Impl.addCommandLineExplicitInputs(ExplicitSwiftModuleInputs); + } return result; } From c98982377521fa0202e66855237ddd7adf65d575 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Fri, 3 Feb 2023 16:49:00 -0800 Subject: [PATCH 07/98] [Dependency Scanning] Specify dependency inputs of Swift module dependencies on the command line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do this by computing a transitive closure on the computed dependency graph, relying on the fact that it is a DAG. The used algorithm is: ``` for each v ∈ V { T(v) = { v } } for v ∈ V in reverse topological order { for each (v, w) ∈ E { T(v) = T(v) ∪ T(w) } } ``` --- include/swift/AST/ModuleDependencies.h | 24 ++- .../swift/Frontend/ModuleInterfaceLoader.h | 3 +- .../ModuleDependencyCacheSerialization.cpp | 13 -- lib/DependencyScan/ScanDependencies.cpp | 171 +++++++++++++++++- .../explicit-swift-dependencies.swift | 55 ++++++ .../module_deps_cache_reuse.swift | 95 +++++----- .../module_deps_external.swift | 14 +- 7 files changed, 305 insertions(+), 70 deletions(-) create mode 100644 test/ScanDependencies/explicit-swift-dependencies.swift diff --git a/include/swift/AST/ModuleDependencies.h b/include/swift/AST/ModuleDependencies.h index 434f48282c95d..66fbe9938c164 100644 --- a/include/swift/AST/ModuleDependencies.h +++ b/include/swift/AST/ModuleDependencies.h @@ -150,7 +150,7 @@ class SwiftInterfaceModuleDependenciesStorage : /// The Swift frontend invocation arguments to build the Swift module from the /// interface. - const std::vector buildCommandLine; + std::vector buildCommandLine; /// The hash value that will be used for the generated module const std::string contextHash; @@ -186,6 +186,10 @@ class SwiftInterfaceModuleDependenciesStorage : static bool classof(const ModuleDependencyInfoStorageBase *base) { return base->dependencyKind == ModuleDependencyKind::SwiftInterface; } + + void updateCommandLine(const std::vector &newCommandLine) { + buildCommandLine = newCommandLine; + } }; /// Describes the dependencies of a Swift module @@ -433,6 +437,11 @@ class ModuleDependencyInfo { storage->resolvedModuleDependencies.assign(dependencyIDs.begin(), dependencyIDs.end()); } + void updateCommandLine(const std::vector &newCommandLine) { + assert(isSwiftInterfaceModule() && "Can only update command line on Swift interface dependency"); + cast(storage.get())->updateCommandLine(newCommandLine); + } + bool isResolved() const { return storage->resolved; } @@ -752,4 +761,17 @@ class ModuleDependenciesCache { } // namespace swift +namespace std { +template <> +struct hash { + using UnderlyingKindType = std::underlying_type::type; + std::size_t operator()(const swift::ModuleDependencyID &id) const { + auto underlyingKindValue = static_cast(id.second); + + return (hash()(id.first) ^ + (hash()(underlyingKindValue))); + } +}; +} // namespace std + #endif /* SWIFT_AST_MODULE_DEPENDENCIES_H */ diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index 757fbbe53708a..a1546384d0ce0 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -129,7 +129,8 @@ class SearchPathOptions; class CompilerInvocation; /// A ModuleLoader that loads explicitly built Swift modules specified via -/// -swift-module-file or modules found in +/// -swift-module-file or modules found in a provided +/// -explicit-swift-module-map-file JSON input. class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase { explicit ExplicitSwiftModuleLoader(ASTContext &ctx, DependencyTracker *tracker, ModuleLoadingMode loadMode, diff --git a/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp index 6140d9036426a..f9e749c04a4f7 100644 --- a/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp +++ b/lib/DependencyScan/ModuleDependencyCacheSerialization.cpp @@ -656,19 +656,6 @@ struct llvm::DenseMapInfo { } }; -namespace std { -template <> -struct hash { - using UnderlyingKindType = std::underlying_type::type; - std::size_t operator()(const ModuleDependencyID &id) const { - auto underlyingKindValue = static_cast(id.second); - - return (hash()(id.first) ^ - (hash()(underlyingKindValue))); - } -}; -} // namespace std - namespace swift { class ModuleDependenciesCacheSerializer { diff --git a/lib/DependencyScan/ScanDependencies.cpp b/lib/DependencyScan/ScanDependencies.cpp index b2a1e1a426dde..32aa5a8168cd8 100644 --- a/lib/DependencyScan/ScanDependencies.cpp +++ b/lib/DependencyScan/ScanDependencies.cpp @@ -36,6 +36,7 @@ #include "swift/Strings.h" #include "clang/Basic/Module.h" #include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SetOperations.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" @@ -47,6 +48,7 @@ #include #include #include +#include using namespace swift; using namespace swift::dependencies; @@ -157,6 +159,152 @@ static void findAllImportedClangModules(ASTContext &ctx, StringRef moduleName, } } +// Get all dependencies's IDs of this module from its cached +// ModuleDependencyInfo +static ArrayRef +getDependencies(const ModuleDependencyID &moduleID, + const ModuleDependenciesCache &cache) { + const auto &optionalModuleInfo = + cache.findDependency(moduleID.first, moduleID.second); + assert(optionalModuleInfo.has_value()); + return optionalModuleInfo.value()->getModuleDependencies(); +} + +/// Implements a topological sort via recursion and reverse postorder DFS. +/// Does not bother handling cycles, relying on a DAG guarantee by the client. +static std::vector +computeTopologicalSortOfExplicitDependencies( + const ModuleDependencyIDSetVector &allModules, + const ModuleDependenciesCache &cache) { + // Must be explicitly-typed to allow recursion + std::function &, + std::unordered_set &, + std::vector &)> + visit; + visit = [&visit, &cache](const ModuleDependencyID &moduleID, + std::stack &stack, + std::unordered_set &visited, + std::vector &result) { + // Mark this node as visited -- we are done if it already was. + if (!visited.insert(moduleID).second) + return; + + // Otherwise, visit each adjacent node. + for (const auto &succID : getDependencies(moduleID, cache)) { + // We don't worry if successor is already in this current stack, + // since that would mean we have found a cycle, which should not + // be possible because we checked for cycles earlier. + stack.push(succID); + visit(succID, stack, visited, result); + auto top = stack.top(); + stack.pop(); + assert(top == succID); + } + + // Add to the result. + result.push_back(moduleID); + }; + + std::unordered_set visited; + std::vector result; + std::stack stack; + for (const auto &modID : allModules) { + assert(stack.empty()); + stack.push(modID); + visit(modID, stack, visited, result); + auto top = stack.top(); + stack.pop(); + assert(top == modID); + } + + std::reverse(result.begin(), result.end()); + return result; +} + +/// For each module in the graph, compute a set of all its dependencies +/// direct *and* transitive. +static std::unordered_map> +computeTransitiveClosureOfExplicitDependencies( + const std::vector &topologicallySortedModuleList, + const ModuleDependenciesCache &cache) { + std::unordered_map> + result; + for (const auto &modID : topologicallySortedModuleList) + result[modID] = {modID}; + + // Traverse the set of modules in reverse topological order, assimilating + // transitive closures + for (auto it = topologicallySortedModuleList.rbegin(), + end = topologicallySortedModuleList.rend(); + it != end; ++it) { + const auto &modID = *it; + auto &modReachableSet = result[modID]; + for (const auto &succID : getDependencies(modID, cache)) { + const auto &succReachableSet = result[succID]; + llvm::set_union(modReachableSet, succReachableSet); + } + } + return result; +} + +static void +resolveExplicitModuleInputs(ModuleDependencyID moduleID, + const ModuleDependencyInfo &resolvingDepInfo, + const std::set &dependencies, + ModuleDependenciesCache &cache) { + auto resolvingInterfaceDepDetails = + resolvingDepInfo.getAsSwiftInterfaceModule(); + assert(resolvingInterfaceDepDetails && + "Expected Swift Interface dependency."); + + auto commandLine = resolvingInterfaceDepDetails->buildCommandLine; + for (const auto &depModuleID : dependencies) { + const auto optionalDepInfo = + cache.findDependency(depModuleID.first, depModuleID.second); + assert(optionalDepInfo.has_value()); + const auto depInfo = optionalDepInfo.value(); + switch (depModuleID.second) { + case swift::ModuleDependencyKind::SwiftInterface: { + auto interfaceDepDetails = depInfo->getAsSwiftInterfaceModule(); + assert(interfaceDepDetails && "Expected Swift Interface dependency."); + commandLine.push_back("-swift-module-file=" + depModuleID.first + "=" + + interfaceDepDetails->moduleOutputPath); + } break; + case swift::ModuleDependencyKind::SwiftBinary: { + auto binaryDepDetails = depInfo->getAsSwiftBinaryModule(); + assert(binaryDepDetails && "Expected Swift Binary Module dependency."); + commandLine.push_back("-swift-module-file=" + depModuleID.first + "=" + + binaryDepDetails->compiledModulePath); + } break; + case swift::ModuleDependencyKind::SwiftPlaceholder: { + auto placeholderDetails = depInfo->getAsPlaceholderDependencyModule(); + assert(placeholderDetails && "Expected Swift Placeholder dependency."); + commandLine.push_back("-swift-module-file=" + depModuleID.first + "=" + + placeholderDetails->compiledModulePath); + } break; + case swift::ModuleDependencyKind::Clang: { + auto clangDepDetails = depInfo->getAsClangModule(); + assert(binaryDepDetails && "Expected Clang Module dependency."); + commandLine.push_back("-Xcc"); + commandLine.push_back("-fmodule-file=" + depModuleID.first + "=" + + clangDepDetails->pcmOutputPath); + commandLine.push_back("-Xcc"); + commandLine.push_back("-fmodule-map-file=" + + clangDepDetails->moduleMapFile); + } break; + default: + llvm_unreachable("Unhandled dependency kind."); + } + } + + // Update the dependency in the cache with the modified command-line. + auto dependencyInfoCopy = resolvingDepInfo; + dependencyInfoCopy.updateCommandLine(commandLine); + cache.updateDependency(moduleID, dependencyInfoCopy); +} + /// Resolve the direct dependencies of the given module. static ArrayRef resolveDirectDependencies(CompilerInstance &instance, ModuleDependencyID module, @@ -1475,8 +1623,29 @@ swift::dependencies::performModuleScan(CompilerInstance &instance, if (diagnoseCycle(instance, cache, mainModuleID, ASTDelegate)) return std::make_error_code(std::errc::not_supported); + // Resolve Swift dependency command-line arguments + // Must happen after cycle detection, because it relies on + // the DAG property of the dependency graph. + auto topoSortedModuleList = + computeTopologicalSortOfExplicitDependencies(allModules, cache); + auto moduleTransitiveClosures = + computeTransitiveClosureOfExplicitDependencies(topoSortedModuleList, + cache); + for (const auto &dependencyClosure : moduleTransitiveClosures) { + auto &modID = dependencyClosure.first; + // For main module or binary modules, no command-line to resolve. + // For Clang modules, their dependencies are resolved by the clang Scanner + // itself for us. + if (modID.second != ModuleDependencyKind::SwiftInterface) + continue; + auto optionalDeps = cache.findDependency(modID.first, modID.second); + assert(optionalDeps.has_value()); + auto deps = optionalDeps.value(); + resolveExplicitModuleInputs(modID, *deps, dependencyClosure.second, cache); + } + auto dependencyGraph = generateFullDependencyGraph( - instance, cache, ASTDelegate, allModules.getArrayRef()); + instance, cache, ASTDelegate, topoSortedModuleList); // Update the dependency tracker. if (auto depTracker = instance.getDependencyTracker()) { for (auto module : allModules) { diff --git a/test/ScanDependencies/explicit-swift-dependencies.swift b/test/ScanDependencies/explicit-swift-dependencies.swift new file mode 100644 index 0000000000000..185a20750a95e --- /dev/null +++ b/test/ScanDependencies/explicit-swift-dependencies.swift @@ -0,0 +1,55 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/clang-module-cache + +// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %s -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -emit-dependencies -emit-dependencies-path %t/deps.d -import-objc-header %S/Inputs/CHeaders/Bridging.h -swift-version 4 +// Check the contents of the JSON output +// RUN: %FileCheck %s < %t/deps.json + +// REQUIRES: executable_test +// REQUIRES: objc_interop + + +import F + +// CHECK: "mainModuleName": "deps" + +/// --------Main module +// CHECK-LABEL: "modulePath": "deps.swiftmodule", +// CHECK-NEXT: sourceFiles +// CHECK-NEXT: explicit-swift-dependencies.swift +// CHECK-NEXT: ], +// CHECK-NEXT: "directDependencies": [ +// CHECK-DAG: "swift": "F" +// CHECK-DAG: "swift": "Swift" +// CHECK-DAG: "swift": "SwiftOnoneSupport" +// CHECK-DAG: "swift": "_Concurrency" +// CHECK-DAG: "clang": "_SwiftConcurrencyShims" +// CHECK-DAG: "swift": "_StringProcessing" +// CHECK: ], + +/// --------Swift module F +// CHECK: "modulePath": "{{.*}}{{/|\\}}F-{{.*}}.swiftmodule", +// CHECK: directDependencies +// CHECK-NEXT: { +// CHECK-DAG: "clang": "F" +// CHECK-DAG: "swift": "Swift" +// CHECK-DAG: "swift": "SwiftOnoneSupport" + + +// CHECK: "commandLine": [ +// CHECK-NEXT: "-frontend", +// CHECK-NEXT: "-compile-module-from-interface", +// CHECK-NEXT: "-target", +// ... +// CHECK: "-explicit-interface-module-build", +// CHECK-NEXT: "-disable-implicit-swift-modules", +// CHECK-NEXT: "-Xcc", +// CHECK-NEXT: "-fno-implicit-modules", +// CHECK-NEXT: "-Xcc", +// CHECK-NEXT: "-fno-implicit-module-maps", +// CHECK-NEXT: "-o", +// CHECK-NEXT: "{{.*}}{{/|\\}}F-{{.*}}.swiftmodule" +// CHECK-DAG: "-swift-module-file=Swift={{.*}}{{/|\\}}Swift-{{.*}}.swiftmodule" +// CHECK-DAG: "-swift-module-file=SwiftOnoneSupport={{.*}}{{/|\\}}SwiftOnoneSupport-{{.*}}.swiftmodule" +// CHECK-DAG: "-fmodule-file=F={{.*}}{{/|\\}}F-{{.*}}.pcm", +// CHECK-DAG: "-fmodule-file=SwiftShims={{.*}}{{/|\\}}SwiftShims-{{.*}}.pcm", diff --git a/test/ScanDependencies/module_deps_cache_reuse.swift b/test/ScanDependencies/module_deps_cache_reuse.swift index 41eda3dae6a90..5a2e43d54cf37 100644 --- a/test/ScanDependencies/module_deps_cache_reuse.swift +++ b/test/ScanDependencies/module_deps_cache_reuse.swift @@ -62,40 +62,25 @@ import SubE // CHECK-NEXT: "F" // CHECK-NEXT: ] -/// --------Clang module C -// CHECK-LABEL: "modulePath": "{{.*}}/C-{{.*}}.pcm", - -// CHECK: "sourceFiles": [ -// CHECK-DAG: module.modulemap -// CHECK-DAG: C.h +/// --------Swift module A +// CHECK-LABEL: "modulePath": "{{.*}}{{/|\\}}A-{{.*}}.swiftmodule", // CHECK: directDependencies // CHECK-NEXT: { -// CHECK-NEXT: "clang": "B" - -// CHECK: "moduleMapPath" -// CHECK-SAME: module.modulemap - -// CHECK: "contextHash" -// CHECK-SAME: "{{.*}}" - -// CHECK: "commandLine": [ -// CHECK-NEXT: "-frontend" -// CHECK-NEXT: "-only-use-extra-clang-opts -// CHECK-NOT: "BUILD_DIR/bin/clang" -// CHECK: "-emit-pcm", -// CHECK: "-module-name", -// CHECK-NEXT: "C" +// CHECK-DAG: "clang": "A" +// CHECK-DAG: "swift": "Swift" -/// --------Swift module E -// CHECK: "swift": "E" -// CHECK-LABEL: modulePath": "{{.*}}{{/|\\}}E-{{.*}}.swiftmodule" -// CHECK: "directDependencies" -// CHECK-NEXT: { -// CHECK-NEXT: "swift": "Swift" +/// --------Swift module F +// CHECK-LABEL: "modulePath": "{{.*}}{{/|\\}}F-{{.*}}.swiftmodule", +// CHECK-NEXT: "sourceFiles": [ +// CHECK-NEXT: ], +// CHECK-NEXT: "directDependencies": [ +// CHECK-NEXT: { +// CHECK-DAG: "clang": "F" +// CHECK-DAG: "swift": "Swift" +// CHECK-DAG: "swift": "SwiftOnoneSupport" +// CHECK: ], -// CHECK: "moduleInterfacePath" -// CHECK-SAME: E.swiftinterface /// --------Swift module G // CHECK-LABEL: "modulePath": "{{.*}}{{/|\\}}G-{{.*}}.swiftmodule" @@ -121,6 +106,16 @@ import SubE // CHECK" "-fapinotes-swift-version=5" // CHECK" ] +/// --------Swift module E +// CHECK: "swift": "E" +// CHECK-LABEL: modulePath": "{{.*}}{{/|\\}}E-{{.*}}.swiftmodule" +// CHECK: "directDependencies" +// CHECK-NEXT: { +// CHECK-NEXT: "swift": "Swift" + +// CHECK: "moduleInterfacePath" +// CHECK-SAME: E.swiftinterface + /// --------Swift module Swift // CHECK-LABEL: "modulePath": "{{.*}}{{/|\\}}Swift-{{.*}}.swiftmodule", @@ -128,24 +123,34 @@ import SubE // CHECK-NEXT: { // CHECK-NEXT: "clang": "SwiftShims" -/// --------Swift module F -// CHECK-LABEL: "modulePath": "{{.*}}{{/|\\}}F-{{.*}}.swiftmodule", -// CHECK-NEXT: "sourceFiles": [ -// CHECK-NEXT: ], -// CHECK-NEXT: "directDependencies": [ -// CHECK-NEXT: { -// CHECK-DAG: "clang": "F" -// CHECK-DAG: "swift": "Swift" -// CHECK-DAG: "swift": "SwiftOnoneSupport" -// CHECK: ], -/// --------Swift module A -// CHECK-LABEL: "modulePath": "{{.*}}{{/|\\}}A-{{.*}}.swiftmodule", +/// --------Clang module SwiftShims +// CHECK-LABEL: "modulePath": "{{.*}}/SwiftShims-{{.*}}.pcm", + +/// --------Clang module C +// CHECK-LABEL: "modulePath": "{{.*}}/C-{{.*}}.pcm", + +// CHECK: "sourceFiles": [ +// CHECK-DAG: module.modulemap +// CHECK-DAG: C.h // CHECK: directDependencies // CHECK-NEXT: { -// CHECK-DAG: "clang": "A" -// CHECK-DAG: "swift": "Swift" +// CHECK-NEXT: "clang": "B" + +// CHECK: "moduleMapPath" +// CHECK-SAME: module.modulemap + +// CHECK: "contextHash" +// CHECK-SAME: "{{.*}}" + +// CHECK: "commandLine": [ +// CHECK-NEXT: "-frontend" +// CHECK-NOT: "BUILD_DIR/bin/clang" +// CHECK: "-emit-pcm", +// CHECK: "-module-name", +// CHECK-NEXT: "C" +// CHECK-NEXT: "-only-use-extra-clang-opts /// --------Clang module B // CHECK-LABEL: "modulePath": "{{.*}}/B-{{.*}}.pcm", @@ -158,7 +163,3 @@ import SubE // CHECK-NEXT: { // CHECK-NEXT: "clang": "A" // CHECK-NEXT: } - -/// --------Clang module SwiftShims -// CHECK-LABEL: "modulePath": "{{.*}}/SwiftShims-{{.*}}.pcm", - diff --git a/test/ScanDependencies/module_deps_external.swift b/test/ScanDependencies/module_deps_external.swift index 561dbe30d145e..bbf302d94fbfc 100644 --- a/test/ScanDependencies/module_deps_external.swift +++ b/test/ScanDependencies/module_deps_external.swift @@ -71,13 +71,6 @@ import SomeExternalModule // CHECK-NEXT: "F" // CHECK-NEXT: ] -/// --------Swift external module SomeExternalModule -// CHECK-LABEL: "modulePath": "{{.*}}{{/|\\}}SomeExternalModule.swiftmodule", -// CHECK-NEXT: "details": { -// CHECK-NEXT: "swiftPlaceholder": { -// CHECK-NEXT: "moduleDocPath": "BUILD_DIR/{{.*}}/ScanDependencies/Output/module_deps_external.swift.tmp/inputs/SomeExternalModule.swiftdoc", -// CHECK-NEXT: "moduleSourceInfoPath": "BUILD_DIR/{{.*}}/ScanDependencies/Output/module_deps_external.swift.tmp/inputs/SomeExternalModule.swiftsourceinfo" - /// --------Swift module Swift // CHECK-LABEL: "modulePath": "{{.*}}{{/|\\}}Swift-{{.*}}.swiftmodule", @@ -93,3 +86,10 @@ import SomeExternalModule // CHECK-MAKE-DEPS-SAME: Bridging.h // CHECK-MAKE-DEPS-SAME: BridgingOther.h // CHECK-MAKE-DEPS-SAME: module.modulemap + +/// --------Swift external module SomeExternalModule +// CHECK-LABEL: "modulePath": "{{.*}}{{/|\\}}SomeExternalModule.swiftmodule", +// CHECK-NEXT: "details": { +// CHECK-NEXT: "swiftPlaceholder": { +// CHECK-NEXT: "moduleDocPath": "BUILD_DIR/{{.*}}/ScanDependencies/Output/module_deps_external.swift.tmp/inputs/SomeExternalModule.swiftdoc", +// CHECK-NEXT: "moduleSourceInfoPath": "BUILD_DIR/{{.*}}/ScanDependencies/Output/module_deps_external.swift.tmp/inputs/SomeExternalModule.swiftsourceinfo" From be3812d686fa4ee2a6d86df19e8d40725004644b Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Mon, 6 Feb 2023 09:34:19 -0700 Subject: [PATCH 08/98] [Dependency Scanning] Add swift dependency compile arguments required for self-contained commands - '-o ' - '-disable-implicit-swift-modules' - '-Xcc -fno-implicit-modules' and '-Xcc -fno-implicit-module-maps' - '-candidate-module-file' These were previously supplied by the driver. Instead, they will now be ready to be run directly from the dependency scanner's output. --- .../ClangModuleDependencyScanner.cpp | 8 ++--- lib/DependencyScan/ScanDependencies.cpp | 29 +++++++++---------- lib/Serialization/ModuleDependencyScanner.cpp | 21 ++++++++++++-- .../module_deps_cache_reuse.swift | 1 - 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/lib/ClangImporter/ClangModuleDependencyScanner.cpp b/lib/ClangImporter/ClangModuleDependencyScanner.cpp index b39827850b165..3b73b0ff9d00c 100644 --- a/lib/ClangImporter/ClangModuleDependencyScanner.cpp +++ b/lib/ClangImporter/ClangModuleDependencyScanner.cpp @@ -153,15 +153,15 @@ void ClangImporter::recordModuleDependencies( // We are using Swift frontend mode. swiftArgs.push_back("-frontend"); - // We pass the entire argument list via -Xcc, so the invocation should - // use extra clang options alone. - swiftArgs.push_back("-only-use-extra-clang-opts"); - // Swift frontend action: -emit-pcm swiftArgs.push_back("-emit-pcm"); swiftArgs.push_back("-module-name"); swiftArgs.push_back(clangModuleDep.ID.ModuleName); + // We pass the entire argument list via -Xcc, so the invocation should + // use extra clang options alone. + swiftArgs.push_back("-only-use-extra-clang-opts"); + auto pcmPath = moduleCacheRelativeLookupModuleOutput( clangModuleDep.ID, ModuleOutputKind::ModuleFile, getModuleCachePathFromClang(getClangInstance())); diff --git a/lib/DependencyScan/ScanDependencies.cpp b/lib/DependencyScan/ScanDependencies.cpp index 32aa5a8168cd8..23345444ac925 100644 --- a/lib/DependencyScan/ScanDependencies.cpp +++ b/lib/DependencyScan/ScanDependencies.cpp @@ -176,16 +176,14 @@ static std::vector computeTopologicalSortOfExplicitDependencies( const ModuleDependencyIDSetVector &allModules, const ModuleDependenciesCache &cache) { + std::unordered_set visited; + std::vector result; + std::stack stack; + // Must be explicitly-typed to allow recursion - std::function &, - std::unordered_set &, - std::vector &)> - visit; - visit = [&visit, &cache](const ModuleDependencyID &moduleID, - std::stack &stack, - std::unordered_set &visited, - std::vector &result) { + std::function visit; + visit = [&visit, &cache, &visited, &result, + &stack](const ModuleDependencyID &moduleID) { // Mark this node as visited -- we are done if it already was. if (!visited.insert(moduleID).second) return; @@ -196,7 +194,7 @@ computeTopologicalSortOfExplicitDependencies( // since that would mean we have found a cycle, which should not // be possible because we checked for cycles earlier. stack.push(succID); - visit(succID, stack, visited, result); + visit(succID); auto top = stack.top(); stack.pop(); assert(top == succID); @@ -206,13 +204,10 @@ computeTopologicalSortOfExplicitDependencies( result.push_back(moduleID); }; - std::unordered_set visited; - std::vector result; - std::stack stack; for (const auto &modID : allModules) { assert(stack.empty()); stack.push(modID); - visit(modID, stack, visited, result); + visit(modID); auto top = stack.top(); stack.pop(); assert(top == modID); @@ -222,13 +217,15 @@ computeTopologicalSortOfExplicitDependencies( return result; } -/// For each module in the graph, compute a set of all its dependencies +/// For each module in the graph, compute a set of all its dependencies, /// direct *and* transitive. static std::unordered_map> computeTransitiveClosureOfExplicitDependencies( const std::vector &topologicallySortedModuleList, const ModuleDependenciesCache &cache) { + // The usage of an ordered ::set is important to ensure the + // dependencies are listed in a deterministic order. std::unordered_map> result; for (const auto &modID : topologicallySortedModuleList) @@ -286,7 +283,7 @@ resolveExplicitModuleInputs(ModuleDependencyID moduleID, } break; case swift::ModuleDependencyKind::Clang: { auto clangDepDetails = depInfo->getAsClangModule(); - assert(binaryDepDetails && "Expected Clang Module dependency."); + assert(clangDepDetails && "Expected Clang Module dependency."); commandLine.push_back("-Xcc"); commandLine.push_back("-fmodule-file=" + depModuleID.first + "=" + clangDepDetails->pcmOutputPath); diff --git a/lib/Serialization/ModuleDependencyScanner.cpp b/lib/Serialization/ModuleDependencyScanner.cpp index a89c29b16f4de..3197e41920e94 100644 --- a/lib/Serialization/ModuleDependencyScanner.cpp +++ b/lib/Serialization/ModuleDependencyScanner.cpp @@ -112,21 +112,38 @@ ErrorOr ModuleDependencyScanner::scanInterfaceFile( StringRef(), SourceLoc(), [&](ASTContext &Ctx, ModuleDecl *mainMod, - ArrayRef Args, + ArrayRef BaseArgs, ArrayRef PCMArgs, StringRef Hash) { assert(mainMod); std::string InPath = moduleInterfacePath.str(); auto compiledCandidates = getCompiledCandidates(Ctx, realModuleName.str(), InPath); + std::vector Args(BaseArgs.begin(), BaseArgs.end()); + + // Add explicit Swift dependency compilation flags + Args.push_back("-explicit-interface-module-build"); + Args.push_back("-disable-implicit-swift-modules"); + Args.push_back("-Xcc"); Args.push_back("-fno-implicit-modules"); + Args.push_back("-Xcc"); Args.push_back("-fno-implicit-module-maps"); + for (const auto &candidate : compiledCandidates) { + Args.push_back("-candidate-module-file"); + Args.push_back(candidate); + } + // Compute the output path and add it to the command line SmallString<128> outputPathBase(moduleCachePath); llvm::sys::path::append( outputPathBase, moduleName.str() + "-" + Hash + "." + file_types::getExtension(file_types::TY_SwiftModuleFile)); + Args.push_back("-o"); + Args.push_back(outputPathBase.str().str()); + + std::vector ArgsRefs(Args.begin(), Args.end()); Result = ModuleDependencyInfo::forSwiftInterfaceModule( - outputPathBase.str().str(), InPath, compiledCandidates, Args, PCMArgs, + outputPathBase.str().str(), InPath, compiledCandidates, ArgsRefs, PCMArgs, Hash, isFramework); + // Open the interface file. auto &fs = *Ctx.SourceMgr.getFileSystem(); auto interfaceBuf = fs.getBufferForFile(moduleInterfacePath); diff --git a/test/ScanDependencies/module_deps_cache_reuse.swift b/test/ScanDependencies/module_deps_cache_reuse.swift index 5a2e43d54cf37..d683fb18c2426 100644 --- a/test/ScanDependencies/module_deps_cache_reuse.swift +++ b/test/ScanDependencies/module_deps_cache_reuse.swift @@ -81,7 +81,6 @@ import SubE // CHECK-DAG: "swift": "SwiftOnoneSupport" // CHECK: ], - /// --------Swift module G // CHECK-LABEL: "modulePath": "{{.*}}{{/|\\}}G-{{.*}}.swiftmodule" // CHECK: "directDependencies" From bf341ac51c7da67b430acc50a2951b7186b8b6a7 Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Tue, 7 Feb 2023 14:39:40 +0000 Subject: [PATCH 09/98] [cxx-interop] Require `begin()` and `end()` to be non-mutating for auto-conformed container types This makes sure that Swift is only auto-conforming C++ container types to `CxxSequence`/`CxxConvertibleToCollection` if they expose non-mutating `begin()` and `end()` methods. We might want to make `begin()` and `end()` non-mutating in the near future to enable performance optimizations. This change makes sure that client code relying on the automatic conformances doesn't suddenly stop compiling if/when the mutability requirement on the protocol function changes. --- lib/ClangImporter/ClangDerivedConformances.cpp | 12 ++++++------ .../overlay/custom-sequence-module-interface.swift | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/ClangImporter/ClangDerivedConformances.cpp b/lib/ClangImporter/ClangDerivedConformances.cpp index eec239b11bd96..5b0142db23e15 100644 --- a/lib/ClangImporter/ClangDerivedConformances.cpp +++ b/lib/ClangImporter/ClangDerivedConformances.cpp @@ -396,7 +396,7 @@ void swift::conformToCxxSequenceIfNeeded( if (!cxxIteratorProto || !cxxSequenceProto) return; - // Check if present: `mutating func __beginUnsafe() -> RawIterator` + // Check if present: `func __beginUnsafe() -> RawIterator` auto beginId = ctx.getIdentifier("__beginUnsafe"); auto begins = lookupDirectWithoutExtensions(decl, beginId); if (begins.size() != 1) @@ -406,7 +406,7 @@ void swift::conformToCxxSequenceIfNeeded( return; auto rawIteratorTy = begin->getResultInterfaceType(); - // Check if present: `mutating func __endUnsafe() -> RawIterator` + // Check if present: `func __endUnsafe() -> RawIterator` auto endId = ctx.getIdentifier("__endUnsafe"); auto ends = lookupDirectWithoutExtensions(decl, endId); if (ends.size() != 1) @@ -415,6 +415,10 @@ void swift::conformToCxxSequenceIfNeeded( if (!end) return; + // Check if `begin()` and `end()` are non-mutating. + if (begin->isMutating() || end->isMutating()) + return; + // Check if `__beginUnsafe` and `__endUnsafe` have the same return type. auto endTy = end->getResultInterfaceType(); if (!endTy || endTy->getCanonicalType() != rawIteratorTy->getCanonicalType()) @@ -468,10 +472,6 @@ void swift::conformToCxxSequenceIfNeeded( !ctx.getProtocol(KnownProtocolKind::CxxRandomAccessCollection)) return false; - // Check if `begin()` and `end()` are non-mutating. - if (begin->isMutating() || end->isMutating()) - return false; - // Check if RawIterator conforms to UnsafeCxxRandomAccessIterator. auto rawIteratorRAConformanceRef = decl->getModuleContext()->lookupConformance(rawIteratorTy, diff --git a/test/Interop/Cxx/stdlib/overlay/custom-sequence-module-interface.swift b/test/Interop/Cxx/stdlib/overlay/custom-sequence-module-interface.swift index 3a6fa5c64ac36..543ad44dbe0eb 100644 --- a/test/Interop/Cxx/stdlib/overlay/custom-sequence-module-interface.swift +++ b/test/Interop/Cxx/stdlib/overlay/custom-sequence-module-interface.swift @@ -24,10 +24,10 @@ // CHECK: typealias RawIterator = UnsafePointer? // CHECK: } -// CHECK: struct HasMutatingBeginEnd : CxxConvertibleToCollection { -// CHECK: typealias Element = ConstIterator.Pointee -// CHECK: typealias Iterator = CxxIterator -// CHECK: typealias RawIterator = ConstIterator +// CHECK: struct HasMutatingBeginEnd { +// CHECK-NOT: typealias Element = ConstIterator.Pointee +// CHECK-NOT: typealias Iterator = CxxIterator +// CHECK-NOT: typealias RawIterator = ConstIterator // CHECK: } // CHECK: struct HasNoBeginMethod { From e313c533b67ead0b30fa7693a945f88a6784d90b Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 7 Feb 2023 11:13:30 -0800 Subject: [PATCH 10/98] [ConstraintSystem] Teach `getCalleeLocator` about pattern matching Requesting a callee locator from locator in pattern matching context should always yield pattern match. --- lib/Sema/ConstraintSystem.cpp | 9 +++++++++ test/Constraints/patterns.swift | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index c73bb6286a37d..a54729c9ba2a7 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -496,6 +496,15 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( } } + { + // Pattern match is always a callee regardless of what comes after it. + auto iter = path.rbegin(); + if (locator->findLast(iter)) { + auto newPath = path.drop_back(iter - path.rbegin()); + return getConstraintLocator(anchor, newPath); + } + } + if (locator->findLast()) { return getConstraintLocator(anchor, LocatorPathElt::ApplyFunction()); } diff --git a/test/Constraints/patterns.swift b/test/Constraints/patterns.swift index 142b7ec1114f5..36a4a15ba5d1a 100644 --- a/test/Constraints/patterns.swift +++ b/test/Constraints/patterns.swift @@ -540,3 +540,20 @@ func f60503() { let (key, _) = settings.enumerate() // expected-error{{cannot find 'settings' in scope}} let (_, _) = settings.enumerate() // expected-error{{cannot find 'settings' in scope}} } + +// rdar://105089074 +enum EWithIdent where Id: P { // expected-note 2 {{where 'Id' = 'Int'}} +case test(Id) +} + +extension [EWithIdent] { + func test() { + sorted { lhs, rhs in + switch (rhs, rhs) { + case let (.test(x), .test(y)): break + // expected-error@-1 2 {{generic enum 'EWithIdent' requires that 'Int' conform to 'P'}} + case (_, _): break + } + } + } +} From f4e7e1c53e47cc7500240c85642b9a3567cef61b Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Tue, 7 Feb 2023 14:51:24 -0800 Subject: [PATCH 11/98] [interop][SwiftToCxx] update external_source_symbol check for USR clause following Clang changes --- lib/PrintAsClang/_SwiftCxxInteroperability.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/PrintAsClang/_SwiftCxxInteroperability.h b/lib/PrintAsClang/_SwiftCxxInteroperability.h index 4e243bfa21cbc..0c54b57c7ea90 100644 --- a/lib/PrintAsClang/_SwiftCxxInteroperability.h +++ b/lib/PrintAsClang/_SwiftCxxInteroperability.h @@ -39,7 +39,7 @@ #define SWIFT_SYMBOL_MODULE(moduleValue) \ __attribute__((external_source_symbol( \ language = "Swift", defined_in = moduleValue, generated_declaration))) -#if __has_attribute(external_source_symbol_with_usr) +#if __has_attribute(external_source_symbol) > 1 #define SWIFT_SYMBOL_MODULE_USR(moduleValue, usrValue) \ __attribute__(( \ external_source_symbol(language = "Swift", defined_in = moduleValue, \ From 183fc28b72515631f32afc32011ab49bd80ac977 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 7 Feb 2023 16:12:31 -0800 Subject: [PATCH 12/98] [benchmark] change a buffer length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - the intermediate length (15 bytes) didn’t show results significantly different from those produced by the short length benchmark (7 bytes). --- benchmark/single-source/BufferFind.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/benchmark/single-source/BufferFind.swift b/benchmark/single-source/BufferFind.swift index ec556ba96c61c..aef1aaeac8943 100644 --- a/benchmark/single-source/BufferFind.swift +++ b/benchmark/single-source/BufferFind.swift @@ -28,19 +28,19 @@ public var benchmarks: [BenchmarkInfo] = [ setUpFunction: buffer1000Setup, tearDownFunction: bufferTeardown ), - // size 15, alignment 0 + // size 39, alignment 0 BenchmarkInfo( - name: "RawBuffer.15.findFirst", + name: "RawBuffer.39.findFirst", runFunction: run_BufferFindFirst, tags: [.validation, .api], - setUpFunction: buffer15Setup, + setUpFunction: buffer39Setup, tearDownFunction: bufferTeardown ), BenchmarkInfo( - name: "RawBuffer.15.findLast", + name: "RawBuffer.39.findLast", runFunction: run_BufferFindLast, tags: [.validation, .api], - setUpFunction: buffer15Setup, + setUpFunction: buffer39Setup, tearDownFunction: bufferTeardown ), // size 7, alignment 0 @@ -66,8 +66,8 @@ func buffer1000Setup() { bufferSetup(size: 1000, alignment: 0) } -func buffer15Setup() { - bufferSetup(size: 15, alignment: 0) +func buffer39Setup() { + bufferSetup(size: 39, alignment: 0) } func buffer7Setup() { From 3b7295114822b3ee0d5acccbe2b645fe3e18d3ea Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 6 Feb 2023 13:27:12 -0800 Subject: [PATCH 13/98] [sil] Provide FieldSensitivePrunedLiveness with its own implementation of PrunedLiveBlocks called FieldSensitivePrunedLiveBlocks. This will let the non-field sensitive version use a more performant implementation internally. This is important since PrunedLiveBlocks is used in the hot path when working with Ownership SSA, while the field sensitive version is only used for certain diagnostics. NOTE: I did not refactor PrunedLiveness to use the faster implementation... this is just a quick pass over the code to prepare for that change. --- .../swift/SIL/FieldSensitivePrunedLiveness.h | 276 +++++++++++++++++- .../Utils/FieldSensitivePrunedLiveness.cpp | 130 +++++++-- lib/SIL/Utils/PrunedLiveness.cpp | 3 +- .../Mandatory/MoveOnlyAddressChecker.cpp | 2 +- 4 files changed, 382 insertions(+), 29 deletions(-) diff --git a/include/swift/SIL/FieldSensitivePrunedLiveness.h b/include/swift/SIL/FieldSensitivePrunedLiveness.h index b3d54600ca362..566ea20cc8e34 100644 --- a/include/swift/SIL/FieldSensitivePrunedLiveness.h +++ b/include/swift/SIL/FieldSensitivePrunedLiveness.h @@ -9,6 +9,13 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +/// +/// \file This is a field sensitive implementation of PrunedLiveness. It is a +/// completely separate implementation for efficiency reasons but in spirit is +/// implementing the exact same algorithms with changes to account for dealing +/// with multiple elements. +/// +//===----------------------------------------------------------------------===// #ifndef SWIFT_SIL_FIELDSENSITIVEPRUNTEDLIVENESS_H #define SWIFT_SIL_FIELDSENSITIVEPRUNTEDLIVENESS_H @@ -17,7 +24,10 @@ #include "swift/Basic/Debug.h" #include "swift/Basic/FrozenMultiMap.h" #include "swift/Basic/STLExtras.h" -#include "swift/SIL/PrunedLiveness.h" +#include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILValue.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/Support/raw_ostream.h" @@ -375,6 +385,252 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, return os; } +/// Discover "pruned" liveness for an arbitrary set of uses. The client builds +/// liveness by first initializing "def" blocks, then incrementally feeding uses +/// to updateForUse(). +/// +/// Incrementally building liveness is important for algorithms that create an +/// initial live region, perform some analysis on that, then expand the live +/// region by adding new uses before continuing the analysis. +/// +/// Initializing "def blocks" restricts liveness on any path through those def +/// blocks to the blocks that occur on or after the def block. If any uses is +/// not dominated by a def block, then liveness will include the entry block, +/// as if defined by a function argument +/// +/// We allow for multiple bits of liveness information to be tracked by +/// internally using a SmallBitVector. The multiple bit tracking is useful when +/// tracking state for multiple fields of the same root value. To do this, we +/// actually track 2 bits per actual needed bit so we can represent 3 Dead, +/// LiveOut, LiveWithin. This was previously unnecessary since we could just +/// represent dead by not having liveness state for a block. With multiple bits +/// possible this is no longer true. +/// +/// TODO: For efficiency, use BasicBlockBitfield rather than SmallDenseMap. +class FieldSensitivePrunedLiveBlocks { +public: + /// Per-block liveness state computed during backward dataflow propagation. + /// All unvisited blocks are considered Dead. As the are visited, blocks + /// transition through these states in one direction: + /// + /// Dead -> LiveWithin -> LiveOut + /// + /// Dead blocks are either outside of the def's pruned liveness region, or + /// they have not yet been discovered by the liveness computation. + /// + /// LiveWithin blocks have at least one use and/or def within the block, but + /// are not (yet) LiveOut. + /// + /// LiveOut blocks are live on at least one successor path. LiveOut blocks may + /// or may not contain defs or uses. + /// + /// NOTE: The values below for Dead, LiveWithin, LiveOut were picked to ensure + /// that given a 2 bit representation of the value, a value is Dead if the + /// first bit is 0 and is LiveOut if the second bit is set. + enum IsLive { + Dead = 0, + LiveWithin = 1, + LiveOut = 3, + }; + + /// A bit vector that stores information about liveness. This is composed + /// with SmallBitVector since it contains two bits per liveness so that it + /// can represent 3 states, Dead, LiveWithin, LiveOut. We take advantage of + /// their numeric values to make testing easier \see documentation on IsLive. + class LivenessSmallBitVector { + SmallBitVector bits; + + public: + LivenessSmallBitVector() : bits() {} + + void init(unsigned numBits) { + assert(bits.size() == 0); + assert(numBits != 0); + bits.resize(numBits * 2); + } + + unsigned size() const { return bits.size() / 2; } + + IsLive getLiveness(unsigned bitNo) const { + if (!bits[bitNo * 2]) + return IsLive::Dead; + return bits[bitNo * 2 + 1] ? LiveOut : LiveWithin; + } + + /// Returns the liveness in \p resultingFoundLiveness. We only return the + /// bits for endBitNo - startBitNo. + void getLiveness(unsigned startBitNo, unsigned endBitNo, + SmallVectorImpl &resultingFoundLiveness) const { + unsigned actualStartBitNo = startBitNo * 2; + unsigned actualEndBitNo = endBitNo * 2; + + for (unsigned i = actualStartBitNo, e = actualEndBitNo; i != e; i += 2) { + if (!bits[i]) { + resultingFoundLiveness.push_back(Dead); + continue; + } + + resultingFoundLiveness.push_back(bits[i + 1] ? LiveOut : LiveWithin); + } + } + + void setLiveness(unsigned startBitNo, unsigned endBitNo, IsLive isLive) { + for (unsigned i = startBitNo * 2, e = endBitNo * 2; i != e; i += 2) { + bits[i] = isLive & 1; + bits[i + 1] = isLive & 2; + } + } + + void setLiveness(unsigned bitNo, IsLive isLive) { + setLiveness(bitNo, bitNo + 1, isLive); + } + }; + +private: + /// Map all blocks in which current def is live to a SmallBitVector indicating + /// whether the value represented by said bit is also liveout of the block. + llvm::SmallDenseMap liveBlocks; + + /// Number of bits of liveness to track. By default 1. Used to track multiple + /// liveness bits. + unsigned numBitsToTrack; + + /// Optional vector of live blocks for clients that deterministically iterate. + SmallVectorImpl *discoveredBlocks; + + /// Once the first use has been seen, no definitions can be added. + SWIFT_ASSERT_ONLY_DECL(bool seenUse = false); + +public: + FieldSensitivePrunedLiveBlocks( + unsigned numBitsToTrack, + SmallVectorImpl *discoveredBlocks = nullptr) + : numBitsToTrack(numBitsToTrack), discoveredBlocks(discoveredBlocks) { + assert(!discoveredBlocks || discoveredBlocks->empty()); + } + + unsigned getNumBitsToTrack() const { return numBitsToTrack; } + + bool empty() const { return liveBlocks.empty(); } + + void clear() { + liveBlocks.clear(); + SWIFT_ASSERT_ONLY(seenUse = false); + } + + unsigned numLiveBlocks() const { return liveBlocks.size(); } + + /// If the constructor was provided with a vector to populate, then this + /// returns the list of all live blocks with no duplicates. + ArrayRef getDiscoveredBlocks() const { + return *discoveredBlocks; + } + + void initializeDefBlock(SILBasicBlock *defBB, unsigned bitNo) { + markBlockLive(defBB, bitNo, LiveWithin); + } + + void initializeDefBlock(SILBasicBlock *defBB, unsigned startBitNo, + unsigned endBitNo) { + markBlockLive(defBB, startBitNo, endBitNo, LiveWithin); + } + + /// Update this liveness result for a single use. + IsLive updateForUse(SILInstruction *user, unsigned bitNo) { + auto *block = user->getParent(); + 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, + SmallVectorImpl &resultingLiveness); + + IsLive getBlockLiveness(SILBasicBlock *bb, unsigned bitNo) const { + auto liveBlockIter = liveBlocks.find(bb); + if (liveBlockIter == liveBlocks.end()) { + return Dead; + } + + return liveBlockIter->second.getLiveness(bitNo); + } + + /// FIXME: This API should directly return the live bitset. The live bitset + /// type should have an api for querying and iterating over the live fields. + void getBlockLiveness(SILBasicBlock *bb, unsigned startBitNo, + unsigned endBitNo, + SmallVectorImpl &foundLivenessInfo) const { + auto liveBlockIter = liveBlocks.find(bb); + if (liveBlockIter == liveBlocks.end()) { + for (unsigned i : range(endBitNo - startBitNo)) { + (void)i; + foundLivenessInfo.push_back(Dead); + } + return; + } + + liveBlockIter->second.getLiveness(startBitNo, endBitNo, foundLivenessInfo); + } + + llvm::StringRef getStringRef(IsLive isLive) const; + void print(llvm::raw_ostream &OS) const; + void dump() const; + +protected: + void markBlockLive(SILBasicBlock *bb, unsigned bitNo, IsLive isLive) { + assert(isLive != Dead && "erasing live blocks isn't implemented."); + auto iterAndInserted = + liveBlocks.insert(std::make_pair(bb, LivenessSmallBitVector())); + if (iterAndInserted.second) { + // We initialize the size of the small bit vector here rather than in + // liveBlocks.insert above to prevent us from allocating upon failure if + // we have more than SmallBitVector's small size number of bits. + auto &insertedBV = iterAndInserted.first->getSecond(); + insertedBV.init(numBitsToTrack); + insertedBV.setLiveness(bitNo, bitNo + 1, isLive); + if (discoveredBlocks) + discoveredBlocks->push_back(bb); + } else { + // If we are dead, always update to the new liveness. + switch (iterAndInserted.first->getSecond().getLiveness(bitNo)) { + case Dead: + iterAndInserted.first->getSecond().setLiveness(bitNo, bitNo + 1, + isLive); + break; + case LiveWithin: + if (isLive == LiveOut) { + // Update the existing entry to be live-out. + iterAndInserted.first->getSecond().setLiveness(bitNo, bitNo + 1, + LiveOut); + } + break; + case LiveOut: + break; + } + } + } + + void markBlockLive(SILBasicBlock *bb, unsigned startBitNo, unsigned endBitNo, + IsLive isLive) { + for (unsigned index : range(startBitNo, endBitNo)) { + markBlockLive(bb, index, isLive); + } + } + +private: + /// A helper routine that as a fast path handles the scalar case. We do not + /// handle the mult-bit case today since the way the code is written today + /// assumes we process a bit at a time. + /// + /// TODO: Make a multi-bit query for efficiency reasons. + void computeScalarUseBlockLiveness(SILBasicBlock *userBB, + unsigned startBitNo); +}; + /// This is exactly like pruned liveness except that instead of tracking a /// single bit of liveness, it tracks multiple bits of liveness for leaf type /// tree nodes of an allocation one is calculating pruned liveness for. @@ -383,7 +639,7 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, /// the type T being a child of T in the tree. We say recursively since the tree /// unfolds for F and its children as well. class FieldSensitivePrunedLiveness { - PrunedLiveBlocks liveBlocks; + FieldSensitivePrunedLiveBlocks liveBlocks; public: struct InterestingUser { @@ -517,22 +773,22 @@ class FieldSensitivePrunedLiveness { void updateForUse(SILInstruction *user, TypeTreeLeafTypeRange span, bool lifetimeEnding); - void getBlockLiveness( - SILBasicBlock *bb, TypeTreeLeafTypeRange span, - SmallVectorImpl &resultingFoundLiveness) const { + void getBlockLiveness(SILBasicBlock *bb, TypeTreeLeafTypeRange span, + SmallVectorImpl + &resultingFoundLiveness) const { liveBlocks.getBlockLiveness(bb, span.startEltOffset, span.endEltOffset, resultingFoundLiveness); } /// Return the liveness for this specific sub-element of our root value. - PrunedLiveBlocks::IsLive getBlockLiveness(SILBasicBlock *bb, - unsigned subElementNumber) const { + FieldSensitivePrunedLiveBlocks::IsLive + getBlockLiveness(SILBasicBlock *bb, unsigned subElementNumber) const { return liveBlocks.getBlockLiveness(bb, subElementNumber); } - void getBlockLiveness( - SILBasicBlock *bb, - SmallVectorImpl &foundLiveness) const { + void getBlockLiveness(SILBasicBlock *bb, + SmallVectorImpl + &foundLiveness) const { liveBlocks.getBlockLiveness(bb, 0, liveBlocks.getNumBitsToTrack(), foundLiveness); } diff --git a/lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp b/lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp index 3cfd612fc6c8f..48d41cd223f9d 100644 --- a/lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp +++ b/lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp @@ -19,7 +19,6 @@ #include "swift/SIL/BasicBlockDatastructures.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/OwnershipUtils.h" -#include "swift/SIL/PrunedLiveness.h" #include "swift/SIL/SILBuilder.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/ScopedAddressUtils.h" @@ -445,6 +444,102 @@ void TypeTreeLeafTypeRange::constructProjectionsForNeededElements( } } +//===----------------------------------------------------------------------===// +// MARK: FieldSensitivePrunedLiveBlocks +//===----------------------------------------------------------------------===// + +void FieldSensitivePrunedLiveBlocks::computeScalarUseBlockLiveness( + SILBasicBlock *userBB, unsigned bitNo) { + // If, we are visiting this block, then it is not already LiveOut. Mark it + // LiveWithin to indicate a liveness boundary within the block. + markBlockLive(userBB, bitNo, LiveWithin); + + BasicBlockWorklist worklist(userBB->getFunction()); + worklist.push(userBB); + + while (auto *block = worklist.pop()) { + // The popped `bb` is live; now mark all its predecessors LiveOut. + // + // Traversal terminates at any previously visited block, including the + // blocks initialized as definition blocks. + for (auto *predBlock : block->getPredecessorBlocks()) { + switch (getBlockLiveness(predBlock, bitNo)) { + case Dead: + worklist.pushIfNotVisited(predBlock); + LLVM_FALLTHROUGH; + case LiveWithin: + markBlockLive(predBlock, bitNo, LiveOut); + break; + case LiveOut: + break; + } + } + } +} + +/// Update the current def's liveness based on one specific use instruction. +/// +/// Return the updated liveness of the \p use block (LiveOut or LiveWithin). +/// +/// Terminators are not live out of the block. +void FieldSensitivePrunedLiveBlocks::updateForUse( + SILInstruction *user, unsigned startBitNo, unsigned endBitNo, + SmallVectorImpl &resultingLivenessInfo) { + resultingLivenessInfo.clear(); + + SWIFT_ASSERT_ONLY(seenUse = true); + + auto *bb = user->getParent(); + getBlockLiveness(bb, startBitNo, endBitNo, resultingLivenessInfo); + + for (auto pair : llvm::enumerate(resultingLivenessInfo)) { + unsigned index = pair.index(); + unsigned specificBitNo = startBitNo + index; + switch (pair.value()) { + case LiveOut: + case LiveWithin: + continue; + case Dead: { + // This use block has not yet been marked live. Mark it and its + // predecessor blocks live. + computeScalarUseBlockLiveness(bb, specificBitNo); + resultingLivenessInfo.push_back(getBlockLiveness(bb, specificBitNo)); + continue; + } + } + llvm_unreachable("covered switch"); + } +} + +llvm::StringRef +FieldSensitivePrunedLiveBlocks::getStringRef(IsLive isLive) const { + switch (isLive) { + case Dead: + return "Dead"; + case LiveWithin: + return "LiveWithin"; + case LiveOut: + return "LiveOut"; + } +} + +void FieldSensitivePrunedLiveBlocks::print(llvm::raw_ostream &OS) const { + if (!discoveredBlocks) { + OS << "No deterministic live block list\n"; + return; + } + SmallVector isLive; + for (auto *block : *discoveredBlocks) { + block->printAsOperand(OS); + OS << ": "; + for (unsigned i : range(getNumBitsToTrack())) + OS << getStringRef(this->getBlockLiveness(block, i)) << ", "; + OS << "\n"; + } +} + +void FieldSensitivePrunedLiveBlocks::dump() const { print(llvm::dbgs()); } + //===----------------------------------------------------------------------===// // MARK: FieldSensitiveLiveness //===----------------------------------------------------------------------===// @@ -452,7 +547,7 @@ void TypeTreeLeafTypeRange::constructProjectionsForNeededElements( void FieldSensitivePrunedLiveness::updateForUse(SILInstruction *user, TypeTreeLeafTypeRange range, bool lifetimeEnding) { - SmallVector resultingLiveness; + SmallVector resultingLiveness; liveBlocks.updateForUse(user, range.startEltOffset, range.endEltOffset, resultingLiveness); @@ -479,7 +574,7 @@ bool FieldSensitivePrunedLiveRange::isWithinBoundary( return true; } - using IsLive = PrunedLiveBlocks::IsLive; + using IsLive = FieldSensitivePrunedLiveBlocks::IsLive; auto *block = inst->getParent(); @@ -491,12 +586,12 @@ bool FieldSensitivePrunedLiveRange::isWithinBoundary( LLVM_DEBUG(llvm::dbgs() << " Visiting bit: " << bit << '\n'); bool isLive = false; switch (pair.value()) { - case PrunedLiveBlocks::Dead: + case FieldSensitivePrunedLiveBlocks::Dead: LLVM_DEBUG(llvm::dbgs() << " Dead... continuing!\n"); // We are only not within the boundary if all of our bits are dead. We // track this via allDeadBits. So, just continue. continue; - case PrunedLiveBlocks::LiveOut: + case FieldSensitivePrunedLiveBlocks::LiveOut: // If we are LiveOut and are not a def block, then we know that we are // within the boundary for this bit. We consider ourselves to be within // the boundary if /any/ of our bits are within the boundary. So return @@ -513,7 +608,7 @@ bool FieldSensitivePrunedLiveRange::isWithinBoundary( LLVM_DEBUG(llvm::dbgs() << " LiveOut, but a def block... searching block!\n"); [[clang::fallthrough]]; - case PrunedLiveBlocks::LiveWithin: + case FieldSensitivePrunedLiveBlocks::LiveWithin: bool shouldContinue = false; if (!isLive) LLVM_DEBUG(llvm::dbgs() << " LiveWithin... searching block!\n"); @@ -576,13 +671,13 @@ bool FieldSensitivePrunedLiveRange::isWithinBoundary( return false; } -static StringRef getStringRef(PrunedLiveBlocks::IsLive isLive) { +static StringRef getStringRef(FieldSensitivePrunedLiveBlocks::IsLive isLive) { switch (isLive) { - case PrunedLiveBlocks::Dead: + case FieldSensitivePrunedLiveBlocks::Dead: return "Dead"; - case PrunedLiveBlocks::LiveWithin: + case FieldSensitivePrunedLiveBlocks::LiveWithin: return "LiveWithin"; - case PrunedLiveBlocks::LiveOut: + case FieldSensitivePrunedLiveBlocks::LiveOut: return "LiveOut"; } } @@ -594,7 +689,7 @@ void FieldSensitivePrunedLiveRange::computeBoundary( LLVM_DEBUG(llvm::dbgs() << "Liveness Boundary Compuation!\n"); - using IsLive = PrunedLiveBlocks::IsLive; + using IsLive = FieldSensitivePrunedLiveBlocks::IsLive; SmallVector isLiveTmp; for (SILBasicBlock *block : getDiscoveredBlocks()) { SWIFT_DEFER { isLiveTmp.clear(); }; @@ -610,9 +705,10 @@ void FieldSensitivePrunedLiveRange::computeBoundary( LLVM_DEBUG(llvm::dbgs() << "Bit: " << index << ". Liveness: " << getStringRef(pair.value()) << '\n'); switch (pair.value()) { - case PrunedLiveBlocks::LiveOut: + case FieldSensitivePrunedLiveBlocks::LiveOut: for (SILBasicBlock *succBB : block->getSuccessors()) { - if (getBlockLiveness(succBB, index) == PrunedLiveBlocks::Dead) { + if (getBlockLiveness(succBB, index) == + FieldSensitivePrunedLiveBlocks::Dead) { LLVM_DEBUG(llvm::dbgs() << "Marking succBB as boundary edge: bb" << succBB->getDebugID() << '\n'); boundary.getBoundaryEdgeBits(succBB).set(index); @@ -622,13 +718,13 @@ void FieldSensitivePrunedLiveRange::computeBoundary( boundary); foundAnyNonDead = true; break; - case PrunedLiveBlocks::LiveWithin: { + case FieldSensitivePrunedLiveBlocks::LiveWithin: { asImpl().findBoundariesInBlock(block, index, /*isLiveOut*/ false, boundary); foundAnyNonDead = true; break; } - case PrunedLiveBlocks::Dead: + case FieldSensitivePrunedLiveBlocks::Dead: // We do not assert here like in the normal pruned liveness // implementation since we can have dead on some bits and liveness along // others. @@ -682,7 +778,7 @@ void findBoundaryInNonDefBlock(SILBasicBlock *block, unsigned bitNo, FieldSensitivePrunedLivenessBoundary &boundary, const FieldSensitivePrunedLiveness &liveness) { assert(liveness.getBlockLiveness(block, bitNo) == - PrunedLiveBlocks::LiveWithin); + FieldSensitivePrunedLiveBlocks::LiveWithin); LLVM_DEBUG(llvm::dbgs() << "Looking for boundary in non-def block\n"); for (SILInstruction &inst : llvm::reverse(*block)) { @@ -895,7 +991,7 @@ void FieldSensitiveMultiDefPrunedLiveRange::findBoundariesInBlock( if (llvm::all_of(block->getPredecessorBlocks(), [&](SILBasicBlock *predBlock) -> bool { return getBlockLiveness(predBlock, bitNo) == - PrunedLiveBlocks::IsLive::LiveOut; + FieldSensitivePrunedLiveBlocks::IsLive::LiveOut; })) { boundary.getBoundaryEdgeBits(block).set(bitNo); } diff --git a/lib/SIL/Utils/PrunedLiveness.cpp b/lib/SIL/Utils/PrunedLiveness.cpp index d83223efcf577..97663ad5daa57 100644 --- a/lib/SIL/Utils/PrunedLiveness.cpp +++ b/lib/SIL/Utils/PrunedLiveness.cpp @@ -16,8 +16,9 @@ #include "swift/SIL/BasicBlockDatastructures.h" #include "swift/SIL/BasicBlockUtils.h" #include "swift/SIL/OwnershipUtils.h" -#include "swift/SIL/ScopedAddressUtils.h" #include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILValue.h" +#include "swift/SIL/ScopedAddressUtils.h" using namespace swift; diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp index a8dd7223b8c00..b719beff32a34 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp @@ -1386,7 +1386,7 @@ bool GlobalLivenessChecker::testInstVectorLiveness( // If we didn't find a single block error, then we need to go search for our // liveness error in successor blocks. We know that this means that our // current block must be live out. Do a quick check just to be careful. - using IsLive = PrunedLiveBlocks::IsLive; + using IsLive = FieldSensitivePrunedLiveBlocks::IsLive; SmallVector isLiveArray; #ifndef NDEBUG liveness.getBlockLiveness(errorUser->getParent(), errorSpan, isLiveArray); From d5aea69335e7d2f816fffb8ce3140461f021191c Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Feb 2023 10:59:34 -0800 Subject: [PATCH 14/98] [sil] Refactor field sensitive pruned liveness so it can be used with different sized root values. --- .../swift/SIL/FieldSensitivePrunedLiveness.h | 115 +++++++++++++++--- .../Utils/FieldSensitivePrunedLiveness.cpp | 1 + .../Mandatory/MoveOnlyObjectChecker.h | 3 +- 3 files changed, 99 insertions(+), 20 deletions(-) diff --git a/include/swift/SIL/FieldSensitivePrunedLiveness.h b/include/swift/SIL/FieldSensitivePrunedLiveness.h index 566ea20cc8e34..1728e0955bd3d 100644 --- a/include/swift/SIL/FieldSensitivePrunedLiveness.h +++ b/include/swift/SIL/FieldSensitivePrunedLiveness.h @@ -493,7 +493,10 @@ class FieldSensitivePrunedLiveBlocks { /// Number of bits of liveness to track. By default 1. Used to track multiple /// liveness bits. - unsigned numBitsToTrack; + /// + /// NOTE: After clearing, this is set to None to ensure that the user + /// reinitializes it as appropriate. + Optional numBitsToTrack; /// Optional vector of live blocks for clients that deterministically iterate. SmallVectorImpl *discoveredBlocks; @@ -503,40 +506,56 @@ class FieldSensitivePrunedLiveBlocks { public: FieldSensitivePrunedLiveBlocks( - unsigned numBitsToTrack, SmallVectorImpl *discoveredBlocks = nullptr) - : numBitsToTrack(numBitsToTrack), discoveredBlocks(discoveredBlocks) { + : numBitsToTrack(), discoveredBlocks(discoveredBlocks) { assert(!discoveredBlocks || discoveredBlocks->empty()); } - unsigned getNumBitsToTrack() const { return numBitsToTrack; } + bool isInitialized() const { return numBitsToTrack.hasValue(); } + + unsigned getNumBitsToTrack() const { return *numBitsToTrack; } bool empty() const { return liveBlocks.empty(); } void clear() { liveBlocks.clear(); + if (discoveredBlocks) + discoveredBlocks->clear(); + numBitsToTrack = None; SWIFT_ASSERT_ONLY(seenUse = false); } - unsigned numLiveBlocks() const { return liveBlocks.size(); } + void init(unsigned inputNumBitsToTrack) { + clear(); + numBitsToTrack = inputNumBitsToTrack; + } + + unsigned numLiveBlocks() const { + assert(isInitialized()); + return liveBlocks.size(); + } /// If the constructor was provided with a vector to populate, then this /// returns the list of all live blocks with no duplicates. ArrayRef getDiscoveredBlocks() const { + assert(isInitialized()); return *discoveredBlocks; } void initializeDefBlock(SILBasicBlock *defBB, unsigned bitNo) { + assert(isInitialized()); markBlockLive(defBB, bitNo, LiveWithin); } void initializeDefBlock(SILBasicBlock *defBB, unsigned startBitNo, unsigned endBitNo) { + assert(isInitialized()); markBlockLive(defBB, startBitNo, endBitNo, LiveWithin); } /// Update this liveness result for a single use. IsLive updateForUse(SILInstruction *user, unsigned bitNo) { + assert(isInitialized()); auto *block = user->getParent(); auto liveness = getBlockLiveness(block, bitNo); if (liveness != Dead) @@ -551,6 +570,7 @@ class FieldSensitivePrunedLiveBlocks { SmallVectorImpl &resultingLiveness); IsLive getBlockLiveness(SILBasicBlock *bb, unsigned bitNo) const { + assert(isInitialized()); auto liveBlockIter = liveBlocks.find(bb); if (liveBlockIter == liveBlocks.end()) { return Dead; @@ -564,6 +584,7 @@ class FieldSensitivePrunedLiveBlocks { void getBlockLiveness(SILBasicBlock *bb, unsigned startBitNo, unsigned endBitNo, SmallVectorImpl &foundLivenessInfo) const { + assert(isInitialized()); auto liveBlockIter = liveBlocks.find(bb); if (liveBlockIter == liveBlocks.end()) { for (unsigned i : range(endBitNo - startBitNo)) { @@ -582,6 +603,8 @@ class FieldSensitivePrunedLiveBlocks { protected: void markBlockLive(SILBasicBlock *bb, unsigned bitNo, IsLive isLive) { + assert(isInitialized()); + assert(isLive != Dead && "erasing live blocks isn't implemented."); auto iterAndInserted = liveBlocks.insert(std::make_pair(bb, LivenessSmallBitVector())); @@ -590,7 +613,7 @@ class FieldSensitivePrunedLiveBlocks { // liveBlocks.insert above to prevent us from allocating upon failure if // we have more than SmallBitVector's small size number of bits. auto &insertedBV = iterAndInserted.first->getSecond(); - insertedBV.init(numBitsToTrack); + insertedBV.init(*numBitsToTrack); insertedBV.setLiveness(bitNo, bitNo + 1, isLive); if (discoveredBlocks) discoveredBlocks->push_back(bb); @@ -616,6 +639,7 @@ class FieldSensitivePrunedLiveBlocks { void markBlockLive(SILBasicBlock *bb, unsigned startBitNo, unsigned endBitNo, IsLive isLive) { + assert(isInitialized()); for (unsigned index : range(startBitNo, endBitNo)) { markBlockLive(bb, index, isLive); } @@ -675,10 +699,9 @@ class FieldSensitivePrunedLiveness { public: FieldSensitivePrunedLiveness( - SILFunction *fn, SILValue rootValue, + SILFunction *fn, SmallVectorImpl *discoveredBlocks = nullptr) - : liveBlocks(TypeSubElementCount(rootValue), discoveredBlocks), - rootValue(rootValue) {} + : liveBlocks(discoveredBlocks) {} bool empty() const { assert(!liveBlocks.empty() || users.empty()); @@ -688,26 +711,50 @@ class FieldSensitivePrunedLiveness { void clear() { liveBlocks.clear(); users.clear(); + rootValue = SILValue(); + } + + void init(SILValue newRootValue) { + clear(); + rootValue = newRootValue; + liveBlocks.init(TypeSubElementCount(newRootValue)); } - SILValue getRootValue() const { return rootValue; } - SILType getRootType() const { return rootValue->getType(); } + bool isInitialized() const { + return liveBlocks.isInitialized() && bool(rootValue); + } - unsigned numLiveBlocks() const { return liveBlocks.numLiveBlocks(); } + SILValue getRootValue() const { + assert(isInitialized()); + return rootValue; + } + + SILType getRootType() const { + assert(isInitialized()); + return rootValue->getType(); + } + + unsigned numLiveBlocks() const { + assert(isInitialized()); + return liveBlocks.numLiveBlocks(); + } TypeTreeLeafTypeRange getTopLevelSpan() const { + assert(isInitialized()); return TypeTreeLeafTypeRange(0, getNumSubElements()); } /// If the constructor was provided with a vector to populate, then this /// returns the list of all live blocks with no duplicates. ArrayRef getDiscoveredBlocks() const { + assert(isInitialized()); return liveBlocks.getDiscoveredBlocks(); } using UserRange = iterator_range *>; UserRange getAllUsers() const { + assert(isInitialized()); return llvm::make_range(users.begin(), users.end()); } @@ -716,6 +763,7 @@ class FieldSensitivePrunedLiveness { function_ref>( const std::pair &)>>; LifetimeEndingUserRange getAllLifetimeEndingUses() const { + assert(isInitialized()); function_ref>( const std::pair &)> op; @@ -733,6 +781,7 @@ class FieldSensitivePrunedLiveness { function_ref>( const std::pair &)>>; NonLifetimeEndingUserRange getAllNonLifetimeEndingUses() const { + assert(isInitialized()); function_ref>( const std::pair &)> op; @@ -749,6 +798,7 @@ class FieldSensitivePrunedLiveness { UserRange, function_ref &)>>; UserBlockRange getAllUserBlocks() const { + assert(isInitialized()); function_ref &)> op; @@ -758,6 +808,7 @@ class FieldSensitivePrunedLiveness { } void initializeDefBlock(SILBasicBlock *defBB, TypeTreeLeafTypeRange span) { + assert(isInitialized()); liveBlocks.initializeDefBlock(defBB, span.startEltOffset, span.endEltOffset); } @@ -803,6 +854,7 @@ class FieldSensitivePrunedLiveness { /// interesting use of the current def and whether it ends the lifetime. std::pair> isInterestingUser(SILInstruction *user) const { + assert(isInitialized()); auto useIter = users.find(user); if (useIter == users.end()) return {NonUser, None}; @@ -936,9 +988,9 @@ class FieldSensitivePrunedLiveRange : public FieldSensitivePrunedLiveness { public: FieldSensitivePrunedLiveRange( - SILFunction *fn, SILValue rootValue, + SILFunction *fn, SmallVectorImpl *discoveredBlocks = nullptr) - : FieldSensitivePrunedLiveness(fn, rootValue, discoveredBlocks) {} + : FieldSensitivePrunedLiveness(fn, discoveredBlocks) {} /// Check if \p inst occurs in between the definition of a def and the /// liveness boundary for bits in \p span. @@ -980,6 +1032,8 @@ class FieldSensitivePrunedLiveRange : public FieldSensitivePrunedLiveness { /// could be handled by adding an updateForUseBeforeFirstDef() API. class FieldSensitiveSSAPrunedLiveRange : public FieldSensitivePrunedLiveRange { + using Super = FieldSensitivePrunedLiveRange; + std::pair> def = {{}, {}}; /// None for arguments. @@ -988,11 +1042,12 @@ class FieldSensitiveSSAPrunedLiveRange public: FieldSensitiveSSAPrunedLiveRange( - SILFunction *fn, SILValue rootValue, + SILFunction *fn, SmallVectorImpl *discoveredBlocks = nullptr) - : FieldSensitivePrunedLiveRange(fn, rootValue, discoveredBlocks) {} + : FieldSensitivePrunedLiveRange(fn, discoveredBlocks) {} std::pair> getDef() const { + assert(isInitialized()); return def; } @@ -1003,6 +1058,7 @@ class FieldSensitiveSSAPrunedLiveRange } void initializeDef(SILValue def, TypeTreeLeafTypeRange span) { + assert(Super::isInitialized()); assert(!this->def.first && !this->def.second && "reinitialization"); this->def = {def, span}; @@ -1010,7 +1066,9 @@ class FieldSensitiveSSAPrunedLiveRange initializeDefBlock(def->getParentBlock(), span); } - bool isInitialized() const { return bool(def.first) && bool(def.second); } + bool isInitialized() const { + return Super::isInitialized() && bool(def.first) && bool(def.second); + } bool isDef(SILInstruction *inst, unsigned bit) const { return inst == defInst.first && defInst.second->contains(bit); @@ -1037,6 +1095,9 @@ class FieldSensitiveSSAPrunedLiveRange class FieldSensitiveMultiDefPrunedLiveRange : public FieldSensitivePrunedLiveRange< FieldSensitiveMultiDefPrunedLiveRange> { + using Super = + FieldSensitivePrunedLiveRange; + // TODO: See if we can make this more efficient. SmallFrozenMultiMap defs; SmallFrozenMultiMap defBlocks; @@ -1045,7 +1106,14 @@ class FieldSensitiveMultiDefPrunedLiveRange FieldSensitiveMultiDefPrunedLiveRange( SILFunction *fn, SILValue rootValue, SmallVectorImpl *discoveredBlocks = nullptr) - : FieldSensitivePrunedLiveRange(fn, rootValue, discoveredBlocks) {} + : FieldSensitivePrunedLiveRange(fn, discoveredBlocks) { + // We init here since we do not allow for reinitialization to occur. + Super::init(rootValue); + } + + void init(SILValue rootValue) { + llvm_unreachable("multi-def liveness cannot be reused"); + } void clear() { llvm_unreachable("multi-def liveness cannot be reused"); } @@ -1055,11 +1123,13 @@ class FieldSensitiveMultiDefPrunedLiveRange /// Internally this freezes our def/defblocks arrays so we can use them as /// maps. void finishedInitializationOfDefs() { + assert(isInitialized()); defs.setFrozen(); defBlocks.setFrozen(); } void initializeDef(SILValue def, TypeTreeLeafTypeRange span) { + assert(Super::isInitialized()); defs.insert(def, span); auto *block = def->getParentBlock(); defBlocks.insert(block, span); @@ -1067,16 +1137,18 @@ class FieldSensitiveMultiDefPrunedLiveRange } void initializeDef(SILInstruction *def, TypeTreeLeafTypeRange span) { + assert(Super::isInitialized()); defs.insert(cast(def), span); auto *block = def->getParent(); defBlocks.insert(block, span); initializeDefBlock(block, span); } - bool isInitialized() const { return !defs.empty(); } + bool isInitialized() const { return Super::isInitialized() && !defs.empty(); } /// Return true if this block is a def block for this specific bit. bool isDefBlock(SILBasicBlock *block, unsigned bit) const { + assert(isInitialized()); auto iter = defBlocks.find(block); if (!iter) return false; @@ -1085,6 +1157,7 @@ class FieldSensitiveMultiDefPrunedLiveRange } bool isDefBlock(SILBasicBlock *block, TypeTreeLeafTypeRange span) const { + assert(isInitialized()); auto iter = defBlocks.find(block); if (!iter) return false; @@ -1094,6 +1167,7 @@ class FieldSensitiveMultiDefPrunedLiveRange } bool isDef(SILInstruction *inst, unsigned bit) const { + assert(isInitialized()); auto iter = defs.find(cast(inst)); if (!iter) return false; @@ -1102,6 +1176,7 @@ class FieldSensitiveMultiDefPrunedLiveRange } bool isDef(SILValue value, unsigned bit) const { + assert(isInitialized()); auto iter = defs.find(cast(value)); if (!iter) return false; @@ -1110,6 +1185,7 @@ class FieldSensitiveMultiDefPrunedLiveRange } bool isDef(SILInstruction *inst, TypeTreeLeafTypeRange span) const { + assert(isInitialized()); auto iter = defs.find(cast(inst)); if (!iter) return false; @@ -1119,6 +1195,7 @@ class FieldSensitiveMultiDefPrunedLiveRange } bool isDef(SILValue value, TypeTreeLeafTypeRange span) const { + assert(isInitialized()); auto iter = defs.find(cast(value)); if (!iter) return false; diff --git a/lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp b/lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp index 48d41cd223f9d..d45cf19420347 100644 --- a/lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp +++ b/lib/SIL/Utils/FieldSensitivePrunedLiveness.cpp @@ -485,6 +485,7 @@ void FieldSensitivePrunedLiveBlocks::computeScalarUseBlockLiveness( void FieldSensitivePrunedLiveBlocks::updateForUse( SILInstruction *user, unsigned startBitNo, unsigned endBitNo, SmallVectorImpl &resultingLivenessInfo) { + assert(isInitialized()); resultingLivenessInfo.clear(); SWIFT_ASSERT_ONLY(seenUse = true); diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h index e1339931bdbb8..fcfc5478d9f54 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h +++ b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h @@ -208,7 +208,8 @@ struct BorrowToDestructureTransform { DiagnosticEmitter &diagnosticEmitter, PostOrderAnalysis *poa, SmallVectorImpl &discoveredBlocks) : allocator(allocator), mmci(mmci), diagnosticEmitter(diagnosticEmitter), - liveness(mmci->getFunction(), mmci, &discoveredBlocks), poa(poa) { + liveness(mmci->getFunction(), &discoveredBlocks), poa(poa) { + liveness.init(mmci); liveness.initializeDef(mmci, TypeTreeLeafTypeRange(mmci)); } From a4985bf05661d38a369641d51453b34b8bbcf79c Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Feb 2023 11:12:12 -0800 Subject: [PATCH 15/98] [move-only] Change MoveOnlyBorrowToDestructureTransform into a driver invoked by the tester/main checker pass rather than a utility. This is in preparation for sinking the main computation of the pass into an implementation local helper struct that can process multiple values. I am doing this so we can use the same code to process switch_enum arguments and am using the current implementation to make sure I don't break anything. --- .../MoveOnlyBorrowToDestructureTransform.cpp | 77 +++++++++++++++--- ...OnlyBorrowToDestructureTransformTester.cpp | 80 +------------------ .../Mandatory/MoveOnlyObjectChecker.cpp | 62 ++------------ .../Mandatory/MoveOnlyObjectChecker.h | 28 ++++--- 4 files changed, 94 insertions(+), 153 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp index 6e2fad79c06c3..78eb04b1f6c18 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp @@ -143,8 +143,8 @@ bool BorrowToDestructureTransform::gatherUses( blocksToUses.insert( nextUse->getParentBlock(), {nextUse, {*leafRange, false /*is lifetime ending*/}}); - liveness.updateForUse(nextUse->getUser(), *leafRange, - false /*is lifetime ending*/); + liveness->updateForUse(nextUse->getUser(), *leafRange, + false /*is lifetime ending*/); instToInterestingOperandIndexMap.insert(nextUse->getUser(), nextUse); continue; } @@ -167,8 +167,8 @@ bool BorrowToDestructureTransform::gatherUses( destructureNeedingUses.push_back(nextUse); blocksToUses.insert(nextUse->getParentBlock(), {nextUse, {*leafRange, true /*is lifetime ending*/}}); - liveness.updateForUse(nextUse->getUser(), *leafRange, - true /*is lifetime ending*/); + liveness->updateForUse(nextUse->getUser(), *leafRange, + true /*is lifetime ending*/); instToInterestingOperandIndexMap.insert(nextUse->getUser(), nextUse); continue; } @@ -207,7 +207,7 @@ void BorrowToDestructureTransform::checkForErrorsOnSameInstruction() { // check if any of our consuming uses that are on the boundary are used by the // same instruction as a different consuming or non-consuming use. instToInterestingOperandIndexMap.setFrozen(); - SmallBitVector usedBits(liveness.getNumSubElements()); + SmallBitVector usedBits(liveness->getNumSubElements()); for (auto instRangePair : instToInterestingOperandIndexMap.getRange()) { SWIFT_DEFER { usedBits.reset(); }; @@ -321,7 +321,7 @@ void BorrowToDestructureTransform::checkDestructureUsesOnBoundary() const { << " DestructureNeedingUse: " << *use->getUser()); auto destructureUseSpan = *TypeTreeLeafTypeRange::get(use->get(), mmci); - if (!liveness.isWithinBoundary(use->getUser(), destructureUseSpan)) { + if (!liveness->isWithinBoundary(use->getUser(), destructureUseSpan)) { LLVM_DEBUG(llvm::dbgs() << " On boundary or within boundary! No error!\n"); continue; @@ -336,8 +336,9 @@ void BorrowToDestructureTransform::checkDestructureUsesOnBoundary() const { // TODO: Fix diagnostic to use destructure needing use and boundary // uses. LLVM_DEBUG(llvm::dbgs() << " Within boundary! Emitting error!\n"); - FieldSensitivePrunedLivenessBoundary boundary(liveness.getNumSubElements()); - liveness.computeBoundary(boundary); + FieldSensitivePrunedLivenessBoundary boundary( + liveness->getNumSubElements()); + liveness->computeBoundary(boundary); diagnosticEmitter.emitObjectDestructureNeededWithinBorrowBoundary( mmci, use->getUser(), destructureUseSpan, boundary); } @@ -1069,7 +1070,7 @@ void BorrowToDestructureTransform::rewriteUses() { << "Performing BorrowToDestructureTransform::rewriteUses()!\n"); llvm::SmallPtrSet seenOperands; - SmallBitVector bitsNeededInBlock(liveness.getNumSubElements()); + SmallBitVector bitsNeededInBlock(liveness->getNumSubElements()); IntervalMapAllocator::Map typeSpanToValue(allocator.get()); auto *fn = mmci->getFunction(); @@ -1457,3 +1458,61 @@ void BorrowToDestructureTransform::cleanup( // And finally do the same thing for our initial copy_value. addCompensatingDestroys(liveness, boundary, initialValue); } + +//===----------------------------------------------------------------------===// +// MARK: Top Level Entrypoint +//===----------------------------------------------------------------------===// + +bool BorrowToDestructureTransform::transform() { + StackList borrowWorklist(mmci->getFunction()); + + // If we failed to gather borrows due to the transform not understanding part + // of the SIL, fail and return false. + if (!BorrowToDestructureTransform::gatherBorrows(mmci, borrowWorklist)) + return false; + + // If we do not have any borrows to process, return true early to show we + // succeeded in processing. + if (borrowWorklist.empty()) + return true; + + // Attempt to gather uses. Return false if we saw something that we did not + // understand. + if (!gatherUses(borrowWorklist)) + return false; + + // Next make sure that any destructure needing instructions are on the + // boundary in a per bit field sensitive manner. + unsigned diagnosticCount = diagnosticEmitter.getDiagnosticCount(); + checkDestructureUsesOnBoundary(); + + // If we emitted any diagnostic, break out. We return true since we actually + // succeeded in our processing by finding the error. We only return false if + // we want to tell the rest of the checker that there was an internal + // compiler error that we need to emit a "compiler doesn't understand + // error". + if (diagnosticCount != diagnosticEmitter.getDiagnosticCount()) + return true; + + // Then check if we had two consuming uses on the same instruction or a + // consuming/non-consuming use on the same isntruction. + checkForErrorsOnSameInstruction(); + + // If we emitted any diagnostic, break out. We return true since we actually + // succeeded in our processing by finding the error. We only return false if + // we want to tell the rest of the checker that there was an internal + // compiler error that we need to emit a "compiler doesn't understand + // error". + if (diagnosticCount != diagnosticEmitter.getDiagnosticCount()) + return true; + + // At this point, we know that all of our destructure requiring uses are on + // the boundary of our live range. Now we need to do the rewriting. + blockToAvailableValues.emplace(*liveness); + rewriteUses(); + + // Now that we have done our rewritting, we need to do a few cleanups. + cleanup(borrowWorklist); + + return true; +} diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp index 8b01609eb174f..8e9d1b2a9b8bc 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp @@ -53,85 +53,9 @@ static bool runTransform(SILFunction *fn, auto *mmci = moveIntroducersToProcess.back(); moveIntroducersToProcess = moveIntroducersToProcess.drop_back(); - unsigned currentDiagnosticCount = diagnosticEmitter.getDiagnosticCount(); - - StackList borrowWorklist(mmci->getFunction()); - - // If we failed to gather borrows due to the transform not understanding - // part of the SIL, emit a diagnostic, RAUW the mark must check, and - // continue. - if (!BorrowToDestructureTransform::gatherBorrows(mmci, borrowWorklist)) { - diagnosticEmitter.emitCheckerDoesntUnderstandDiagnostic(mmci); - mmci->replaceAllUsesWith(mmci->getOperand()); - mmci->eraseFromParent(); - madeChange = true; - continue; - } - - // If we do not have any borrows to process, continue and process the next - // instruction. - if (borrowWorklist.empty()) - continue; - - SmallVector discoveredBlocks; - - // Now that we have found all of our borrows, we want to find struct_extract - // uses of our borrow as well as any operands that cannot use an owned - // value. - SWIFT_DEFER { discoveredBlocks.clear(); }; BorrowToDestructureTransform transform(allocator, mmci, diagnosticEmitter, - poa, discoveredBlocks); - - // Attempt to gather uses. Return if we saw something that we did not - // understand. Emit a compiler did not understand diagnostic, RAUW the mmci - // so later passes do not see it, and set madeChange to true. - if (!transform.gatherUses(borrowWorklist)) { - diagnosticEmitter.emitCheckerDoesntUnderstandDiagnostic(mmci); - mmci->replaceAllUsesWith(mmci->getOperand()); - mmci->eraseFromParent(); - madeChange = true; - continue; - } - - // Next make sure that any destructure needing instructions are on the - // boundary in a per bit field sensitive manner. - transform.checkDestructureUsesOnBoundary(); - - // If we emitted any diagnostic, set madeChange to true, eliminate our mmci, - // and continue. - if (currentDiagnosticCount != diagnosticEmitter.getDiagnosticCount()) { - mmci->replaceAllUsesWith(mmci->getOperand()); - mmci->eraseFromParent(); - madeChange = true; - continue; - } - - // Then check if we had two consuming uses on the same instruction or a - // consuming/non-consuming use on the same isntruction. - transform.checkForErrorsOnSameInstruction(); - - // If we emitted any diagnostic, set madeChange to true, eliminate our mmci, - // and continue. - if (currentDiagnosticCount != diagnosticEmitter.getDiagnosticCount()) { - mmci->replaceAllUsesWith(mmci->getOperand()); - mmci->eraseFromParent(); - madeChange = true; - continue; - } - - // At this point, we know that all of our destructure requiring uses are on - // the boundary of our live range. Now we need to do the rewriting. - transform.blockToAvailableValues.emplace(transform.liveness); - transform.rewriteUses(); - - // Now that we have done our rewritting, we need to do a few cleanups. - // - // NOTE: We do not eliminate our mark_must_check since we want later passes - // to do additional checking upon the mark_must_check including making sure - // that our destructures do not need cause the need for additional copies to - // be inserted. We only eliminate the mark_must_check if we emitted a - // diagnostic of some sort. - transform.cleanup(borrowWorklist); + poa); + transform.transform(); madeChange = true; } diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp index 1a9d1247c83d4..eb1dcfaf00f8b 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp @@ -422,65 +422,13 @@ struct MoveOnlyChecker { bool MoveOnlyChecker::convertBorrowExtractsToOwnedDestructures( MarkMustCheckInst *mmci, DiagnosticEmitter &diagnosticEmitter, DominanceInfo *domTree, PostOrderAnalysis *poa) { - StackList borrowWorklist(mmci->getFunction()); - - // If we failed to gather borrows due to the transform not understanding part - // of the SIL, fail and return false. - if (!BorrowToDestructureTransform::gatherBorrows(mmci, borrowWorklist)) - return false; - - // If we do not have any borrows to process, return true early to show we - // succeeded in processing. - if (borrowWorklist.empty()) { - LLVM_DEBUG(llvm::dbgs() << " Did not find any borrows to process!\n"); - return true; - } - - SmallVector discoveredBlocks; - - // Now that we have found all of our borrows, we want to find struct_extract - // uses of our borrow as well as any operands that cannot use an owned value. - SWIFT_DEFER { discoveredBlocks.clear(); }; BorrowToDestructureTransform transform(allocator, mmci, diagnosticEmitter, - poa, discoveredBlocks); - - // Attempt to gather uses. Return false if we saw something that we did not - // understand. - if (!transform.gatherUses(borrowWorklist)) + poa); + if (!transform.transform()) { + LLVM_DEBUG(llvm::dbgs() + << "Failed to perform borrow to destructure transform!\n"); return false; - - // Next make sure that any destructure needing instructions are on the - // boundary in a per bit field sensitive manner. - unsigned diagnosticCount = diagnosticEmitter.getDiagnosticCount(); - transform.checkDestructureUsesOnBoundary(); - - // If we emitted any diagnostic, break out. We return true since we actually - // succeeded in our processing by finding the error. We only return false if - // we want to tell the rest of the checker that there was an internal - // compiler error that we need to emit a "compiler doesn't understand - // error". - if (diagnosticCount != diagnosticEmitter.getDiagnosticCount()) - return true; - - // Then check if we had two consuming uses on the same instruction or a - // consuming/non-consuming use on the same isntruction. - transform.checkForErrorsOnSameInstruction(); - - // If we emitted any diagnostic, break out. We return true since we actually - // succeeded in our processing by finding the error. We only return false if - // we want to tell the rest of the checker that there was an internal - // compiler error that we need to emit a "compiler doesn't understand - // error". - if (diagnosticCount != diagnosticEmitter.getDiagnosticCount()) - return true; - - // At this point, we know that all of our destructure requiring uses are on - // the boundary of our live range. Now we need to do the rewriting. - transform.blockToAvailableValues.emplace(transform.liveness); - transform.rewriteUses(); - - // Now that we have done our rewritting, we need to do a few cleanups. - transform.cleanup(borrowWorklist); + } return true; } diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h index fcfc5478d9f54..09286c6390456 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h +++ b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h @@ -118,7 +118,8 @@ struct OSSACanonicalizer { class DiagnosticEmitter; -struct BorrowToDestructureTransform { +class BorrowToDestructureTransform { +public: class IntervalMapAllocator { public: using Map = llvm::IntervalMap< @@ -182,10 +183,12 @@ struct BorrowToDestructureTransform { } }; +private: IntervalMapAllocator &allocator; MarkMustCheckInst *mmci; DiagnosticEmitter &diagnosticEmitter; - FieldSensitiveSSAPrunedLiveRange liveness; + // Temporarily optional as this code is refactored. + Optional liveness; SmallVector destructureNeedingUses; PostOrderAnalysis *poa; PostOrderFunctionInfo *pofi = nullptr; @@ -203,16 +206,23 @@ struct BorrowToDestructureTransform { SmallFrozenMultiMap instToInterestingOperandIndexMap; - BorrowToDestructureTransform( - IntervalMapAllocator &allocator, MarkMustCheckInst *mmci, - DiagnosticEmitter &diagnosticEmitter, PostOrderAnalysis *poa, - SmallVectorImpl &discoveredBlocks) + SmallVector discoveredBlocks; + +public: + BorrowToDestructureTransform(IntervalMapAllocator &allocator, + MarkMustCheckInst *mmci, + DiagnosticEmitter &diagnosticEmitter, + PostOrderAnalysis *poa) : allocator(allocator), mmci(mmci), diagnosticEmitter(diagnosticEmitter), - liveness(mmci->getFunction(), &discoveredBlocks), poa(poa) { - liveness.init(mmci); - liveness.initializeDef(mmci, TypeTreeLeafTypeRange(mmci)); + liveness(), poa(poa) { + liveness.emplace(mmci->getFunction(), &discoveredBlocks); + liveness->init(mmci); + liveness->initializeDef(mmci, TypeTreeLeafTypeRange(mmci)); } + bool transform(); + +private: PostOrderFunctionInfo *getPostOrderFunctionInfo() { if (!pofi) pofi = poa->get(mmci->getFunction()); From 91686c0758ffc1dfc1b498e88a40e07c86d547dd Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Feb 2023 12:00:08 -0800 Subject: [PATCH 16/98] [move-only] Refactor some helper structs out of BorrowToDestructureTransform into a private namespace in preparation for sinking into the implementation file. --- .../MoveOnlyBorrowToDestructureTransform.cpp | 7 +- ...OnlyBorrowToDestructureTransformTester.cpp | 3 +- .../Mandatory/MoveOnlyObjectChecker.cpp | 2 +- .../Mandatory/MoveOnlyObjectChecker.h | 132 +++++++++--------- 4 files changed, 74 insertions(+), 70 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp index 78eb04b1f6c18..7a7fdedf1f1db 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp @@ -45,10 +45,7 @@ using namespace swift; using namespace swift::siloptimizer; - -namespace { -using AvailableValues = BorrowToDestructureTransform::AvailableValues; -} +using namespace swift::siloptimizer::borrowtodestructure; //===----------------------------------------------------------------------===// // MARK: Utilities @@ -1053,7 +1050,7 @@ BorrowToDestructureTransform::computeAvailableValues(SILBasicBlock *block) { #ifndef NDEBUG static LLVM_ATTRIBUTE_USED void -dumpIntervalMap(BorrowToDestructureTransform::IntervalMapAllocator::Map &map) { +dumpIntervalMap(IntervalMapAllocator::Map &map) { llvm::dbgs() << "Dumping Interval Map!\n"; for (auto bi = map.begin(), be = map.end(); bi != be; ++bi) { llvm::dbgs() << "Entry. Start: " << bi.start() << " End: " << bi.stop() diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp index 8e9d1b2a9b8bc..e69e726711832 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp @@ -38,6 +38,7 @@ using namespace swift; using namespace swift::siloptimizer; +using namespace swift::siloptimizer::borrowtodestructure; //===----------------------------------------------------------------------===// // Top Level Entrypoint @@ -47,7 +48,7 @@ static bool runTransform(SILFunction *fn, ArrayRef moveIntroducersToProcess, PostOrderAnalysis *poa, DiagnosticEmitter &diagnosticEmitter) { - BorrowToDestructureTransform::IntervalMapAllocator allocator; + IntervalMapAllocator allocator; bool madeChange = false; while (!moveIntroducersToProcess.empty()) { auto *mmci = moveIntroducersToProcess.back(); diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp index eb1dcfaf00f8b..50f8b0e55136a 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp @@ -390,7 +390,7 @@ struct MoveOnlyChecker { /// A set of mark_must_check that we are actually going to process. SmallSetVector moveIntroducersToProcess; - BorrowToDestructureTransform::IntervalMapAllocator allocator; + borrowtodestructure::IntervalMapAllocator allocator; MoveOnlyChecker(SILFunction *fn, DeadEndBlocks *deBlocks) : fn(fn) {} diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h index 09286c6390456..8f0dd988c3608 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h +++ b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h @@ -118,72 +118,78 @@ struct OSSACanonicalizer { class DiagnosticEmitter; -class BorrowToDestructureTransform { +namespace borrowtodestructure { + +class IntervalMapAllocator { public: - class IntervalMapAllocator { - public: - using Map = llvm::IntervalMap< - unsigned, SILValue, - llvm::IntervalMapImpl::NodeSizer::LeafSize, - llvm::IntervalMapHalfOpenInfo>; - - using Allocator = Map::Allocator; - - private: - /// Lazily initialized allocator. - Optional allocator; - - public: - Allocator &get() { - if (!allocator) - allocator.emplace(); - return *allocator; - } - }; - - // We reserve more bits that we need at the beginning so that we can avoid - // reallocating and potentially breaking our internal mutable array ref - // points into the data store. - struct AvailableValues { - MutableArrayRef values; - - SILValue operator[](unsigned index) const { return values[index]; } - SILValue &operator[](unsigned index) { return values[index]; } - unsigned size() const { return values.size(); } - - AvailableValues() : values() {} - AvailableValues(MutableArrayRef values) : values(values) {} - - void print(llvm::raw_ostream &os, const char *prefix = nullptr) const; - SWIFT_DEBUG_DUMP; - }; - - struct AvailableValueStore { - std::vector dataStore; - llvm::DenseMap blockToValues; - unsigned nextOffset = 0; - unsigned numBits; - - AvailableValueStore(const FieldSensitivePrunedLiveness &liveness) - : dataStore(liveness.getDiscoveredBlocks().size() * - liveness.getNumSubElements()), - numBits(liveness.getNumSubElements()) {} - - std::pair get(SILBasicBlock *block) { - auto iter = blockToValues.try_emplace(block, AvailableValues()); - - if (!iter.second) { - return {&iter.first->second, false}; - } - - iter.first->second.values = - MutableArrayRef(&dataStore[nextOffset], numBits); - nextOffset += numBits; - return {&iter.first->second, true}; - } - }; + using Map = llvm::IntervalMap< + unsigned, SILValue, + llvm::IntervalMapImpl::NodeSizer::LeafSize, + llvm::IntervalMapHalfOpenInfo>; + + using Allocator = Map::Allocator; private: + /// Lazily initialized allocator. + Optional allocator; + +public: + Allocator &get() { + if (!allocator) + allocator.emplace(); + return *allocator; + } +}; + +// We reserve more bits that we need at the beginning so that we can avoid +// reallocating and potentially breaking our internal mutable array ref +// points into the data store. +struct AvailableValues { + MutableArrayRef values; + + SILValue operator[](unsigned index) const { return values[index]; } + SILValue &operator[](unsigned index) { return values[index]; } + unsigned size() const { return values.size(); } + + AvailableValues() : values() {} + AvailableValues(MutableArrayRef values) : values(values) {} + + void print(llvm::raw_ostream &os, const char *prefix = nullptr) const; + SWIFT_DEBUG_DUMP; +}; + +struct AvailableValueStore { + std::vector dataStore; + llvm::DenseMap blockToValues; + unsigned nextOffset = 0; + unsigned numBits; + + AvailableValueStore(const FieldSensitivePrunedLiveness &liveness) + : dataStore(liveness.getDiscoveredBlocks().size() * + liveness.getNumSubElements()), + numBits(liveness.getNumSubElements()) {} + + std::pair get(SILBasicBlock *block) { + auto iter = blockToValues.try_emplace(block, AvailableValues()); + + if (!iter.second) { + return {&iter.first->second, false}; + } + + iter.first->second.values = + MutableArrayRef(&dataStore[nextOffset], numBits); + nextOffset += numBits; + return {&iter.first->second, true}; + } +}; + +} // namespace borrowtodestructure + +class BorrowToDestructureTransform { + using IntervalMapAllocator = borrowtodestructure::IntervalMapAllocator; + using AvailableValueStore = borrowtodestructure::AvailableValueStore; + using AvailableValues = borrowtodestructure::AvailableValues; + IntervalMapAllocator &allocator; MarkMustCheckInst *mmci; DiagnosticEmitter &diagnosticEmitter; From bd81c0d41d51c2ecfa8895a34c92900cb84f0a65 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Feb 2023 12:02:46 -0800 Subject: [PATCH 17/98] [move-only] Move header info for borrow to destructure transform into its own header and out of MoveOnlyObjectChecker.h --- .../Mandatory/MoveOnlyBorrowToDestructure.h | 181 ++++++++++++++++++ .../MoveOnlyBorrowToDestructureTransform.cpp | 1 + ...OnlyBorrowToDestructureTransformTester.cpp | 1 + .../Mandatory/MoveOnlyObjectChecker.cpp | 1 + .../Mandatory/MoveOnlyObjectChecker.h | 159 +-------------- 5 files changed, 186 insertions(+), 157 deletions(-) create mode 100644 lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h new file mode 100644 index 0000000000000..44bbc3563723c --- /dev/null +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h @@ -0,0 +1,181 @@ +//===--- MoveOnlyBorrowToDestructure.h ------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SILOPTIMIZER_MANDATORY_MOVEONLYBORROWTODESTRUCTURE_H +#define SWIFT_SILOPTIMIZER_MANDATORY_MOVEONLYBORROWTODESTRUCTURE_H + +#include "swift/Basic/FrozenMultiMap.h" +#include "swift/SIL/FieldSensitivePrunedLiveness.h" +#include "swift/SIL/StackList.h" +#include "swift/SILOptimizer/Analysis/PostOrderAnalysis.h" + +#include "llvm/ADT/IntervalMap.h" + +namespace swift { +namespace siloptimizer { + +class DiagnosticEmitter; + +namespace borrowtodestructure { + +class IntervalMapAllocator { +public: + using Map = llvm::IntervalMap< + unsigned, SILValue, + llvm::IntervalMapImpl::NodeSizer::LeafSize, + llvm::IntervalMapHalfOpenInfo>; + + using Allocator = Map::Allocator; + +private: + /// Lazily initialized allocator. + Optional allocator; + +public: + Allocator &get() { + if (!allocator) + allocator.emplace(); + return *allocator; + } +}; + +// We reserve more bits that we need at the beginning so that we can avoid +// reallocating and potentially breaking our internal mutable array ref +// points into the data store. +struct AvailableValues { + MutableArrayRef values; + + SILValue operator[](unsigned index) const { return values[index]; } + SILValue &operator[](unsigned index) { return values[index]; } + unsigned size() const { return values.size(); } + + AvailableValues() : values() {} + AvailableValues(MutableArrayRef values) : values(values) {} + + void print(llvm::raw_ostream &os, const char *prefix = nullptr) const; + SWIFT_DEBUG_DUMP; +}; + +struct AvailableValueStore { + std::vector dataStore; + llvm::DenseMap blockToValues; + unsigned nextOffset = 0; + unsigned numBits; + + AvailableValueStore(const FieldSensitivePrunedLiveness &liveness) + : dataStore(liveness.getDiscoveredBlocks().size() * + liveness.getNumSubElements()), + numBits(liveness.getNumSubElements()) {} + + std::pair get(SILBasicBlock *block) { + auto iter = blockToValues.try_emplace(block, AvailableValues()); + + if (!iter.second) { + return {&iter.first->second, false}; + } + + iter.first->second.values = + MutableArrayRef(&dataStore[nextOffset], numBits); + nextOffset += numBits; + return {&iter.first->second, true}; + } +}; + +} // namespace borrowtodestructure + +class BorrowToDestructureTransform { + using IntervalMapAllocator = borrowtodestructure::IntervalMapAllocator; + using AvailableValueStore = borrowtodestructure::AvailableValueStore; + using AvailableValues = borrowtodestructure::AvailableValues; + + IntervalMapAllocator &allocator; + MarkMustCheckInst *mmci; + DiagnosticEmitter &diagnosticEmitter; + // Temporarily optional as this code is refactored. + Optional liveness; + SmallVector destructureNeedingUses; + PostOrderAnalysis *poa; + PostOrderFunctionInfo *pofi = nullptr; + Optional blockToAvailableValues; + SILValue initialValue; + SmallVector createdDestructures; + SmallVector createdPhiArguments; + + using InterestingUser = FieldSensitivePrunedLiveness::InterestingUser; + SmallFrozenMultiMap, 8> + blocksToUses; + + /// A frozen multi-map we use to diagnose consuming uses that are used by the + /// same instruction as another consuming use or non-consuming use. + SmallFrozenMultiMap + instToInterestingOperandIndexMap; + + SmallVector discoveredBlocks; + +public: + BorrowToDestructureTransform(IntervalMapAllocator &allocator, + MarkMustCheckInst *mmci, + DiagnosticEmitter &diagnosticEmitter, + PostOrderAnalysis *poa) + : allocator(allocator), mmci(mmci), diagnosticEmitter(diagnosticEmitter), + liveness(), poa(poa) { + liveness.emplace(mmci->getFunction(), &discoveredBlocks); + liveness->init(mmci); + liveness->initializeDef(mmci, TypeTreeLeafTypeRange(mmci)); + } + + bool transform(); + +private: + PostOrderFunctionInfo *getPostOrderFunctionInfo() { + if (!pofi) + pofi = poa->get(mmci->getFunction()); + return pofi; + } + + /// Visit all of the uses of \p mmci and find all begin_borrows. + /// + /// Returns false if we found an escape and thus cannot process. It is assumed + /// that the caller will fail in such a case. + static bool gatherBorrows(MarkMustCheckInst *mmci, + StackList &borrowWorklist); + + /// Walk through our borrow's uses recursively and find uses that require only + /// a subset of the bits of our type. These are uses that are consuming uses + /// that are destructure uses. + bool gatherUses(StackList &borrowWorklist); + + /// Once we have gathered up all of our destructure uses and liveness + /// requiring uses, validate that all of our destructure uses are on our + /// boundary. Once we have done this, we know that it is safe to perform our + /// transform. + void checkDestructureUsesOnBoundary() const; + + /// Check for cases where we have two consuming uses on the same instruction + /// or a consuming/non-consuming use on the same instruction. + void checkForErrorsOnSameInstruction(); + + /// Rewrite all of the uses of our borrow on our borrow operand, performing + /// destructures as appropriate. + void rewriteUses(); + + /// After we have rewritten uses, cleanup the IR by deleting the original + /// borrow/struct_extract/copies and inserting compensating destroy_values. + void cleanup(StackList &borrowWorklist); + + AvailableValues &computeAvailableValues(SILBasicBlock *block); +}; + +} // namespace siloptimizer +} // namespace swift + +#endif diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp index 7a7fdedf1f1db..c5e1e4b2a29af 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp @@ -25,6 +25,7 @@ #define DEBUG_TYPE "sil-move-only-checker" +#include "MoveOnlyBorrowToDestructure.h" #include "MoveOnlyDiagnostics.h" #include "MoveOnlyObjectChecker.h" diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp index e69e726711832..40a33f89fd573 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp @@ -20,6 +20,7 @@ #define DEBUG_TYPE "sil-move-only-checker" +#include "MoveOnlyBorrowToDestructure.h" #include "MoveOnlyDiagnostics.h" #include "MoveOnlyObjectChecker.h" diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp index 50f8b0e55136a..338d110743c7f 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp @@ -57,6 +57,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/RecyclingAllocator.h" +#include "MoveOnlyBorrowToDestructure.h" #include "MoveOnlyDiagnostics.h" #include "MoveOnlyObjectChecker.h" diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h index 8f0dd988c3608..e643d78d834f6 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h +++ b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.h @@ -20,17 +20,14 @@ #ifndef SWIFT_SILOPTIMIZER_MANDATORY_MOVEONLYOBJECTCHECKER_H #define SWIFT_SILOPTIMIZER_MANDATORY_MOVEONLYOBJECTCHECKER_H -#include "swift/Basic/FrozenMultiMap.h" -#include "swift/SIL/FieldSensitivePrunedLiveness.h" -#include "swift/SIL/PrunedLiveness.h" -#include "swift/SIL/SILInstruction.h" #include "swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h" -#include "llvm/ADT/IntervalMap.h" #include "llvm/Support/Compiler.h" namespace swift { namespace siloptimizer { +class DiagnosticEmitter; + /// Wrapper around CanonicalizeOSSALifetime that we use to specialize its /// interface for our purposes. struct OSSACanonicalizer { @@ -116,158 +113,6 @@ struct OSSACanonicalizer { } }; -class DiagnosticEmitter; - -namespace borrowtodestructure { - -class IntervalMapAllocator { -public: - using Map = llvm::IntervalMap< - unsigned, SILValue, - llvm::IntervalMapImpl::NodeSizer::LeafSize, - llvm::IntervalMapHalfOpenInfo>; - - using Allocator = Map::Allocator; - -private: - /// Lazily initialized allocator. - Optional allocator; - -public: - Allocator &get() { - if (!allocator) - allocator.emplace(); - return *allocator; - } -}; - -// We reserve more bits that we need at the beginning so that we can avoid -// reallocating and potentially breaking our internal mutable array ref -// points into the data store. -struct AvailableValues { - MutableArrayRef values; - - SILValue operator[](unsigned index) const { return values[index]; } - SILValue &operator[](unsigned index) { return values[index]; } - unsigned size() const { return values.size(); } - - AvailableValues() : values() {} - AvailableValues(MutableArrayRef values) : values(values) {} - - void print(llvm::raw_ostream &os, const char *prefix = nullptr) const; - SWIFT_DEBUG_DUMP; -}; - -struct AvailableValueStore { - std::vector dataStore; - llvm::DenseMap blockToValues; - unsigned nextOffset = 0; - unsigned numBits; - - AvailableValueStore(const FieldSensitivePrunedLiveness &liveness) - : dataStore(liveness.getDiscoveredBlocks().size() * - liveness.getNumSubElements()), - numBits(liveness.getNumSubElements()) {} - - std::pair get(SILBasicBlock *block) { - auto iter = blockToValues.try_emplace(block, AvailableValues()); - - if (!iter.second) { - return {&iter.first->second, false}; - } - - iter.first->second.values = - MutableArrayRef(&dataStore[nextOffset], numBits); - nextOffset += numBits; - return {&iter.first->second, true}; - } -}; - -} // namespace borrowtodestructure - -class BorrowToDestructureTransform { - using IntervalMapAllocator = borrowtodestructure::IntervalMapAllocator; - using AvailableValueStore = borrowtodestructure::AvailableValueStore; - using AvailableValues = borrowtodestructure::AvailableValues; - - IntervalMapAllocator &allocator; - MarkMustCheckInst *mmci; - DiagnosticEmitter &diagnosticEmitter; - // Temporarily optional as this code is refactored. - Optional liveness; - SmallVector destructureNeedingUses; - PostOrderAnalysis *poa; - PostOrderFunctionInfo *pofi = nullptr; - Optional blockToAvailableValues; - SILValue initialValue; - SmallVector createdDestructures; - SmallVector createdPhiArguments; - - using InterestingUser = FieldSensitivePrunedLiveness::InterestingUser; - SmallFrozenMultiMap, 8> - blocksToUses; - - /// A frozen multi-map we use to diagnose consuming uses that are used by the - /// same instruction as another consuming use or non-consuming use. - SmallFrozenMultiMap - instToInterestingOperandIndexMap; - - SmallVector discoveredBlocks; - -public: - BorrowToDestructureTransform(IntervalMapAllocator &allocator, - MarkMustCheckInst *mmci, - DiagnosticEmitter &diagnosticEmitter, - PostOrderAnalysis *poa) - : allocator(allocator), mmci(mmci), diagnosticEmitter(diagnosticEmitter), - liveness(), poa(poa) { - liveness.emplace(mmci->getFunction(), &discoveredBlocks); - liveness->init(mmci); - liveness->initializeDef(mmci, TypeTreeLeafTypeRange(mmci)); - } - - bool transform(); - -private: - PostOrderFunctionInfo *getPostOrderFunctionInfo() { - if (!pofi) - pofi = poa->get(mmci->getFunction()); - return pofi; - } - - /// Visit all of the uses of \p mmci and find all begin_borrows. - /// - /// Returns false if we found an escape and thus cannot process. It is assumed - /// that the caller will fail in such a case. - static bool gatherBorrows(MarkMustCheckInst *mmci, - StackList &borrowWorklist); - - /// Walk through our borrow's uses recursively and find uses that require only - /// a subset of the bits of our type. These are uses that are consuming uses - /// that are destructure uses. - bool gatherUses(StackList &borrowWorklist); - - /// Once we have gathered up all of our destructure uses and liveness - /// requiring uses, validate that all of our destructure uses are on our - /// boundary. Once we have done this, we know that it is safe to perform our - /// transform. - void checkDestructureUsesOnBoundary() const; - - /// Check for cases where we have two consuming uses on the same instruction - /// or a consuming/non-consuming use on the same instruction. - void checkForErrorsOnSameInstruction(); - - /// Rewrite all of the uses of our borrow on our borrow operand, performing - /// destructures as appropriate. - void rewriteUses(); - - /// After we have rewritten uses, cleanup the IR by deleting the original - /// borrow/struct_extract/copies and inserting compensating destroy_values. - void cleanup(StackList &borrowWorklist); - - AvailableValues &computeAvailableValues(SILBasicBlock *block); -}; - /// Search for candidate object mark_must_checks. If we find one that does not /// fit a pattern that we understand, emit an error diagnostic telling the /// programmer that the move checker did not know how to recognize this code From 4b6a87a041a54bcba039952eb4046cd2806a4ee2 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Feb 2023 12:12:11 -0800 Subject: [PATCH 18/98] [move-only] Begin extracting out the impl of BorrowToDestructure from its interface by moving gatherUses onto the impl. --- .../Mandatory/MoveOnlyBorrowToDestructure.h | 9 ++- .../MoveOnlyBorrowToDestructureTransform.cpp | 63 ++++++++++++------- 2 files changed, 45 insertions(+), 27 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h index 44bbc3563723c..67f96027b9d67 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h @@ -90,9 +90,13 @@ struct AvailableValueStore { } }; +struct Implementation; + } // namespace borrowtodestructure class BorrowToDestructureTransform { + friend borrowtodestructure::Implementation; + using IntervalMapAllocator = borrowtodestructure::IntervalMapAllocator; using AvailableValueStore = borrowtodestructure::AvailableValueStore; using AvailableValues = borrowtodestructure::AvailableValues; @@ -149,11 +153,6 @@ class BorrowToDestructureTransform { static bool gatherBorrows(MarkMustCheckInst *mmci, StackList &borrowWorklist); - /// Walk through our borrow's uses recursively and find uses that require only - /// a subset of the bits of our type. These are uses that are consuming uses - /// that are destructure uses. - bool gatherUses(StackList &borrowWorklist); - /// Once we have gathered up all of our destructure uses and liveness /// requiring uses, validate that all of our destructure uses are on our /// boundary. Once we have done this, we know that it is safe to perform our diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp index c5e1e4b2a29af..fe889397cfe87 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp @@ -79,18 +79,25 @@ static SILLocation getSafeLoc(SILInstruction *inst) { } //===----------------------------------------------------------------------===// -// MARK: Convert Borrow Extracts To Owned Destructures +// MARK: Private Implementation //===----------------------------------------------------------------------===// -bool BorrowToDestructureTransform::gatherUses( - StackList &borrowWorklist) { +struct borrowtodestructure::Implementation { + BorrowToDestructureTransform &interface; + + bool gatherUses(SILValue value); + + /// Returns mark_must_check if we are processing borrows or the enum argument + /// if we are processing switch_enum. + SILValue getRootValue() const { return interface.mmci; } +}; + +bool Implementation::gatherUses(SILValue value) { LLVM_DEBUG(llvm::dbgs() << "Gathering uses!\n"); - StackList useWorklist(mmci->getFunction()); + StackList useWorklist(value->getFunction()); - for (auto *borrow : borrowWorklist) { - for (auto *use : borrow->getUses()) { - useWorklist.push_back(use); - } + for (auto *use : value->getUses()) { + useWorklist.push_back(use); } while (!useWorklist.empty()) { @@ -131,19 +138,21 @@ bool BorrowToDestructureTransform::gatherUses( // normal use, so we fall through. } - auto leafRange = TypeTreeLeafTypeRange::get(nextUse->get(), mmci); + auto leafRange = + TypeTreeLeafTypeRange::get(nextUse->get(), getRootValue()); if (!leafRange) { LLVM_DEBUG(llvm::dbgs() << " Failed to compute leaf range?!\n"); return false; } LLVM_DEBUG(llvm::dbgs() << " Found non lifetime ending use!\n"); - blocksToUses.insert( + interface.blocksToUses.insert( nextUse->getParentBlock(), {nextUse, {*leafRange, false /*is lifetime ending*/}}); - liveness->updateForUse(nextUse->getUser(), *leafRange, - false /*is lifetime ending*/); - instToInterestingOperandIndexMap.insert(nextUse->getUser(), nextUse); + interface.liveness->updateForUse(nextUse->getUser(), *leafRange, + false /*is lifetime ending*/); + interface.instToInterestingOperandIndexMap.insert(nextUse->getUser(), + nextUse); continue; } @@ -155,19 +164,22 @@ bool BorrowToDestructureTransform::gatherUses( continue; } - auto leafRange = TypeTreeLeafTypeRange::get(nextUse->get(), mmci); + auto leafRange = + TypeTreeLeafTypeRange::get(nextUse->get(), getRootValue()); if (!leafRange) { LLVM_DEBUG(llvm::dbgs() << " Failed to compute leaf range?!\n"); return false; } LLVM_DEBUG(llvm::dbgs() << " Found lifetime ending use!\n"); - destructureNeedingUses.push_back(nextUse); - blocksToUses.insert(nextUse->getParentBlock(), - {nextUse, {*leafRange, true /*is lifetime ending*/}}); - liveness->updateForUse(nextUse->getUser(), *leafRange, - true /*is lifetime ending*/); - instToInterestingOperandIndexMap.insert(nextUse->getUser(), nextUse); + interface.destructureNeedingUses.push_back(nextUse); + interface.blocksToUses.insert( + nextUse->getParentBlock(), + {nextUse, {*leafRange, true /*is lifetime ending*/}}); + interface.liveness->updateForUse(nextUse->getUser(), *leafRange, + true /*is lifetime ending*/); + interface.instToInterestingOperandIndexMap.insert(nextUse->getUser(), + nextUse); continue; } @@ -200,6 +212,10 @@ bool BorrowToDestructureTransform::gatherUses( return true; } +//===----------------------------------------------------------------------===// +// MARK: Convert Borrow Extracts To Owned Destructures +//===----------------------------------------------------------------------===// + void BorrowToDestructureTransform::checkForErrorsOnSameInstruction() { // At this point, we have emitted all boundary checks. We also now need to // check if any of our consuming uses that are on the boundary are used by the @@ -1476,8 +1492,11 @@ bool BorrowToDestructureTransform::transform() { // Attempt to gather uses. Return false if we saw something that we did not // understand. - if (!gatherUses(borrowWorklist)) - return false; + Implementation impl{*this}; + for (auto *bbi : borrowWorklist) { + if (!impl.gatherUses(bbi)) + return false; + } // Next make sure that any destructure needing instructions are on the // boundary in a per bit field sensitive manner. From c25d58d838fa436ece80c987a7f8686c3e0397c1 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Feb 2023 12:15:09 -0800 Subject: [PATCH 19/98] [move-only] Refactor BorrowToDestructureTransform::gatherBorrows into an implementation local static function. --- .../Mandatory/MoveOnlyBorrowToDestructure.h | 7 - .../MoveOnlyBorrowToDestructureTransform.cpp | 162 +++++++++--------- 2 files changed, 84 insertions(+), 85 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h index 67f96027b9d67..1f8a4ca8f437e 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h @@ -146,13 +146,6 @@ class BorrowToDestructureTransform { return pofi; } - /// Visit all of the uses of \p mmci and find all begin_borrows. - /// - /// Returns false if we found an escape and thus cannot process. It is assumed - /// that the caller will fail in such a case. - static bool gatherBorrows(MarkMustCheckInst *mmci, - StackList &borrowWorklist); - /// Once we have gathered up all of our destructure uses and liveness /// requiring uses, validate that all of our destructure uses are on our /// boundary. Once we have done this, we know that it is safe to perform our diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp index fe889397cfe87..184ee4de11285 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp @@ -358,83 +358,6 @@ void BorrowToDestructureTransform::checkDestructureUsesOnBoundary() const { } } -bool BorrowToDestructureTransform::gatherBorrows( - MarkMustCheckInst *mmci, StackList &borrowWorklist) { - // If we have a no implicit copy mark_must_check, we do not run the borrow to - // destructure transform since: - // - // 1. If we have a move only type, we should have emitted an earlier error - // saying that move only types should not be marked as no implicit copy. - // - // 2. If we do not have a move only type, then we know that all fields that we - // access directly and would cause a need to destructure must be copyable, - // so no transformation/error is needed. - if (mmci->getType().isMoveOnlyWrapped()) { - LLVM_DEBUG(llvm::dbgs() << "Skipping move only wrapped inst: " << *mmci); - return true; - } - - LLVM_DEBUG(llvm::dbgs() << "Performing BorrowToDestructureTramsform!\n" - "Searching for borrows for inst: " - << *mmci); - - StackList worklist(mmci->getFunction()); - for (auto *op : mmci->getUses()) - worklist.push_back(op); - - while (!worklist.empty()) { - auto *use = worklist.pop_back_val(); - switch (use->getOperandOwnership()) { - case OperandOwnership::NonUse: - case OperandOwnership::TrivialUse: - continue; - - // Conservatively treat a conversion to an unowned value as a pointer - // escape. Is it legal to canonicalize ForwardingUnowned? - case OperandOwnership::ForwardingUnowned: - case OperandOwnership::PointerEscape: - return false; - - case OperandOwnership::InstantaneousUse: - case OperandOwnership::UnownedInstantaneousUse: - case OperandOwnership::BitwiseEscape: - // We don't care about these types of uses. - continue; - - case OperandOwnership::ForwardingConsume: - // Skip if our type is not move only. - if (!use->get()->getType().isMoveOnly()) - continue; - - // Search through forwarding consumes. - ForwardingOperand(use).visitForwardedValues([&](SILValue value) -> bool { - for (auto *use : value->getUses()) - worklist.push_back(use); - return true; - }); - continue; - case OperandOwnership::DestroyingConsume: - // We don't care about destroying consume. - continue; - case OperandOwnership::Borrow: - if (auto *bbi = dyn_cast(use->getUser())) { - LLVM_DEBUG(llvm::dbgs() << " Found borrow: " << *bbi); - borrowWorklist.push_back(bbi); - } - continue; - case OperandOwnership::InteriorPointer: - // We don't care about these. - continue; - case OperandOwnership::GuaranteedForwarding: - case OperandOwnership::EndBorrow: - case OperandOwnership::Reborrow: - llvm_unreachable("Visiting an owned value!\n"); - } - } - - return true; -} - static StructDecl *getFullyReferenceableStruct(SILType ktypeTy) { auto structDecl = ktypeTy.getStructOrBoundGenericStruct(); if (!structDecl || structDecl->hasUnreferenceableStorage()) @@ -1473,6 +1396,89 @@ void BorrowToDestructureTransform::cleanup( addCompensatingDestroys(liveness, boundary, initialValue); } +//===----------------------------------------------------------------------===// +// MARK: Borrow and SwitchEnum Gathering +//===----------------------------------------------------------------------===// + +/// Visit all of the uses of \p mmci and find all begin_borrows. +/// +/// Returns false if we found an escape and thus cannot process. It is assumed +/// that the caller will fail in such a case. +static bool gatherBorrows(MarkMustCheckInst *mmci, + StackList &borrowWorklist) { + // If we have a no implicit copy mark_must_check, we do not run the borrow to + // destructure transform since: + // + // 1. If we have a move only type, we should have emitted an earlier error + // saying that move only types should not be marked as no implicit copy. + // + // 2. If we do not have a move only type, then we know that all fields that we + // access directly and would cause a need to destructure must be copyable, + // so no transformation/error is needed. + if (mmci->getType().isMoveOnlyWrapped()) { + LLVM_DEBUG(llvm::dbgs() << "Skipping move only wrapped inst: " << *mmci); + return true; + } + + LLVM_DEBUG(llvm::dbgs() << "Searching for borrows for inst: " << *mmci); + + StackList worklist(mmci->getFunction()); + for (auto *op : mmci->getUses()) + worklist.push_back(op); + + while (!worklist.empty()) { + auto *use = worklist.pop_back_val(); + switch (use->getOperandOwnership()) { + case OperandOwnership::NonUse: + case OperandOwnership::TrivialUse: + continue; + + // Conservatively treat a conversion to an unowned value as a pointer + // escape. Is it legal to canonicalize ForwardingUnowned? + case OperandOwnership::ForwardingUnowned: + case OperandOwnership::PointerEscape: + return false; + + case OperandOwnership::InstantaneousUse: + case OperandOwnership::UnownedInstantaneousUse: + case OperandOwnership::BitwiseEscape: + // We don't care about these types of uses. + continue; + + case OperandOwnership::ForwardingConsume: + // Skip if our type is not move only. + if (!use->get()->getType().isMoveOnly()) + continue; + + // Search through forwarding consumes. + ForwardingOperand(use).visitForwardedValues([&](SILValue value) -> bool { + for (auto *use : value->getUses()) + worklist.push_back(use); + return true; + }); + continue; + case OperandOwnership::DestroyingConsume: + // We don't care about destroying consume. + continue; + case OperandOwnership::Borrow: + if (auto *bbi = dyn_cast(use->getUser())) { + LLVM_DEBUG(llvm::dbgs() << " Found borrow: " << *bbi); + borrowWorklist.push_back(bbi); + } + continue; + case OperandOwnership::InteriorPointer: + // We don't care about these. + continue; + case OperandOwnership::GuaranteedForwarding: + case OperandOwnership::EndBorrow: + case OperandOwnership::Reborrow: + llvm_unreachable("Visiting an owned value!\n"); + } + } + + return true; +} + //===----------------------------------------------------------------------===// // MARK: Top Level Entrypoint //===----------------------------------------------------------------------===// @@ -1482,7 +1488,7 @@ bool BorrowToDestructureTransform::transform() { // If we failed to gather borrows due to the transform not understanding part // of the SIL, fail and return false. - if (!BorrowToDestructureTransform::gatherBorrows(mmci, borrowWorklist)) + if (!gatherBorrows(mmci, borrowWorklist)) return false; // If we do not have any borrows to process, return true early to show we From 5d1fe77a0fa68b353f6dfd6a59e58cf8d8457022 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Feb 2023 12:25:25 -0800 Subject: [PATCH 20/98] [move-only] Move the rest of the actual computation from the borrowToDestructureTransform driver to the local Implementation. --- .../Mandatory/MoveOnlyBorrowToDestructure.h | 16 -- .../MoveOnlyBorrowToDestructureTransform.cpp | 141 +++++++++++------- 2 files changed, 89 insertions(+), 68 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h index 1f8a4ca8f437e..aa59a26ff3b1f 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h @@ -146,25 +146,9 @@ class BorrowToDestructureTransform { return pofi; } - /// Once we have gathered up all of our destructure uses and liveness - /// requiring uses, validate that all of our destructure uses are on our - /// boundary. Once we have done this, we know that it is safe to perform our - /// transform. - void checkDestructureUsesOnBoundary() const; - - /// Check for cases where we have two consuming uses on the same instruction - /// or a consuming/non-consuming use on the same instruction. - void checkForErrorsOnSameInstruction(); - - /// Rewrite all of the uses of our borrow on our borrow operand, performing - /// destructures as appropriate. - void rewriteUses(); - /// After we have rewritten uses, cleanup the IR by deleting the original /// borrow/struct_extract/copies and inserting compensating destroy_values. void cleanup(StackList &borrowWorklist); - - AvailableValues &computeAvailableValues(SILBasicBlock *block); }; } // namespace siloptimizer diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp index 184ee4de11285..2abd4c52feadb 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp @@ -87,9 +87,41 @@ struct borrowtodestructure::Implementation { bool gatherUses(SILValue value); + /// Once we have gathered up all of our destructure uses and liveness + /// requiring uses, validate that all of our destructure uses are on our + /// boundary. Once we have done this, we know that it is safe to perform our + /// transform. + void checkDestructureUsesOnBoundary() const; + + /// Check for cases where we have two consuming uses on the same instruction + /// or a consuming/non-consuming use on the same instruction. + void checkForErrorsOnSameInstruction(); + + /// Rewrite all of the uses of our borrow on our borrow operand, performing + /// destructures as appropriate. + void rewriteUses(); + + AvailableValues &computeAvailableValues(SILBasicBlock *block); + /// Returns mark_must_check if we are processing borrows or the enum argument /// if we are processing switch_enum. SILValue getRootValue() const { return interface.mmci; } + + DiagnosticEmitter &getDiagnostics() const { + return interface.diagnosticEmitter; + } + + /// Always returns the actual root mark_must_check for both switch_enum args + /// and normal borrow user checks. + MarkMustCheckInst *getMarkedValue() const { return interface.mmci; } + + PostOrderFunctionInfo *getPostOrderFunctionInfo() { + return interface.getPostOrderFunctionInfo(); + } + + IntervalMapAllocator::Allocator &getAllocator() { + return interface.allocator.get(); + } }; bool Implementation::gatherUses(SILValue value) { @@ -212,18 +244,15 @@ bool Implementation::gatherUses(SILValue value) { return true; } -//===----------------------------------------------------------------------===// -// MARK: Convert Borrow Extracts To Owned Destructures -//===----------------------------------------------------------------------===// - -void BorrowToDestructureTransform::checkForErrorsOnSameInstruction() { +void Implementation::checkForErrorsOnSameInstruction() { // At this point, we have emitted all boundary checks. We also now need to // check if any of our consuming uses that are on the boundary are used by the // same instruction as a different consuming or non-consuming use. - instToInterestingOperandIndexMap.setFrozen(); - SmallBitVector usedBits(liveness->getNumSubElements()); + interface.instToInterestingOperandIndexMap.setFrozen(); + SmallBitVector usedBits(interface.liveness->getNumSubElements()); - for (auto instRangePair : instToInterestingOperandIndexMap.getRange()) { + for (auto instRangePair : + interface.instToInterestingOperandIndexMap.getRange()) { SWIFT_DEFER { usedBits.reset(); }; // First loop through our uses and handle any consuming twice errors. We @@ -234,7 +263,8 @@ void BorrowToDestructureTransform::checkForErrorsOnSameInstruction() { if (!use->isConsuming()) continue; - auto destructureUseSpan = *TypeTreeLeafTypeRange::get(use->get(), mmci); + auto destructureUseSpan = + *TypeTreeLeafTypeRange::get(use->get(), getRootValue()); for (unsigned index : destructureUseSpan.getRange()) { if (usedBits[index]) { // If we get that we used the same bit twice, we have an error. We set @@ -261,7 +291,8 @@ void BorrowToDestructureTransform::checkForErrorsOnSameInstruction() { if (use->isConsuming()) continue; - auto destructureUseSpan = *TypeTreeLeafTypeRange::get(use->get(), mmci); + auto destructureUseSpan = + *TypeTreeLeafTypeRange::get(use->get(), getRootValue()); for (unsigned index : destructureUseSpan.getRange()) { if (!usedBits[index]) continue; @@ -301,18 +332,19 @@ void BorrowToDestructureTransform::checkForErrorsOnSameInstruction() { if (!use->isConsuming()) continue; - auto destructureUseSpan = *TypeTreeLeafTypeRange::get(use->get(), mmci); + auto destructureUseSpan = + *TypeTreeLeafTypeRange::get(use->get(), getRootValue()); bool emittedError = false; for (unsigned index : destructureUseSpan.getRange()) { if (!usedBits[index]) continue; if (badOperand->isConsuming()) - diagnosticEmitter.emitObjectInstConsumesValueTwice(mmci, use, - badOperand); + getDiagnostics().emitObjectInstConsumesValueTwice(getMarkedValue(), + use, badOperand); else - diagnosticEmitter.emitObjectInstConsumesAndUsesValue(mmci, use, - badOperand); + getDiagnostics().emitObjectInstConsumesAndUsesValue(getMarkedValue(), + use, badOperand); emittedError = true; } @@ -323,19 +355,21 @@ void BorrowToDestructureTransform::checkForErrorsOnSameInstruction() { } } -void BorrowToDestructureTransform::checkDestructureUsesOnBoundary() const { +void Implementation::checkDestructureUsesOnBoundary() const { LLVM_DEBUG(llvm::dbgs() << "Checking destructure uses on boundary!\n"); // Now that we have found all of our destructure needing uses and liveness // needing uses, make sure that none of our destructure needing uses are // within our boundary. If so, we have an automatic error since we have a // use-after-free. - for (auto *use : destructureNeedingUses) { + for (auto *use : interface.destructureNeedingUses) { LLVM_DEBUG(llvm::dbgs() << " DestructureNeedingUse: " << *use->getUser()); - auto destructureUseSpan = *TypeTreeLeafTypeRange::get(use->get(), mmci); - if (!liveness->isWithinBoundary(use->getUser(), destructureUseSpan)) { + auto destructureUseSpan = + *TypeTreeLeafTypeRange::get(use->get(), getRootValue()); + if (!interface.liveness->isWithinBoundary(use->getUser(), + destructureUseSpan)) { LLVM_DEBUG(llvm::dbgs() << " On boundary or within boundary! No error!\n"); continue; @@ -351,10 +385,10 @@ void BorrowToDestructureTransform::checkDestructureUsesOnBoundary() const { // uses. LLVM_DEBUG(llvm::dbgs() << " Within boundary! Emitting error!\n"); FieldSensitivePrunedLivenessBoundary boundary( - liveness->getNumSubElements()); - liveness->computeBoundary(boundary); - diagnosticEmitter.emitObjectDestructureNeededWithinBorrowBoundary( - mmci, use->getUser(), destructureUseSpan, boundary); + interface.liveness->getNumSubElements()); + interface.liveness->computeBoundary(boundary); + getDiagnostics().emitObjectDestructureNeededWithinBorrowBoundary( + getMarkedValue(), use->getUser(), destructureUseSpan, boundary); } } @@ -613,15 +647,14 @@ static void dumpSmallestTypeAvailable( /// ensures that we match at the source level the assumption by users that they /// can use entire valid parts as late as possible. If we were to do it earlier /// we would emit errors too early. -AvailableValues & -BorrowToDestructureTransform::computeAvailableValues(SILBasicBlock *block) { +AvailableValues &Implementation::computeAvailableValues(SILBasicBlock *block) { LLVM_DEBUG(llvm::dbgs() << " Computing Available Values For bb" << block->getDebugID() << '\n'); // First grab our block. If we already have state for the block, just return // its available values. We already computed the available values and // potentially updated it with new destructured values for our block. - auto pair = blockToAvailableValues->get(block); + auto pair = interface.blockToAvailableValues->get(block); if (!pair.second) { LLVM_DEBUG(llvm::dbgs() << " Already have values! Returning them!\n"); @@ -641,11 +674,11 @@ BorrowToDestructureTransform::computeAvailableValues(SILBasicBlock *block) { // ensure that from an OSSA perspective any any destructures we insert are // independent of any other copies. We assume that OSSA canonicalization will // remove the extra copy later after we run or emit an error if it can't. - if (block == mmci->getParent()) { + if (block == getRootValue()->getParentBlock()) { LLVM_DEBUG(llvm::dbgs() << " In initial block, setting to initial value!\n"); for (unsigned i : indices(newValues)) - newValues[i] = initialValue; + newValues[i] = interface.initialValue; LLVM_DEBUG(newValues.print(llvm::dbgs(), " ")); return newValues; } @@ -745,7 +778,7 @@ BorrowToDestructureTransform::computeAvailableValues(SILBasicBlock *block) { for (unsigned i : range(predAvailableValues.size())) { if (predAvailableValues[i]) smallestTypeAvailable.push_back( - {{TypeOffsetSizePair(predAvailableValues[i], mmci), + {{TypeOffsetSizePair(predAvailableValues[i], getRootValue()), predAvailableValues[i]->getType()}}); else smallestTypeAvailable.emplace_back(None); @@ -779,7 +812,8 @@ BorrowToDestructureTransform::computeAvailableValues(SILBasicBlock *block) { // the NOTE above), we know that if subElt has a smaller size than our // accumulator, then it must be further down the type tree from our // accumulator. - auto offsetSize = TypeOffsetSizePair(predAvailableValues[i], mmci); + auto offsetSize = + TypeOffsetSizePair(predAvailableValues[i], getRootValue()); if (smallestTypeAvailable[i]->first.size > offsetSize.size) smallestTypeAvailable[i] = {offsetSize, predAvailableValues[i]->getType()}; @@ -800,7 +834,7 @@ BorrowToDestructureTransform::computeAvailableValues(SILBasicBlock *block) { << " Destructuring available values in preds to smallest size for bb" << block->getDebugID() << '\n'); auto *fn = block->getFunction(); - IntervalMapAllocator::Map typeSpanToValue(allocator.get()); + IntervalMapAllocator::Map typeSpanToValue(getAllocator()); for (auto *predBlock : predsSkippingBackEdges) { SWIFT_DEFER { typeSpanToValue.clear(); }; @@ -843,7 +877,7 @@ BorrowToDestructureTransform::computeAvailableValues(SILBasicBlock *block) { auto iter = typeSpanToValue.find(i); assert(iter != typeSpanToValue.end()); auto iterValue = iter.value(); - auto iterOffsetSize = TypeOffsetSizePair(iterValue, mmci); + auto iterOffsetSize = TypeOffsetSizePair(iterValue, getRootValue()); if (smallestOffsetSize->first.size == iterOffsetSize.size) { // Our value should already be in the interval map. assert(iter.start() == iterOffsetSize.startOffset && @@ -954,7 +988,7 @@ BorrowToDestructureTransform::computeAvailableValues(SILBasicBlock *block) { SILType offsetType = smallestTypeAvailable[i]->second; auto *phi = block->createPhiArgument(offsetType, OwnershipKind::Owned); newValues[i] = phi; - createdPhiArguments.push_back(phi); + interface.createdPhiArguments.push_back(phi); } for (auto *predBlock : predsSkippingBackEdges) { @@ -1000,28 +1034,30 @@ dumpIntervalMap(IntervalMapAllocator::Map &map) { } #endif -void BorrowToDestructureTransform::rewriteUses() { - blocksToUses.setFrozen(); +void Implementation::rewriteUses() { + interface.blocksToUses.setFrozen(); LLVM_DEBUG(llvm::dbgs() << "Performing BorrowToDestructureTransform::rewriteUses()!\n"); llvm::SmallPtrSet seenOperands; - SmallBitVector bitsNeededInBlock(liveness->getNumSubElements()); - IntervalMapAllocator::Map typeSpanToValue(allocator.get()); + SmallBitVector bitsNeededInBlock(interface.liveness->getNumSubElements()); + IntervalMapAllocator::Map typeSpanToValue(getAllocator()); - auto *fn = mmci->getFunction(); - assert(!initialValue); + auto *fn = getMarkedValue()->getFunction(); + assert(!interface.initialValue); { - auto *next = mmci->getNextInstruction(); + // We are always going to copy our root value. + auto *next = getRootValue()->getNextInstruction(); SILBuilderWithScope builder(next); - initialValue = builder.createCopyValue(getSafeLoc(next), mmci); + interface.initialValue = + builder.createCopyValue(getSafeLoc(next), getRootValue()); } - assert(initialValue); + assert(interface.initialValue); // Walking each block in RPO order. - for (auto *block : - getPostOrderFunctionInfo()->getReversePostOrder(mmci->getParent())) { + for (auto *block : getPostOrderFunctionInfo()->getReversePostOrder( + getRootValue()->getParentBlock())) { SWIFT_DEFER { bitsNeededInBlock.reset(); seenOperands.clear(); @@ -1031,7 +1067,7 @@ void BorrowToDestructureTransform::rewriteUses() { << "Visiting block bb" << block->getDebugID() << '\n'); // See if we have any operands that we need to process... - if (auto operandList = blocksToUses.find(block)) { + if (auto operandList = interface.blocksToUses.find(block)) { // If we do, gather up the bits that we need. for (auto operand : *operandList) { auto &subEltSpan = operand.second.subEltSpan; @@ -1068,7 +1104,7 @@ void BorrowToDestructureTransform::rewriteUses() { if (!seenOperands.count(&operand)) continue; - auto span = *TypeTreeLeafTypeRange::get(operand.get(), mmci); + auto span = *TypeTreeLeafTypeRange::get(operand.get(), getRootValue()); // All available values in our span should have the same value // associated with it. @@ -1150,8 +1186,8 @@ void BorrowToDestructureTransform::rewriteUses() { // Compute the location in the type of first's type and operand.get()'s // type. - TypeOffsetSizePair firstValueOffsetSize(first, mmci); - TypeOffsetSizePair useOffsetSize(operand.get(), mmci); + TypeOffsetSizePair firstValueOffsetSize(first, getRootValue()); + TypeOffsetSizePair useOffsetSize(operand.get(), getRootValue()); LLVM_DEBUG(llvm::dbgs() << " FirstValueTypeOffsetSize: " << firstValueOffsetSize << '\n'); @@ -1227,7 +1263,8 @@ void BorrowToDestructureTransform::rewriteUses() { LLVM_DEBUG( llvm::dbgs() << " Consuming Operand! Extracting using destructures!\n"); - SILBuilderWithScope consumeBuilder(inst, &createdDestructures); + SILBuilderWithScope consumeBuilder(inst, + &interface.createdDestructures); auto loc = getSafeLoc(inst); auto iterOffsetSize = firstValueOffsetSize; SILValue iterValue = first; @@ -1507,7 +1544,7 @@ bool BorrowToDestructureTransform::transform() { // Next make sure that any destructure needing instructions are on the // boundary in a per bit field sensitive manner. unsigned diagnosticCount = diagnosticEmitter.getDiagnosticCount(); - checkDestructureUsesOnBoundary(); + impl.checkDestructureUsesOnBoundary(); // If we emitted any diagnostic, break out. We return true since we actually // succeeded in our processing by finding the error. We only return false if @@ -1519,7 +1556,7 @@ bool BorrowToDestructureTransform::transform() { // Then check if we had two consuming uses on the same instruction or a // consuming/non-consuming use on the same isntruction. - checkForErrorsOnSameInstruction(); + impl.checkForErrorsOnSameInstruction(); // If we emitted any diagnostic, break out. We return true since we actually // succeeded in our processing by finding the error. We only return false if @@ -1532,7 +1569,7 @@ bool BorrowToDestructureTransform::transform() { // At this point, we know that all of our destructure requiring uses are on // the boundary of our live range. Now we need to do the rewriting. blockToAvailableValues.emplace(*liveness); - rewriteUses(); + impl.rewriteUses(); // Now that we have done our rewritting, we need to do a few cleanups. cleanup(borrowWorklist); From 69a163551f6d51f1666f612a981a0ffd6736fc18 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Feb 2023 12:29:11 -0800 Subject: [PATCH 21/98] [move-only] Hide the AvailableValue impl used by BorrowToDestructureTransform in the implementation file. --- .../Mandatory/MoveOnlyBorrowToDestructure.h | 45 ----------- .../MoveOnlyBorrowToDestructureTransform.cpp | 77 ++++++++++++++++--- 2 files changed, 66 insertions(+), 56 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h index aa59a26ff3b1f..1cbed4e3180b4 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h @@ -48,48 +48,6 @@ class IntervalMapAllocator { } }; -// We reserve more bits that we need at the beginning so that we can avoid -// reallocating and potentially breaking our internal mutable array ref -// points into the data store. -struct AvailableValues { - MutableArrayRef values; - - SILValue operator[](unsigned index) const { return values[index]; } - SILValue &operator[](unsigned index) { return values[index]; } - unsigned size() const { return values.size(); } - - AvailableValues() : values() {} - AvailableValues(MutableArrayRef values) : values(values) {} - - void print(llvm::raw_ostream &os, const char *prefix = nullptr) const; - SWIFT_DEBUG_DUMP; -}; - -struct AvailableValueStore { - std::vector dataStore; - llvm::DenseMap blockToValues; - unsigned nextOffset = 0; - unsigned numBits; - - AvailableValueStore(const FieldSensitivePrunedLiveness &liveness) - : dataStore(liveness.getDiscoveredBlocks().size() * - liveness.getNumSubElements()), - numBits(liveness.getNumSubElements()) {} - - std::pair get(SILBasicBlock *block) { - auto iter = blockToValues.try_emplace(block, AvailableValues()); - - if (!iter.second) { - return {&iter.first->second, false}; - } - - iter.first->second.values = - MutableArrayRef(&dataStore[nextOffset], numBits); - nextOffset += numBits; - return {&iter.first->second, true}; - } -}; - struct Implementation; } // namespace borrowtodestructure @@ -98,8 +56,6 @@ class BorrowToDestructureTransform { friend borrowtodestructure::Implementation; using IntervalMapAllocator = borrowtodestructure::IntervalMapAllocator; - using AvailableValueStore = borrowtodestructure::AvailableValueStore; - using AvailableValues = borrowtodestructure::AvailableValues; IntervalMapAllocator &allocator; MarkMustCheckInst *mmci; @@ -109,7 +65,6 @@ class BorrowToDestructureTransform { SmallVector destructureNeedingUses; PostOrderAnalysis *poa; PostOrderFunctionInfo *pofi = nullptr; - Optional blockToAvailableValues; SILValue initialValue; SmallVector createdDestructures; SmallVector createdPhiArguments; diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp index 2abd4c52feadb..3d98d0e344415 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp @@ -52,6 +52,64 @@ using namespace swift::siloptimizer::borrowtodestructure; // MARK: Utilities //===----------------------------------------------------------------------===// +/// Return a loc that can be used regardless if \p inst is a terminator or not. +static SILLocation getSafeLoc(SILInstruction *inst) { + if (isa(inst)) + return RegularLocation::getDiagnosticsOnlyLocation(inst->getLoc(), + inst->getModule()); + return inst->getLoc(); +} + +//===----------------------------------------------------------------------===// +// MARK: Available Values +//===----------------------------------------------------------------------===// + +namespace { + +// We reserve more bits that we need at the beginning so that we can avoid +// reallocating and potentially breaking our internal mutable array ref +// points into the data store. +struct AvailableValues { + MutableArrayRef values; + + SILValue operator[](unsigned index) const { return values[index]; } + SILValue &operator[](unsigned index) { return values[index]; } + unsigned size() const { return values.size(); } + + AvailableValues() : values() {} + AvailableValues(MutableArrayRef values) : values(values) {} + + void print(llvm::raw_ostream &os, const char *prefix = nullptr) const; + SWIFT_DEBUG_DUMP; +}; + +struct AvailableValueStore { + std::vector dataStore; + llvm::DenseMap blockToValues; + unsigned nextOffset = 0; + unsigned numBits; + + AvailableValueStore(const FieldSensitivePrunedLiveness &liveness) + : dataStore(liveness.getDiscoveredBlocks().size() * + liveness.getNumSubElements()), + numBits(liveness.getNumSubElements()) {} + + std::pair get(SILBasicBlock *block) { + auto iter = blockToValues.try_emplace(block, AvailableValues()); + + if (!iter.second) { + return {&iter.first->second, false}; + } + + iter.first->second.values = + MutableArrayRef(&dataStore[nextOffset], numBits); + nextOffset += numBits; + return {&iter.first->second, true}; + } +}; + +} // namespace + void AvailableValues::print(llvm::raw_ostream &os, const char *prefix) const { if (prefix) os << prefix; @@ -70,14 +128,6 @@ void AvailableValues::print(llvm::raw_ostream &os, const char *prefix) const { void AvailableValues::dump() const { print(llvm::dbgs(), nullptr); } -/// Return a loc that can be used regardless if \p inst is a terminator or not. -static SILLocation getSafeLoc(SILInstruction *inst) { - if (isa(inst)) - return RegularLocation::getDiagnosticsOnlyLocation(inst->getLoc(), - inst->getModule()); - return inst->getLoc(); -} - //===----------------------------------------------------------------------===// // MARK: Private Implementation //===----------------------------------------------------------------------===// @@ -85,6 +135,11 @@ static SILLocation getSafeLoc(SILInstruction *inst) { struct borrowtodestructure::Implementation { BorrowToDestructureTransform &interface; + Optional blockToAvailableValues; + + Implementation(BorrowToDestructureTransform &interface) + : interface(interface) {} + bool gatherUses(SILValue value); /// Once we have gathered up all of our destructure uses and liveness @@ -654,7 +709,7 @@ AvailableValues &Implementation::computeAvailableValues(SILBasicBlock *block) { // First grab our block. If we already have state for the block, just return // its available values. We already computed the available values and // potentially updated it with new destructured values for our block. - auto pair = interface.blockToAvailableValues->get(block); + auto pair = blockToAvailableValues->get(block); if (!pair.second) { LLVM_DEBUG(llvm::dbgs() << " Already have values! Returning them!\n"); @@ -1535,7 +1590,7 @@ bool BorrowToDestructureTransform::transform() { // Attempt to gather uses. Return false if we saw something that we did not // understand. - Implementation impl{*this}; + Implementation impl(*this); for (auto *bbi : borrowWorklist) { if (!impl.gatherUses(bbi)) return false; @@ -1568,7 +1623,7 @@ bool BorrowToDestructureTransform::transform() { // At this point, we know that all of our destructure requiring uses are on // the boundary of our live range. Now we need to do the rewriting. - blockToAvailableValues.emplace(*liveness); + impl.blockToAvailableValues.emplace(*liveness); impl.rewriteUses(); // Now that we have done our rewritting, we need to do a few cleanups. From b244df07026b46b4effbb6b504a292a29dd613d7 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Feb 2023 12:33:52 -0800 Subject: [PATCH 22/98] [move-only][borrow2destructure] Sink liveness into the implementation and allow for it to be initialized separately from construction. --- .../Mandatory/MoveOnlyBorrowToDestructure.h | 10 +---- .../MoveOnlyBorrowToDestructureTransform.cpp | 40 ++++++++++++------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h index 1cbed4e3180b4..0fb9298d86f5f 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h @@ -60,8 +60,6 @@ class BorrowToDestructureTransform { IntervalMapAllocator &allocator; MarkMustCheckInst *mmci; DiagnosticEmitter &diagnosticEmitter; - // Temporarily optional as this code is refactored. - Optional liveness; SmallVector destructureNeedingUses; PostOrderAnalysis *poa; PostOrderFunctionInfo *pofi = nullptr; @@ -78,19 +76,13 @@ class BorrowToDestructureTransform { SmallFrozenMultiMap instToInterestingOperandIndexMap; - SmallVector discoveredBlocks; - public: BorrowToDestructureTransform(IntervalMapAllocator &allocator, MarkMustCheckInst *mmci, DiagnosticEmitter &diagnosticEmitter, PostOrderAnalysis *poa) : allocator(allocator), mmci(mmci), diagnosticEmitter(diagnosticEmitter), - liveness(), poa(poa) { - liveness.emplace(mmci->getFunction(), &discoveredBlocks); - liveness->init(mmci); - liveness->initializeDef(mmci, TypeTreeLeafTypeRange(mmci)); - } + poa(poa) {} bool transform(); diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp index 3d98d0e344415..f269b1b8f7a1c 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp @@ -137,8 +137,18 @@ struct borrowtodestructure::Implementation { Optional blockToAvailableValues; - Implementation(BorrowToDestructureTransform &interface) - : interface(interface) {} + // Temporarily optional as this code is refactored. + FieldSensitiveSSAPrunedLiveRange liveness; + + Implementation(BorrowToDestructureTransform &interface, + SmallVectorImpl &discoveredBlocks) + : interface(interface), + liveness(interface.mmci->getFunction(), &discoveredBlocks) {} + + void init(SILValue rootAddress) { + liveness.init(rootAddress); + liveness.initializeDef(rootAddress, TypeTreeLeafTypeRange(rootAddress)); + } bool gatherUses(SILValue value); @@ -236,8 +246,8 @@ bool Implementation::gatherUses(SILValue value) { interface.blocksToUses.insert( nextUse->getParentBlock(), {nextUse, {*leafRange, false /*is lifetime ending*/}}); - interface.liveness->updateForUse(nextUse->getUser(), *leafRange, - false /*is lifetime ending*/); + liveness.updateForUse(nextUse->getUser(), *leafRange, + false /*is lifetime ending*/); interface.instToInterestingOperandIndexMap.insert(nextUse->getUser(), nextUse); continue; @@ -263,8 +273,8 @@ bool Implementation::gatherUses(SILValue value) { interface.blocksToUses.insert( nextUse->getParentBlock(), {nextUse, {*leafRange, true /*is lifetime ending*/}}); - interface.liveness->updateForUse(nextUse->getUser(), *leafRange, - true /*is lifetime ending*/); + liveness.updateForUse(nextUse->getUser(), *leafRange, + true /*is lifetime ending*/); interface.instToInterestingOperandIndexMap.insert(nextUse->getUser(), nextUse); continue; @@ -304,7 +314,7 @@ void Implementation::checkForErrorsOnSameInstruction() { // check if any of our consuming uses that are on the boundary are used by the // same instruction as a different consuming or non-consuming use. interface.instToInterestingOperandIndexMap.setFrozen(); - SmallBitVector usedBits(interface.liveness->getNumSubElements()); + SmallBitVector usedBits(liveness.getNumSubElements()); for (auto instRangePair : interface.instToInterestingOperandIndexMap.getRange()) { @@ -423,8 +433,7 @@ void Implementation::checkDestructureUsesOnBoundary() const { auto destructureUseSpan = *TypeTreeLeafTypeRange::get(use->get(), getRootValue()); - if (!interface.liveness->isWithinBoundary(use->getUser(), - destructureUseSpan)) { + if (!liveness.isWithinBoundary(use->getUser(), destructureUseSpan)) { LLVM_DEBUG(llvm::dbgs() << " On boundary or within boundary! No error!\n"); continue; @@ -439,9 +448,8 @@ void Implementation::checkDestructureUsesOnBoundary() const { // TODO: Fix diagnostic to use destructure needing use and boundary // uses. LLVM_DEBUG(llvm::dbgs() << " Within boundary! Emitting error!\n"); - FieldSensitivePrunedLivenessBoundary boundary( - interface.liveness->getNumSubElements()); - interface.liveness->computeBoundary(boundary); + FieldSensitivePrunedLivenessBoundary boundary(liveness.getNumSubElements()); + liveness.computeBoundary(boundary); getDiagnostics().emitObjectDestructureNeededWithinBorrowBoundary( getMarkedValue(), use->getUser(), destructureUseSpan, boundary); } @@ -1096,7 +1104,7 @@ void Implementation::rewriteUses() { << "Performing BorrowToDestructureTransform::rewriteUses()!\n"); llvm::SmallPtrSet seenOperands; - SmallBitVector bitsNeededInBlock(interface.liveness->getNumSubElements()); + SmallBitVector bitsNeededInBlock(liveness.getNumSubElements()); IntervalMapAllocator::Map typeSpanToValue(getAllocator()); auto *fn = getMarkedValue()->getFunction(); @@ -1590,7 +1598,9 @@ bool BorrowToDestructureTransform::transform() { // Attempt to gather uses. Return false if we saw something that we did not // understand. - Implementation impl(*this); + SmallVector discoveredBlocks; + Implementation impl(*this, discoveredBlocks); + impl.init(mmci); for (auto *bbi : borrowWorklist) { if (!impl.gatherUses(bbi)) return false; @@ -1623,7 +1633,7 @@ bool BorrowToDestructureTransform::transform() { // At this point, we know that all of our destructure requiring uses are on // the boundary of our live range. Now we need to do the rewriting. - impl.blockToAvailableValues.emplace(*liveness); + impl.blockToAvailableValues.emplace(impl.liveness); impl.rewriteUses(); // Now that we have done our rewritting, we need to do a few cleanups. From 83918054f8fdf9b08e2d6ccd88a11f8e923bced0 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Feb 2023 12:41:13 -0800 Subject: [PATCH 23/98] [move-only][borrow2destructure] Split cleanup code into the borrow specific cleanup and inserting compensating destroys. --- .../Mandatory/MoveOnlyBorrowToDestructure.h | 5 - .../MoveOnlyBorrowToDestructureTransform.cpp | 153 ++++++++++-------- 2 files changed, 83 insertions(+), 75 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h index 0fb9298d86f5f..b2bb8cb1db0b2 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h @@ -63,7 +63,6 @@ class BorrowToDestructureTransform { SmallVector destructureNeedingUses; PostOrderAnalysis *poa; PostOrderFunctionInfo *pofi = nullptr; - SILValue initialValue; SmallVector createdDestructures; SmallVector createdPhiArguments; @@ -92,10 +91,6 @@ class BorrowToDestructureTransform { pofi = poa->get(mmci->getFunction()); return pofi; } - - /// After we have rewritten uses, cleanup the IR by deleting the original - /// borrow/struct_extract/copies and inserting compensating destroy_values. - void cleanup(StackList &borrowWorklist); }; } // namespace siloptimizer diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp index f269b1b8f7a1c..028fd49f7661b 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp @@ -60,6 +60,53 @@ static SILLocation getSafeLoc(SILInstruction *inst) { return inst->getLoc(); } +static void addCompensatingDestroys(SSAPrunedLiveness &liveness, + PrunedLivenessBoundary &boundary, + SILValue value) { + InstructionSet consumingInsts(value->getFunction()); + liveness.initializeDef(value); + for (auto *use : value->getUses()) { + if (use->isConsuming()) + consumingInsts.insert(use->getUser()); + liveness.updateForUse(use->getUser(), use->isConsuming()); + if (auto *bbi = dyn_cast(use->getUser())) { + for (auto *ebi : bbi->getEndBorrows()) { + liveness.updateForUse(ebi, false /*use is consuming*/); + } + } + } + liveness.computeBoundary(boundary); + for (auto *user : boundary.lastUsers) { + // If this is a consuming inst, just continue. + if (consumingInsts.contains(user)) + continue; + // Otherwise, we need to insert a destroy_value afterwards. + auto *next = user->getNextInstruction(); + SILBuilderWithScope builder(next); + builder.createDestroyValue(getSafeLoc(next), value); + } + + // Insert destroy_value along all boundary edges. + for (auto *edge : boundary.boundaryEdges) { + SILBuilderWithScope builder(edge->begin()); + builder.createDestroyValue(getSafeLoc(&*edge->begin()), value); + } + + // If we have a dead def, insert the destroy_value immediately at the def. + for (auto *deadDef : boundary.deadDefs) { + SILInstruction *nextInst = nullptr; + if (auto *inst = dyn_cast(deadDef)) { + nextInst = inst->getNextInstruction(); + } else if (auto *arg = dyn_cast(deadDef)) { + nextInst = arg->getNextInstruction(); + } else { + llvm_unreachable("Unhandled dead def?!"); + } + SILBuilderWithScope builder(nextInst); + builder.createDestroyValue(getSafeLoc(nextInst), value); + } +} + //===----------------------------------------------------------------------===// // MARK: Available Values //===----------------------------------------------------------------------===// @@ -137,15 +184,26 @@ struct borrowtodestructure::Implementation { Optional blockToAvailableValues; - // Temporarily optional as this code is refactored. + /// The liveness that we use for all borrows or for individual switch_enum + /// arguments. FieldSensitiveSSAPrunedLiveRange liveness; + /// The copy_value we insert upon our mark_must_check or switch_enum argument + /// so that we have an independent owned value. + SILValue initialValue; + Implementation(BorrowToDestructureTransform &interface, SmallVectorImpl &discoveredBlocks) : interface(interface), liveness(interface.mmci->getFunction(), &discoveredBlocks) {} + void clear() { + liveness.clear(); + initialValue = SILValue(); + } + void init(SILValue rootAddress) { + clear(); liveness.init(rootAddress); liveness.initializeDef(rootAddress, TypeTreeLeafTypeRange(rootAddress)); } @@ -166,6 +224,8 @@ struct borrowtodestructure::Implementation { /// destructures as appropriate. void rewriteUses(); + void cleanup(); + AvailableValues &computeAvailableValues(SILBasicBlock *block); /// Returns mark_must_check if we are processing borrows or the enum argument @@ -741,7 +801,7 @@ AvailableValues &Implementation::computeAvailableValues(SILBasicBlock *block) { LLVM_DEBUG(llvm::dbgs() << " In initial block, setting to initial value!\n"); for (unsigned i : indices(newValues)) - newValues[i] = interface.initialValue; + newValues[i] = initialValue; LLVM_DEBUG(newValues.print(llvm::dbgs(), " ")); return newValues; } @@ -1108,15 +1168,14 @@ void Implementation::rewriteUses() { IntervalMapAllocator::Map typeSpanToValue(getAllocator()); auto *fn = getMarkedValue()->getFunction(); - assert(!interface.initialValue); + assert(!initialValue); { // We are always going to copy our root value. auto *next = getRootValue()->getNextInstruction(); SILBuilderWithScope builder(next); - interface.initialValue = - builder.createCopyValue(getSafeLoc(next), getRootValue()); + initialValue = builder.createCopyValue(getSafeLoc(next), getRootValue()); } - assert(interface.initialValue); + assert(initialValue); // Walking each block in RPO order. for (auto *block : getPostOrderFunctionInfo()->getReversePostOrder( @@ -1397,71 +1456,15 @@ void Implementation::rewriteUses() { } } -static void addCompensatingDestroys(SSAPrunedLiveness &liveness, - PrunedLivenessBoundary &boundary, - SILValue value) { - InstructionSet consumingInsts(value->getFunction()); - liveness.initializeDef(value); - for (auto *use : value->getUses()) { - if (use->isConsuming()) - consumingInsts.insert(use->getUser()); - liveness.updateForUse(use->getUser(), use->isConsuming()); - if (auto *bbi = dyn_cast(use->getUser())) { - for (auto *ebi : bbi->getEndBorrows()) { - liveness.updateForUse(ebi, false /*use is consuming*/); - } - } - } - liveness.computeBoundary(boundary); - for (auto *user : boundary.lastUsers) { - // If this is a consuming inst, just continue. - if (consumingInsts.contains(user)) - continue; - // Otherwise, we need to insert a destroy_value afterwards. - auto *next = user->getNextInstruction(); - SILBuilderWithScope builder(next); - builder.createDestroyValue(getSafeLoc(next), value); - } - - // Insert destroy_value along all boundary edges. - for (auto *edge : boundary.boundaryEdges) { - SILBuilderWithScope builder(edge->begin()); - builder.createDestroyValue(getSafeLoc(&*edge->begin()), value); - } - - // If we have a dead def, insert the destroy_value immediately at the def. - for (auto *deadDef : boundary.deadDefs) { - SILInstruction *nextInst = nullptr; - if (auto *inst = dyn_cast(deadDef)) { - nextInst = inst->getNextInstruction(); - } else if (auto *arg = dyn_cast(deadDef)) { - nextInst = arg->getNextInstruction(); - } else { - llvm_unreachable("Unhandled dead def?!"); - } - SILBuilderWithScope builder(nextInst); - builder.createDestroyValue(getSafeLoc(nextInst), value); - } -} - -void BorrowToDestructureTransform::cleanup( - StackList &borrowWorklist) { - // First clean up all of our borrows/copies/struct_extracts which no longer - // have any uses... - InstructionDeleter deleter; - while (!borrowWorklist.empty()) { - deleter.recursivelyForceDeleteUsersAndFixLifetimes( - borrowWorklist.pop_back_val()); - } - +void Implementation::cleanup() { // Then add destroys for any destructure elements that we inserted that we did // not actually completely consume. - auto *fn = mmci->getFunction(); + auto *fn = getMarkedValue()->getFunction(); SmallVector discoveredBlocks; SSAPrunedLiveness liveness(&discoveredBlocks); PrunedLivenessBoundary boundary; - while (!createdDestructures.empty()) { - auto *inst = createdDestructures.pop_back_val(); + while (!interface.createdDestructures.empty()) { + auto *inst = interface.createdDestructures.pop_back_val(); assert(isa(inst) || isa(inst)); for (auto result : inst->getResults()) { if (result->getType().isTrivial(*fn)) @@ -1476,8 +1479,8 @@ void BorrowToDestructureTransform::cleanup( } // Then do this for our inserted phis. - while (!createdPhiArguments.empty()) { - auto *arg = createdPhiArguments.pop_back_val(); + while (!interface.createdPhiArguments.empty()) { + auto *arg = interface.createdPhiArguments.pop_back_val(); // If we have a trivial argument, we do not ened to add any compensating // destroys. @@ -1636,8 +1639,18 @@ bool BorrowToDestructureTransform::transform() { impl.blockToAvailableValues.emplace(impl.liveness); impl.rewriteUses(); - // Now that we have done our rewritting, we need to do a few cleanups. - cleanup(borrowWorklist); + // Now that we have done our rewritting, we need to do a few cleanups starting + // by inserting compensating destroys for all of our inserted + // phis/destructures/initial value copy. + impl.cleanup(); + + // Then clean up all of our borrows/copies/struct_extracts which no longer + // have any uses... + InstructionDeleter deleter; + while (!borrowWorklist.empty()) { + deleter.recursivelyForceDeleteUsersAndFixLifetimes( + borrowWorklist.pop_back_val()); + } return true; } From e9e704eb734fd1cb0be15edc97904d6869166102 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Feb 2023 12:51:48 -0800 Subject: [PATCH 24/98] [move-only][borrow2destructure] Move destructureNeedingUses, instToInterestingOperandIdnexMap, and blocksToUses into the implementation. For now, I am creating new implementations every time... but once I get the tests passing, I am going to improve the performance by reusing the same Implementation for all computations so we can reuse memory. --- .../Mandatory/MoveOnlyBorrowToDestructure.h | 10 ------ .../MoveOnlyBorrowToDestructureTransform.cpp | 31 +++++++++++++------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h index b2bb8cb1db0b2..f3bd9012927a8 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h @@ -60,21 +60,11 @@ class BorrowToDestructureTransform { IntervalMapAllocator &allocator; MarkMustCheckInst *mmci; DiagnosticEmitter &diagnosticEmitter; - SmallVector destructureNeedingUses; PostOrderAnalysis *poa; PostOrderFunctionInfo *pofi = nullptr; SmallVector createdDestructures; SmallVector createdPhiArguments; - using InterestingUser = FieldSensitivePrunedLiveness::InterestingUser; - SmallFrozenMultiMap, 8> - blocksToUses; - - /// A frozen multi-map we use to diagnose consuming uses that are used by the - /// same instruction as another consuming use or non-consuming use. - SmallFrozenMultiMap - instToInterestingOperandIndexMap; - public: BorrowToDestructureTransform(IntervalMapAllocator &allocator, MarkMustCheckInst *mmci, diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp index 028fd49f7661b..e070739c402fd 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp @@ -192,6 +192,17 @@ struct borrowtodestructure::Implementation { /// so that we have an independent owned value. SILValue initialValue; + using InterestingUser = FieldSensitivePrunedLiveness::InterestingUser; + SmallFrozenMultiMap, 8> + blocksToUses; + + /// A frozen multi-map we use to diagnose consuming uses that are used by the + /// same instruction as another consuming use or non-consuming use. + SmallFrozenMultiMap + instToInterestingOperandIndexMap; + + SmallVector destructureNeedingUses; + Implementation(BorrowToDestructureTransform &interface, SmallVectorImpl &discoveredBlocks) : interface(interface), @@ -303,12 +314,12 @@ bool Implementation::gatherUses(SILValue value) { } LLVM_DEBUG(llvm::dbgs() << " Found non lifetime ending use!\n"); - interface.blocksToUses.insert( + blocksToUses.insert( nextUse->getParentBlock(), {nextUse, {*leafRange, false /*is lifetime ending*/}}); liveness.updateForUse(nextUse->getUser(), *leafRange, false /*is lifetime ending*/); - interface.instToInterestingOperandIndexMap.insert(nextUse->getUser(), + instToInterestingOperandIndexMap.insert(nextUse->getUser(), nextUse); continue; } @@ -329,13 +340,13 @@ bool Implementation::gatherUses(SILValue value) { } LLVM_DEBUG(llvm::dbgs() << " Found lifetime ending use!\n"); - interface.destructureNeedingUses.push_back(nextUse); - interface.blocksToUses.insert( + destructureNeedingUses.push_back(nextUse); + blocksToUses.insert( nextUse->getParentBlock(), {nextUse, {*leafRange, true /*is lifetime ending*/}}); liveness.updateForUse(nextUse->getUser(), *leafRange, true /*is lifetime ending*/); - interface.instToInterestingOperandIndexMap.insert(nextUse->getUser(), + instToInterestingOperandIndexMap.insert(nextUse->getUser(), nextUse); continue; } @@ -373,11 +384,11 @@ void Implementation::checkForErrorsOnSameInstruction() { // At this point, we have emitted all boundary checks. We also now need to // check if any of our consuming uses that are on the boundary are used by the // same instruction as a different consuming or non-consuming use. - interface.instToInterestingOperandIndexMap.setFrozen(); + instToInterestingOperandIndexMap.setFrozen(); SmallBitVector usedBits(liveness.getNumSubElements()); for (auto instRangePair : - interface.instToInterestingOperandIndexMap.getRange()) { + instToInterestingOperandIndexMap.getRange()) { SWIFT_DEFER { usedBits.reset(); }; // First loop through our uses and handle any consuming twice errors. We @@ -487,7 +498,7 @@ void Implementation::checkDestructureUsesOnBoundary() const { // needing uses, make sure that none of our destructure needing uses are // within our boundary. If so, we have an automatic error since we have a // use-after-free. - for (auto *use : interface.destructureNeedingUses) { + for (auto *use : destructureNeedingUses) { LLVM_DEBUG(llvm::dbgs() << " DestructureNeedingUse: " << *use->getUser()); @@ -1158,7 +1169,7 @@ dumpIntervalMap(IntervalMapAllocator::Map &map) { #endif void Implementation::rewriteUses() { - interface.blocksToUses.setFrozen(); + blocksToUses.setFrozen(); LLVM_DEBUG(llvm::dbgs() << "Performing BorrowToDestructureTransform::rewriteUses()!\n"); @@ -1189,7 +1200,7 @@ void Implementation::rewriteUses() { << "Visiting block bb" << block->getDebugID() << '\n'); // See if we have any operands that we need to process... - if (auto operandList = interface.blocksToUses.find(block)) { + if (auto operandList = blocksToUses.find(block)) { // If we do, gather up the bits that we need. for (auto operand : *operandList) { auto &subEltSpan = operand.second.subEltSpan; From 83dd93ad2e902972eee58a3344f69a554e98e661 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 7 Feb 2023 15:39:44 -0800 Subject: [PATCH 25/98] [CSGen] Handle recursive use of variable declarations It's possible for out-of-scope type variable to be the type of declaration if such declaration is recursively referenced in the body of a closure located in its initializer expression. In such cases type of the variable declaration cannot be connected to the closure because its not known in advance (determined by the initializer itself). Resolves: https://github.com/apple/swift/issues/63455 --- lib/Sema/CSGen.cpp | 28 +++++++++++++------ lib/Sema/CSSyntacticElement.cpp | 2 +- test/expr/closure/multi_statement.swift | 13 +++++++++ .../IDE/crashers_2_fixed/issue-63455.swift | 25 +++++++++++++++++ 4 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 validation-test/IDE/crashers_2_fixed/issue-63455.swift diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 644a54ec4e581..be04c6956e62a 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -1354,6 +1354,13 @@ namespace { Type visitDeclRefExpr(DeclRefExpr *E) { auto locator = CS.getConstraintLocator(E); + auto invalidateReference = [&]() -> Type { + auto *hole = CS.createTypeVariable(locator, TVO_CanBindToHole); + (void)CS.recordFix(AllowRefToInvalidDecl::create(CS, locator)); + CS.setType(E, hole); + return hole; + }; + Type knownType; if (auto *VD = dyn_cast(E->getDecl())) { knownType = CS.getTypeIfAvailable(VD); @@ -1361,13 +1368,19 @@ namespace { knownType = CS.getVarType(VD); if (knownType) { + // An out-of-scope type variable could be a type of a declaration + // only in diagnostic mode when invalid variable declaration is + // recursively referenced inside of a multi-statement closure + // located somewhere within its initializer e.g.: + // `let x = [] { ... print(x) }` + if (auto *typeVar = knownType->getAs()) { + if (!CS.isActiveTypeVariable(typeVar)) + return invalidateReference(); + } + // If the known type has an error, bail out. if (knownType->hasError()) { - auto *hole = CS.createTypeVariable(locator, TVO_CanBindToHole); - (void)CS.recordFix(AllowRefToInvalidDecl::create(CS, locator)); - if (!CS.hasType(E)) - CS.setType(E, hole); - return hole; + return invalidateReference(); } if (!knownType->hasPlaceholder()) { @@ -1384,10 +1397,7 @@ namespace { // (in getTypeOfReference) so we can match non-error param types. if (!knownType && E->getDecl()->isInvalid() && !CS.isForCodeCompletion()) { - auto *hole = CS.createTypeVariable(locator, TVO_CanBindToHole); - (void)CS.recordFix(AllowRefToInvalidDecl::create(CS, locator)); - CS.setType(E, hole); - return hole; + return invalidateReference(); } // Create an overload choice referencing this declaration and immediately diff --git a/lib/Sema/CSSyntacticElement.cpp b/lib/Sema/CSSyntacticElement.cpp index ff875c7a2edff..59112354d76c5 100644 --- a/lib/Sema/CSSyntacticElement.cpp +++ b/lib/Sema/CSSyntacticElement.cpp @@ -74,7 +74,7 @@ class TypeVariableRefFinder : public ASTWalker { if (auto *DRE = dyn_cast(expr)) { auto *decl = DRE->getDecl(); - if (auto type = CS.getTypeIfAvailable(DRE->getDecl())) { + if (auto type = CS.getTypeIfAvailable(decl)) { auto &ctx = CS.getASTContext(); // If this is not one of the closure parameters which // is inferrable from the body, let's replace type diff --git a/test/expr/closure/multi_statement.swift b/test/expr/closure/multi_statement.swift index ea80638084f16..f6aefc93d1eb2 100644 --- a/test/expr/closure/multi_statement.swift +++ b/test/expr/closure/multi_statement.swift @@ -650,3 +650,16 @@ func test_that_closures_are_attempted_in_order() { return false } } + +// https://github.com/apple/swift/issues/63455 +func test_recursive_var_reference_in_multistatement_closure() { + func takeClosure(_ x: () -> Void) {} + + func test(optionalInt: Int?) { + takeClosure { + let int = optionalInt { // expected-error {{cannot call value of non-function type 'Int?'}} + print(int) + } + } + } +} diff --git a/validation-test/IDE/crashers_2_fixed/issue-63455.swift b/validation-test/IDE/crashers_2_fixed/issue-63455.swift new file mode 100644 index 0000000000000..8f63179e9693b --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/issue-63455.swift @@ -0,0 +1,25 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE + +func sheet(onDismiss: () -> Void) -> EmptyView { + fatalError() +} + +@resultBuilder struct ViewBuilder2 { + static func buildBlock(_ content: EmptyView) -> EmptyView { + return content + } +} + +struct EmptyView {} + +struct SettingsView { + var importedFile: Int? + + @ViewBuilder2 var body2: EmptyView { + sheet { + #^COMPLETE^#if let url = self.importedFile { + print(url) + } + } + } +} From f64e7d3a66a71721e69878e9856f8f494cffba25 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Mon, 6 Feb 2023 15:06:49 -0800 Subject: [PATCH 26/98] [NFC] Slightly loosen IR checks in objcImpl test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Remove dependency on where the `init` selector is emitted. • Use variable names captured from previous lines instead of assuming specific names. --- test/IRGen/objc_implementation.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/IRGen/objc_implementation.swift b/test/IRGen/objc_implementation.swift index e6f068c5bb172..6ecb6a448a06b 100644 --- a/test/IRGen/objc_implementation.swift +++ b/test/IRGen/objc_implementation.swift @@ -16,13 +16,12 @@ // CHECK: @"OBJC_METACLASS_$_ImplClass" = global %objc_class { %objc_class* @"OBJC_METACLASS_$_NSObject", %objc_class* @"OBJC_METACLASS_$_NSObject", %swift.opaque* @_objc_empty_cache, %swift.opaque* null, {{i64 ptrtoint|%swift.opaque\* bitcast}} ({ i32, i32, i32, i32, i8*, i8*, i8*, i8*, i8*, i8*, i8* }* [[_METACLASS_DATA_ImplClass:@[^, ]+]] to {{i64|%swift.opaque\*}}) }, align 8 // CHECK: [[_METACLASS_DATA_ImplClass]] = internal constant { i32, i32, i32, i32, i8*, i8*, i8*, i8*, i8*, i8*, i8* } { i32 129, i32 40, i32 40, i32 0, i8* null, i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.9.ImplClass, i64 0, i64 0), i8* null, i8* null, i8* null, i8* null, i8* null }, section "__DATA, __objc_const", align 8 // TODO: Why the extra i32 field above? -// CHECK: [[selector_data_init:@[^, ]+]] = private global [5 x i8] c"init\00", section "__TEXT,__objc_methname,cstring_literals", align 1 // Class // CHECK: [[selector_data_implProperty:@[^, ]+]] = private global [13 x i8] c"implProperty\00", section "__TEXT,__objc_methname,cstring_literals", align 1 // CHECK: [[selector_data_setImplProperty_:@[^, ]+]] = private global [17 x i8] c"setImplProperty:\00", section "__TEXT,__objc_methname,cstring_literals", align 1 // CHECK: [[selector_data__cxx_destruct:@[^, ]+]] = private global [14 x i8] c".cxx_destruct\00", section "__TEXT,__objc_methname,cstring_literals", align 1 -// CHECK: [[_INSTANCE_METHODS_ImplClass:@[^, ]+]] = internal constant { i32, i32, [5 x { i8*, i8*, i8* }] } { i32 24, i32 5, [5 x { i8*, i8*, i8* }] [{ i8*, i8*, i8* } { i8* getelementptr inbounds ([5 x i8], [5 x i8]* [[selector_data_init]], i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.@16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationEABycfcTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[selector_data_implProperty]], i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.i16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE12implPropertys5Int32VvgTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[selector_data_setImplProperty_]], i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v20@0:8i16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE12implPropertys5Int32VvsTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[selector_data_mainMethod_]], i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v20@0:8i16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE10mainMethodyys5Int32VFTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[selector_data__cxx_destruct]], i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.v16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassCfETo{{(\.ptrauth)?}}" to i8*) }] }, section "__DATA, __objc_data", align 8 +// CHECK: [[_INSTANCE_METHODS_ImplClass:@[^, ]+]] = internal constant { i32, i32, [5 x { i8*, i8*, i8* }] } { i32 24, i32 5, [5 x { i8*, i8*, i8* }] [{ i8*, i8*, i8* } { i8* getelementptr inbounds ([5 x i8], [5 x i8]* @"\01L_selector_data(init)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.@16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationEABycfcTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([13 x i8], [13 x i8]* [[selector_data_implProperty]], i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.i16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE12implPropertys5Int32VvgTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[selector_data_setImplProperty_]], i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v20@0:8i16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE12implPropertys5Int32VvsTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[selector_data_mainMethod_]], i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v20@0:8i16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassC19objc_implementationE10mainMethodyys5Int32VFTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([14 x i8], [14 x i8]* [[selector_data__cxx_destruct]], i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.v16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$sSo9ImplClassCfETo{{(\.ptrauth)?}}" to i8*) }] }, section "__DATA, __objc_data", align 8 // CHECK: [[_IVARS_ImplClass:@[^, ]+]] = internal constant { i32, i32, [2 x { i64*, i8*, i8*, i32, i32 }] } { i32 32, i32 2, [2 x { i64*, i8*, i8*, i32, i32 }] [{ i64*, i8*, i8*, i32, i32 } { i64* @"$sSo9ImplClassC19objc_implementationE12implPropertys5Int32VvpWvd", i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.12.implProperty, i64 0, i64 0), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.0., i64 0, i64 0), i32 2, i32 4 }, { i64*, i8*, i8*, i32, i32 } { i64* @"$sSo9ImplClassC19objc_implementationE13implProperty2So8NSObjectCSgvpWvd", i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.13.implProperty2, i64 0, i64 0), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.0., i64 0, i64 0), i32 3, i32 8 }] }, section "__DATA, __objc_const", align 8 // CHECK: [[_PROPERTIES_ImplClass:@[^, ]+]] = internal constant { i32, i32, [1 x { i8*, i8* }] } { i32 16, i32 1, [1 x { i8*, i8* }] [{ i8*, i8* } { i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.12.implProperty, i64 0, i64 0), i8* getelementptr inbounds ([19 x i8], [19 x i8]* @".str.18.Ti,N,VimplProperty", i64 0, i64 0) }] }, section "__DATA, __objc_const", align 8 // CHECK: [[_DATA_ImplClass:@[^, ]+]] = internal constant { i32, i32, i32, i32, i8*, i8*, { i32, i32, [5 x { i8*, i8*, i8* }] }*, i8*, { i32, i32, [2 x { i64*, i8*, i8*, i32, i32 }] }*, i8*, { i32, i32, [1 x { i8*, i8* }] }* } { i32 388, i32 8, i32 24, i32 0, i8* null, i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.9.ImplClass, i64 0, i64 0), { i32, i32, [5 x { i8*, i8*, i8* }] }* [[_INSTANCE_METHODS_ImplClass]], i8* null, { i32, i32, [2 x { i64*, i8*, i8*, i32, i32 }] }* [[_IVARS_ImplClass]], i8* null, { i32, i32, [1 x { i8*, i8* }] }* [[_PROPERTIES_ImplClass]] }, section "__DATA, __objc_data", align 8 @@ -76,7 +75,7 @@ // CHECK: [[_METACLASS_DATA_SwiftSubclass]] = internal constant { i32, i32, i32, i32, i8*, i8*, i8*, i8*, i8*, i8*, i8* } { i32 129, i32 40, i32 40, i32 0, i8* null, i8* getelementptr inbounds ([41 x i8], [41 x i8]* @.str.40._TtC19objc_implementation13SwiftSubclass, i64 0, i64 0), i8* null, i8* null, i8* null, i8* null, i8* null }, section "__DATA, __objc_const", align 8 // Class -// CHECK: [[_INSTANCE_METHODS_SwiftSubclass:@[^, ]+]] = internal constant { i32, i32, [2 x { i8*, i8*, i8* }] } { i32 24, i32 2, [2 x { i8*, i8*, i8* }] [{ i8*, i8*, i8* } { i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[selector_data_mainMethod_]], i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v20@0:8i16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$s19objc_implementation13SwiftSubclassC10mainMethodyys5Int32VFTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([5 x i8], [5 x i8]* [[selector_data_init]], i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.@16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$s19objc_implementation13SwiftSubclassCACycfcTo{{(\.ptrauth)?}}" to i8*) }] }, section "__DATA, __objc_data", align 8 +// CHECK: [[_INSTANCE_METHODS_SwiftSubclass:@[^, ]+]] = internal constant { i32, i32, [2 x { i8*, i8*, i8* }] } { i32 24, i32 2, [2 x { i8*, i8*, i8* }] [{ i8*, i8*, i8* } { i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[selector_data_mainMethod_]], i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v20@0:8i16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$s19objc_implementation13SwiftSubclassC10mainMethodyys5Int32VFTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([5 x i8], [5 x i8]* @"\01L_selector_data(init)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.@16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$s19objc_implementation13SwiftSubclassCACycfcTo{{(\.ptrauth)?}}" to i8*) }] }, section "__DATA, __objc_data", align 8 // CHECK: [[_DATA_SwiftSubclass:@[^, ]+]] = internal constant { i32, i32, i32, i32, i8*, i8*, { i32, i32, [2 x { i8*, i8*, i8* }] }*, i8*, i8*, i8*, i8* } { i32 128, i32 24, i32 24, i32 0, i8* null, i8* getelementptr inbounds ([41 x i8], [41 x i8]* @.str.40._TtC19objc_implementation13SwiftSubclass, i64 0, i64 0), { i32, i32, [2 x { i8*, i8*, i8* }] }* [[_INSTANCE_METHODS_SwiftSubclass]], i8* null, i8* null, i8* null, i8* null }, section "__DATA, __objc_data", align 8 // Swift metadata @@ -188,8 +187,8 @@ public func fn(impl: ImplClass, swiftSub: SwiftSubclass) { // CHECK: [[PARAM_impl:%[^ ]+]] = bitcast %TSo9ImplClassC* %0 to %1* // CHECK: call void bitcast (void ()* @objc_msgSend to void (%1*, i8*, i32)*)(%1* [[PARAM_impl]], i8* [[SEL_1]], i32 0) // CHECK: [[SEL_2:%[^ ]+]] = load i8*, i8** @"\01L_selector(mainMethod:)", align 8 -// CHECK: [[PARAM_swiftSub:%[^ ]+]] = bitcast %T19objc_implementation13SwiftSubclassC* %1 to %2* -// CHECK: call void bitcast (void ()* @objc_msgSend to void (%2*, i8*, i32)*)(%2* [[PARAM_swiftSub]], i8* [[SEL_2]], i32 1) +// CHECK: [[PARAM_swiftSub:%[^ ]+]] = bitcast %T19objc_implementation13SwiftSubclassC* %1 to [[SEL_1]]* +// CHECK: call void bitcast (void ()* @objc_msgSend to void ([[SEL_1]]*, i8*, i32)*)([[SEL_1]]* [[PARAM_swiftSub]], i8* [[SEL_2]], i32 1) // CHECK: ret void // CHECK: } From 159c6537804aecf052c69696524c46c0dc8615ac Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 7 Feb 2023 23:22:35 -0500 Subject: [PATCH 27/98] Add the tuple_pack_element_addr SIL instruction. This allows dynamically indexing into tuples. IRGen not yet implemented. I think I'm going to need a type_refine_addr instruction in order to handle substitutions into the operand type that eliminate the outer layer of tuple-ness. Gonna handle that in a follow-up commit. --- include/swift/SIL/SILBuilder.h | 8 ++++ include/swift/SIL/SILCloner.h | 15 ++++++ include/swift/SIL/SILInstruction.h | 47 +++++++++++++++++++ include/swift/SIL/SILNodes.def | 2 + lib/IRGen/GenTuple.cpp | 15 ++++++ lib/IRGen/GenTuple.h | 11 +++++ lib/IRGen/IRGenSIL.cpp | 14 ++++++ lib/SIL/IR/OperandOwnership.cpp | 1 + lib/SIL/IR/SILInstructions.cpp | 22 +++++++++ lib/SIL/IR/SILPrinter.cpp | 5 ++ lib/SIL/IR/ValueOwnership.cpp | 1 + lib/SIL/Parser/ParseSIL.cpp | 12 +++++ lib/SIL/Utils/InstructionUtils.cpp | 3 ++ lib/SIL/Verifier/SILVerifier.cpp | 21 +++++++++ .../UtilityPasses/SerializeSILPass.cpp | 1 + lib/SILOptimizer/Utils/SILInliner.cpp | 6 +++ lib/Serialization/DeserializeSIL.cpp | 13 +++++ lib/Serialization/SILFormat.h | 6 +-- lib/Serialization/SerializeSIL.cpp | 19 ++++++++ test/SIL/Parser/variadic_generics.sil | 12 +++++ 20 files changed, 231 insertions(+), 3 deletions(-) diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 5f5d7be45ec90..1e9b993bdad35 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -1991,6 +1991,14 @@ class SILBuilder { elementValue, packIndex, pack)); } + TuplePackElementAddrInst * + createTuplePackElementAddr(SILLocation loc, SILValue packIndex, + SILValue tupleAddr, SILType elementType) { + return insert(TuplePackElementAddrInst::create(getFunction(), + getSILDebugLocation(loc), + packIndex, tupleAddr, elementType)); + } + ProjectBlockStorageInst *createProjectBlockStorage(SILLocation Loc, SILValue Storage) { auto CaptureTy = Storage->getType() diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 86d9feca83a99..c3d72d709f51b 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -2539,6 +2539,21 @@ void SILCloner::visitPackElementSetInst(PackElementSetInst *Inst) { newIndex, newPack)); } +template +void SILCloner::visitTuplePackElementAddrInst( + TuplePackElementAddrInst *Inst) { + getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); + auto loc = getOpLocation(Inst->getLoc()); + auto newIndex = getOpValue(Inst->getIndex()); + auto newTuple = getOpValue(Inst->getTuple()); + auto newElementType = getOpType(Inst->getElementType()); + // FIXME: do we need to rewrite when substitution removes the + // tuple-ness of the type? If so, what do we rewrite to? + recordClonedInstruction( + Inst, getBuilder().createTuplePackElementAddr(loc, newIndex, newTuple, + newElementType)); +} + template void SILCloner::visitCopyBlockInst(CopyBlockInst *Inst) { diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 4c2b67cb64fcc..5901a6da056ac 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -7747,6 +7747,53 @@ class PackElementSetInst } }; +/// Projects a tuple element as appropriate for the given +/// pack element index. The pack index must index into a pack with +/// the same shape as the tuple element type list. +class TuplePackElementAddrInst final + : public InstructionBaseWithTrailingOperands< + SILInstructionKind::TuplePackElementAddrInst, + TuplePackElementAddrInst, + SingleValueInstruction> { +public: + enum { + IndexOperand = 0, + TupleOperand = 1 + }; + +private: + friend SILBuilder; + + TuplePackElementAddrInst(SILDebugLocation debugLoc, + ArrayRef allOperands, + SILType elementType) + : InstructionBaseWithTrailingOperands(allOperands, debugLoc, + elementType) {} + + static TuplePackElementAddrInst *create(SILFunction &F, + SILDebugLocation debugLoc, + SILValue indexOperand, + SILValue tupleOperand, + SILType elementType); + +public: + SILValue getIndex() const { + return getAllOperands()[IndexOperand].get(); + } + + SILValue getTuple() const { + return getAllOperands()[TupleOperand].get(); + } + + CanTupleType getTupleType() const { + return getTuple()->getType().castTo(); + } + + SILType getElementType() const { + return getType(); + } +}; + /// Projects the capture storage address from a @block_storage address. class ProjectBlockStorageInst : public UnaryInstructionBase irgen::getFixedTupleElementOffset(IRGenModule &IGM, SILType tupleType, unsigned fieldNo) { diff --git a/lib/IRGen/GenTuple.h b/lib/IRGen/GenTuple.h index ee2e1a99f395e..67248ffd67f42 100644 --- a/lib/IRGen/GenTuple.h +++ b/lib/IRGen/GenTuple.h @@ -19,6 +19,10 @@ #include "swift/Basic/LLVM.h" +namespace llvm { + class Value; +} + namespace swift { class CanType; @@ -34,6 +38,13 @@ namespace irgen { SILType tupleType, unsigned fieldNo); + /// Project the address of a tuple element, given a dynamic index. + Address projectTupleElementAddressByDynamicIndex(IRGenFunction &IGF, + Address base, + SILType tupleType, + llvm::Value *index, + SILType elementType); + /// Project a tuple element rvalue from an already-exploded tuple rvalue. void projectTupleElementFromExplosion(IRGenFunction &IGF, SILType tupleType, diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index be9e763a5b63d..41b2d7477ed9f 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1281,6 +1281,7 @@ class IRGenSILFunction : void visitScalarPackIndexInst(ScalarPackIndexInst *i); void visitPackElementGetInst(PackElementGetInst *i); void visitPackElementSetInst(PackElementSetInst *i); + void visitTuplePackElementAddrInst(TuplePackElementAddrInst *i); void visitProjectBlockStorageInst(ProjectBlockStorageInst *i); void visitInitBlockStorageHeaderInst(InitBlockStorageHeaderInst *i); @@ -6920,6 +6921,19 @@ void IRGenSILFunction::visitPackElementSetInst(PackElementSetInst *i) { Builder.CreateStore(elementValue.getAddress(), elementStorageAddress); } +void IRGenSILFunction::visitTuplePackElementAddrInst( + TuplePackElementAddrInst *i) { + Address tuple = getLoweredAddress(i->getTuple()); + llvm::Value *index = getLoweredSingletonExplosion(i->getIndex()); + + auto elementType = i->getElementType(); + auto elementAddr = + projectTupleElementAddressByDynamicIndex(*this, tuple, + i->getTuple()->getType(), + index, elementType); + setLoweredAddress(i, elementAddr); +} + void IRGenSILFunction::visitProjectBlockStorageInst(ProjectBlockStorageInst *i){ // TODO Address block = getLoweredAddress(i->getOperand()); diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index 8969e8c2feea2..6cfb1698c3728 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -199,6 +199,7 @@ OPERAND_OWNERSHIP(TrivialUse, DynamicPackIndex) OPERAND_OWNERSHIP(TrivialUse, PackPackIndex) OPERAND_OWNERSHIP(TrivialUse, PackElementGet) OPERAND_OWNERSHIP(TrivialUse, PackElementSet) +OPERAND_OWNERSHIP(TrivialUse, TuplePackElementAddr) // The dealloc_stack_ref operand needs to have NonUse ownership because // this use comes after the last consuming use (which is usually a dealloc_ref). diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index ccd21dd398349..15b79781872ae 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -2400,6 +2400,28 @@ PackElementGetInst *PackElementGetInst::create(SILFunction &F, return ::new (buffer) PackElementGetInst(debugLoc, allOperands, elementType); } +TuplePackElementAddrInst * +TuplePackElementAddrInst::create(SILFunction &F, + SILDebugLocation debugLoc, + SILValue indexOperand, + SILValue tupleOperand, + SILType elementType) { + assert(indexOperand->getType().is()); + assert(tupleOperand->getType().isAddress() && + tupleOperand->getType().is()); + + SmallVector allOperands; + allOperands.push_back(indexOperand); + allOperands.push_back(tupleOperand); + collectTypeDependentOperands(allOperands, F, elementType); + + auto size = totalSizeToAlloc(allOperands.size()); + auto buffer = + F.getModule().allocateInst(size, alignof(TuplePackElementAddrInst)); + return ::new (buffer) TuplePackElementAddrInst(debugLoc, allOperands, + elementType); +} + BeginCOWMutationInst::BeginCOWMutationInst(SILDebugLocation loc, SILValue operand, ArrayRef resultTypes, diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 7da70173ea009..c83e05310ce79 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -2318,6 +2318,11 @@ class SILPrinter : public SILInstructionVisitor { << Ctx.getID(I->getIndex()) << " of " << getIDAndType(I->getPack()); } + void visitTuplePackElementAddrInst(TuplePackElementAddrInst *I) { + *this << Ctx.getID(I->getIndex()) << " of " + << getIDAndType(I->getTuple()) << " as " + << I->getElementType(); + } void visitProjectBlockStorageInst(ProjectBlockStorageInst *PBSI) { *this << getIDAndType(PBSI->getOperand()); } diff --git a/lib/SIL/IR/ValueOwnership.cpp b/lib/SIL/IR/ValueOwnership.cpp index 492568abdea8f..916dc0a440f67 100644 --- a/lib/SIL/IR/ValueOwnership.cpp +++ b/lib/SIL/IR/ValueOwnership.cpp @@ -160,6 +160,7 @@ CONSTANT_OWNERSHIP_INST(None, DynamicPackIndex) CONSTANT_OWNERSHIP_INST(None, PackPackIndex) CONSTANT_OWNERSHIP_INST(None, ScalarPackIndex) CONSTANT_OWNERSHIP_INST(None, PackElementGet) +CONSTANT_OWNERSHIP_INST(None, TuplePackElementAddr) #undef CONSTANT_OWNERSHIP_INST diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index b94367d5aee9b..fbc68ee7211cc 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -3453,6 +3453,18 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, ResultVal = B.createPackElementSet(InstLoc, value, index, pack); break; } + case SILInstructionKind::TuplePackElementAddrInst: { + SILValue index, tuple; + SILType elementType; + if (parseValueRef(index, SILType::getPackIndexType(P.Context), InstLoc, B) || + parseVerbatim("of") || + parseTypedValueRef(tuple, B) || + parseVerbatim("as") || + parseSILType(elementType)) + return true; + ResultVal = B.createTuplePackElementAddr(InstLoc, index, tuple, elementType); + break; + } #define UNARY_INSTRUCTION(ID) \ case SILInstructionKind::ID##Inst: \ diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index b3b023b2d2300..f042586b571e3 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -512,6 +512,9 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType) return RuntimeEffect::Allocating | RuntimeEffect::Releasing | RuntimeEffect::MetaData; + case SILInstructionKind::TuplePackElementAddrInst: + return RuntimeEffect::MetaData; + case SILInstructionKind::SwitchEnumAddrInst: case SILInstructionKind::InjectEnumAddrInst: case SILInstructionKind::TupleElementAddrInst: diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 5f7335ca3e1a7..0cdf402ed26a5 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -5721,6 +5721,27 @@ class SILVerifier : public SILVerifierBase { verifyPackElementType(i->getPackType(), index, i->getElementType()); } + void checkTuplePackElementAddrInst(TuplePackElementAddrInst *i) { + auto index = requireValueKind(i->getIndex(), + "pack index operand must be one of the pack_index instructions"); + if (!index) return; + + // Remove the extra tuple element type structure. + SmallVector tupleElements; { + auto tupleType = requireAddressType(TupleType, i->getTuple()->getType(), + "tuple operand of tuple_pack_element_addr"); + auto eltTypes = tupleType.getElementTypes(); + tupleElements.append(eltTypes.begin(), eltTypes.end()); + } + + require(i->getElementType().isAddress(), + "result of tuple_pack_element_addr must be an address"); + + verifyPackElementType(tupleElements, index, + i->getElementType().getASTType(), + /*types are SIL types*/ true); + } + // This verifies that the entry block of a SIL function doesn't have // any predecessors and also verifies the entry point arguments. void verifyEntryBlock(SILBasicBlock *entry) { diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index 16feac1f656cd..2d859306f6f13 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -349,6 +349,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::HasSymbolInst: case SILInstructionKind::PackElementGetInst: case SILInstructionKind::PackElementSetInst: + case SILInstructionKind::TuplePackElementAddrInst: // Handle by operand and result check. break; diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index bada1af471b1a..c968c9187c9fb 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -879,6 +879,12 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::ProjectBlockStorageInst: return InlineCost::Free; + // tuple_pack_element_addr is just a GEP, but getting the offset + // can require accessing metadata, so conservatively treat it as + // expensive. + case SILInstructionKind::TuplePackElementAddrInst: + return InlineCost::Expensive; + // dynamic_pack_index is free. The other pack-indexing instructions // are just adds of values that should be trivially dynamically // available; that's cheap enough to still consider free under the diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 548e7084152b3..3cf0b57aa3dd3 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1444,6 +1444,19 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, ResultInst = Builder.createPackElementSet(Loc, value, index, pack); break; } + case SILInstructionKind::TuplePackElementAddrInst: { + assert(RecordKind == SIL_PACK_ELEMENT_GET); + auto elementType = getSILType(MF->getType(TyID), + (SILValueCategory) TyCategory, Fn); + auto tupleType = getSILType(MF->getType(TyID2), + (SILValueCategory) TyCategory2, Fn); + auto tuple = getLocalValue(ValID2, tupleType); + auto indexType = SILType::getPackIndexType(MF->getContext()); + auto index = getLocalValue(ValID3, indexType); + ResultInst = Builder.createTuplePackElementAddr(Loc, index, tuple, + elementType); + break; + } #define ONEOPERAND_ONETYPE_INST(ID) \ case SILInstructionKind::ID##Inst: \ diff --git a/lib/Serialization/SILFormat.h b/lib/Serialization/SILFormat.h index 431d2a2f54caa..f24e6f621f8e4 100644 --- a/lib/Serialization/SILFormat.h +++ b/lib/Serialization/SILFormat.h @@ -160,7 +160,7 @@ namespace sil_block { SIL_MOVEONLY_DEINIT, SIL_INST_HAS_SYMBOL, SIL_PACK_ELEMENT_GET, - SIL_PACK_ELEMENT_SET, + SIL_PACK_ELEMENT_SET, }; using SILInstNoOperandLayout = BCRecordLayout< @@ -474,7 +474,7 @@ namespace sil_block { SILTypeCategoryField, // element type category TypeIDField, // pack type SILTypeCategoryField, // pack type category - ValueIDField, // pack value + ValueIDField, // pack value ValueIDField // index value >; @@ -486,7 +486,7 @@ namespace sil_block { ValueIDField, // element value TypeIDField, // pack type SILTypeCategoryField, // pack type category - ValueIDField, // pack value + ValueIDField, // pack value ValueIDField // index value >; diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index 1fa818ec57011..b72711cb499d4 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -1695,6 +1695,25 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { indexRef); break; } + case SILInstructionKind::TuplePackElementAddrInst: { + auto TPEAI = cast(&SI); + auto elementType = TPEAI->getElementType(); + auto elementTypeRef = S.addTypeRef(elementType.getASTType()); + auto tuple = TPEAI->getTuple(); + auto tupleType = tuple->getType(); + auto tupleTypeRef = S.addTypeRef(tupleType.getASTType()); + auto tupleRef = addValueRef(tuple); + auto indexRef = addValueRef(TPEAI->getIndex()); + SILPackElementGetLayout::emitRecord(Out, ScratchRecord, + SILAbbrCodes[SILPackElementGetLayout::Code], + elementTypeRef, + (unsigned) elementType.getCategory(), + tupleTypeRef, + (unsigned) tupleType.getCategory(), + tupleRef, + indexRef); + break; + } case SILInstructionKind::TailAddrInst: { const TailAddrInst *TAI = cast(&SI); SILTailAddrLayout::emitRecord(Out, ScratchRecord, diff --git a/test/SIL/Parser/variadic_generics.sil b/test/SIL/Parser/variadic_generics.sil index 02c2341bb42d2..b4688dbaecbeb 100644 --- a/test/SIL/Parser/variadic_generics.sil +++ b/test/SIL/Parser/variadic_generics.sil @@ -76,3 +76,15 @@ bb0(%i : $Builtin.Word): %ret = tuple () return %ret : $() } + +// CHECK-LABEL: sil @test6 +// CHECK: [[INDEX:%.*]] = dynamic_pack_index +// CHECK: tuple_pack_element_addr [[INDEX]] of %0 : $*(String, repeat each T, Int) as $*@pack_element("01234567-89AB-CDEF-0123-000000000004") U +sil @test6 : $ (@inout (String, repeat each T, Int), Builtin.Word) -> () { +bb0(%tuple : $*(String, repeat each T, Int), %i : $Builtin.Word): + %index = dynamic_pack_index %i of $Pack{Float, repeat each T, Float} + %0 = open_pack_element %index of at , shape $U, uuid "01234567-89AB-CDEF-0123-000000000004" + %elt = tuple_pack_element_addr %index of %tuple : $*(String, repeat each T, Int) as $*@pack_element("01234567-89AB-CDEF-0123-000000000004") U + %ret = tuple () + return %ret : $() +} From 6dca3e9b82a885be9cd171dacd6c1692622bcdd8 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 7 Feb 2023 18:05:44 -0800 Subject: [PATCH 28/98] [AST] Fixed ::getReducedShape for SILPackType. Made TypeBase's implementation dispatch to the implementation specific to SILPackType. --- lib/AST/ParameterPack.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/AST/ParameterPack.cpp b/lib/AST/ParameterPack.cpp index 46689cfa9498a..5955c2dbbeef0 100644 --- a/lib/AST/ParameterPack.cpp +++ b/lib/AST/ParameterPack.cpp @@ -340,6 +340,11 @@ CanType TypeBase::getReducedShape() { if (auto *expansionType = getAs()) return expansionType->getReducedShape(); + if (auto *silPackType = getAs()) { + auto can = cast(silPackType->getCanonicalType()); + return can->getReducedShape(); + } + SmallVector rootParameterPacks; getTypeParameterPacks(rootParameterPacks); From 58954ab405228ebe9dca05f3837265bfb81f182b Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 7 Feb 2023 20:33:27 -0800 Subject: [PATCH 29/98] [IRGen] Added getPackElementSize. --- lib/IRGen/GenPack.cpp | 13 +++++++++---- lib/IRGen/GenPack.h | 10 +++++++--- lib/IRGen/IRGenSIL.cpp | 8 ++++---- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/IRGen/GenPack.cpp b/lib/IRGen/GenPack.cpp index bc92f6d23117a..220fe8cc0ad37 100644 --- a/lib/IRGen/GenPack.cpp +++ b/lib/IRGen/GenPack.cpp @@ -865,15 +865,20 @@ void irgen::cleanupTypeMetadataPack(IRGenFunction &IGF, } } -Address irgen::emitStorageAddressOfPackElement(IRGenFunction &IGF, - Address pack, +Address irgen::emitStorageAddressOfPackElement(IRGenFunction &IGF, Address pack, llvm::Value *index, - SILType elementType) { + SILType elementType, + CanSILPackType packType) { // When we have an indirect pack, the elements are pointers, so we can // simply index into that flat array. assert(elementType.isAddress() && "direct packs not currently supported"); - auto elementSize = IGF.IGM.getPointerSize(); + auto elementSize = getPackElementSize(IGF.IGM, packType); auto elementAddress = IGF.Builder.CreateArrayGEP(pack, index, elementSize); return IGF.Builder.CreateElementBitCast(elementAddress, IGF.IGM.getStoragePointerType(elementType)); } + +Size irgen::getPackElementSize(IRGenModule &IGM, CanSILPackType ty) { + assert(ty->isElementAddress() && "not implemented for direct packs"); + return IGM.getPointerSize(); +} diff --git a/lib/IRGen/GenPack.h b/lib/IRGen/GenPack.h index eac4ae24d9422..0b57b9f99973c 100644 --- a/lib/IRGen/GenPack.h +++ b/lib/IRGen/GenPack.h @@ -17,6 +17,7 @@ #ifndef SWIFT_IRGEN_GENPACK_H #define SWIFT_IRGEN_GENPACK_H +#include "IRGen.h" #include "swift/AST/Types.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" @@ -32,6 +33,7 @@ namespace swift { namespace irgen { class Address; class IRGenFunction; +class IRGenModule; class DynamicMetadataRequest; class MetadataResponse; class StackAddress; @@ -83,9 +85,11 @@ llvm::Value *emitIndexOfStructuralPackComponent(IRGenFunction &IGF, /// /// For indirect packs, note that this is the address of the pack /// array element, not the address stored in the pack array element. -Address emitStorageAddressOfPackElement(IRGenFunction &IGF, - Address pack, llvm::Value *index, - SILType elementType); +Address emitStorageAddressOfPackElement(IRGenFunction &IGF, Address pack, + llvm::Value *index, SILType elementType, + CanSILPackType packType); + +Size getPackElementSize(IRGenModule &, CanSILPackType ty); } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 1508c6c551344..c7a77280e7eb9 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -6897,8 +6897,8 @@ void IRGenSILFunction::visitPackElementGetInst(PackElementGetInst *i) { auto elementType = i->getElementType(); auto &elementTI = getTypeInfo(elementType); - auto elementStorageAddr = - emitStorageAddressOfPackElement(*this, pack, index, elementType); + auto elementStorageAddr = emitStorageAddressOfPackElement( + *this, pack, index, elementType, i->getPackType()); assert(elementType.isAddress() && i->getPackType()->isElementAddress() && @@ -6913,8 +6913,8 @@ void IRGenSILFunction::visitPackElementSetInst(PackElementSetInst *i) { llvm::Value *index = getLoweredSingletonExplosion(i->getIndex()); auto elementType = i->getElementType(); - auto elementStorageAddress = - emitStorageAddressOfPackElement(*this, pack, index, elementType); + auto elementStorageAddress = emitStorageAddressOfPackElement( + *this, pack, index, elementType, i->getPackType()); assert(elementType.isAddress() && i->getPackType()->isElementAddress() && From e2b22d9f62cfce559f6e09d7b972f04d5e172a9c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 8 Feb 2023 00:16:00 -0800 Subject: [PATCH 30/98] [AST/Sema] Remove TypeWrapper feature functionality --- include/swift/AST/Decl.h | 52 -- include/swift/AST/DiagnosticsSema.def | 79 --- include/swift/AST/KnownIdentifiers.def | 9 - include/swift/AST/TypeCheckRequests.h | 188 ------ include/swift/AST/TypeCheckerTypeIDZone.def | 33 - include/swift/AST/TypeWrappers.h | 41 -- lib/AST/ASTPrinter.cpp | 14 - lib/AST/CMakeLists.txt | 1 - lib/AST/TypeCheckRequests.cpp | 4 - lib/AST/TypeWrapper.cpp | 46 -- lib/Sema/CMakeLists.txt | 1 - lib/Sema/CSApply.cpp | 45 -- lib/Sema/CodeSynthesis.cpp | 368 ---------- lib/Sema/TypeCheckAttr.cpp | 447 ------------- lib/Sema/TypeCheckDecl.cpp | 7 - lib/Sema/TypeCheckDeclOverride.cpp | 2 - lib/Sema/TypeCheckStmt.cpp | 15 - lib/Sema/TypeCheckStorage.cpp | 64 +- lib/Sema/TypeCheckType.cpp | 1 - lib/Sema/TypeCheckTypeWrapper.cpp | 632 ------------------ test/IDE/complete_decl_attribute.swift | 11 +- .../AllowErrors/invalid-inheritance.swift | 4 +- 22 files changed, 6 insertions(+), 2058 deletions(-) delete mode 100644 include/swift/AST/TypeWrappers.h delete mode 100644 lib/AST/TypeWrapper.cpp delete mode 100644 lib/Sema/TypeCheckTypeWrapper.cpp diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 2e0ea560f5816..491739cdaa31d 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -35,7 +35,6 @@ #include "swift/AST/StorageImpl.h" #include "swift/AST/TypeAlignments.h" #include "swift/AST/TypeWalker.h" -#include "swift/AST/TypeWrappers.h" #include "swift/AST/Types.h" #include "swift/Basic/ArrayRefView.h" #include "swift/Basic/Compiler.h" @@ -3978,35 +3977,6 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { return getGlobalActorInstance() != nullptr; } - /// Returns true if this type has a type wrapper custom attribute. - bool hasTypeWrapper() const { return bool(getTypeWrapper()); } - - /// Return a type wrapper (if any) associated with this type. - Optional getTypeWrapper() const; - - /// If this declaration has a type wrapper return a property that - /// is used for all type wrapper related operations (mainly for - /// applicable property access routing). - VarDecl *getTypeWrapperProperty() const; - - /// If this declaration has a type wrapper, return `$Storage` - /// declaration that contains all the stored properties managed - /// by the wrapper. Note that if this type is a protocol them - /// this method returns an associated type for $Storage. - TypeDecl *getTypeWrapperStorageDecl() const; - - /// If this declaration is a type wrapper, retrieve - /// its required initializer - `init(storageWrapper:)`. - ConstructorDecl *getTypeWrapperInitializer() const; - - /// Get an initializer that accepts a type wrapper instance to - /// initialize the wrapped type. - ConstructorDecl *getTypeWrappedTypeStorageInitializer() const; - - /// Get a memberwise initializer that could be used to instantiate a - /// type wrapped type. - ConstructorDecl *getTypeWrappedTypeMemberwiseInitializer() const; - // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() >= DeclKind::First_NominalTypeDecl && @@ -5872,17 +5842,6 @@ class VarDecl : public AbstractStorageDecl { /// wrapper that has storage. bool hasStorageOrWrapsStorage() const; - /// Whether this property belongs to a type wrapped type and has - /// all access to it routed through a type wrapper. - bool isAccessedViaTypeWrapper() const; - - /// For type wrapped properties (see \c isAccessedViaTypeWrapper) - /// all access is routed through a type wrapper. - /// - /// \returns an underlying type wrapper property which is a - /// storage endpoint for all access to this property. - VarDecl *getUnderlyingTypeWrapperStorage() const; - /// Visit all auxiliary declarations to this VarDecl. /// /// An auxiliary declaration is a declaration synthesized by the compiler to support @@ -5960,11 +5919,6 @@ class VarDecl : public AbstractStorageDecl { /// backing property will be treated as the member-initialized property. bool isMemberwiseInitialized(bool preferDeclaredProperties) const; - /// Check whether this variable presents a local storage synthesized - /// by the compiler in a user-defined designated initializer to - /// support initialization of type wrapper managed properties. - bool isTypeWrapperLocalStorageForInitializer() const; - /// Return the range of semantics attributes attached to this VarDecl. auto getSemanticsAttrs() const -> decltype(getAttrs().getAttributes()) { @@ -7910,12 +7864,6 @@ class ConstructorDecl : public AbstractFunctionDecl { /// \endcode bool isObjCZeroParameterWithLongSelector() const; - /// If this is a user-defined constructor that belongs to - /// a type wrapped type return a local `_storage` variable - /// injected by the compiler for aid with type wrapper - /// initialization. - VarDecl *getLocalTypeWrapperStorageVar() const; - static bool classof(const Decl *D) { return D->getKind() == DeclKind::Constructor; } diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 54c4bd897e60a..a229cf0806739 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -6748,85 +6748,6 @@ ERROR(move_expression_not_passed_lvalue,none, ERROR(borrow_expression_not_passed_lvalue,none, "'borrow' can only be applied to lvalues", ()) -//------------------------------------------------------------------------------ -// MARK: Type Wrappers -//------------------------------------------------------------------------------ - -ERROR(type_wrappers_are_experimental,none, - "type wrappers are an experimental feature", ()) - -ERROR(type_wrapper_attribute_not_allowed_here,none, - "type wrapper attribute %0 can only be applied to a class, struct, or protocol", - (Identifier)) - -ERROR(type_wrapper_requires_two_generic_params,none, - "type wrapper has to declare two generic parameters: " - "wrapped and storage types", ()) - -ERROR(cannot_use_multiple_type_wrappers,none, - "%0 %1 cannot use more than one type wrapper", - (DescriptiveDeclKind, DeclName)) - -NOTE(type_wrapper_inferred_from,none, - "type wrapper %0 inferred from %1 %2", - (DeclName, DescriptiveDeclKind, DeclName)) - -ERROR(type_wrapper_requires_memberwise_init,none, - "type wrapper type %0 does not contain a required initializer" - " - init(for:storage:)", - (DeclName)) - -ERROR(cannot_overload_type_wrapper_initializer,none, - "cannot overload type wrapper initializer 'init(for:storage:)'", ()) - -ERROR(cannot_declare_type_wrapper_init_with_invalid_first_param,none, - "first parameter of type wrapper initializer should be wrapped type - %0", (Type)) - -ERROR(cannot_declare_type_wrapper_init_with_invalid_second_param,none, - "second parameter of type wrapper initializer should be storage type - %0", (Type)) - -ERROR(type_wrapper_requires_subscript,none, - "type wrapper type %0 does not contain a required subscript" - " - subscript(propertyKeyPath:storageKeyPath:)", - (DeclName)) - -ERROR(type_wrapper_requires_readonly_subscript,none, - "type wrapper type %0 does not contain a required ready-only subscript", - (DeclName)) - -ERROR(type_wrapper_requires_writable_subscript,none, - "type wrapper type %0 does not contain a required writable subscript", - (DeclName)) - -NOTE(add_type_wrapper_subscript_stub_note,none, - "do you want to add a stub?", ()) - -ERROR(type_wrapper_failable_init,none, - "type wrapper initializer %0 cannot be failable", (DeclName)) - -ERROR(type_wrapper_subscript_invalid_parameter_type,none, - "type wrapper subscript parameter %0 expects type %1 (got: %2)", - (Identifier, Type, Type)) - -ERROR(type_wrapper_subscript_invalid_keypath_parameter,none, - "type wrapper subscript parameter %0 expects a key path (got: %1)", - (Identifier, Type)) - -ERROR(type_wrapper_type_requirement_not_accessible,none, - "%select{private|fileprivate|internal|package|public|open}0 %1 %2 cannot have " - "more restrictive access than its enclosing type wrapper type %3 " - "(which is %select{private|fileprivate|internal|package|public|open}4)", - (AccessLevel, DescriptiveDeclKind, DeclName, Type, AccessLevel)) - -ERROR(type_wrapper_ignored_on_local_properties,none, - "%0 must not be used on local properties", (DeclAttribute)) -ERROR(type_wrapper_ignored_on_computed_properties,none, - "%0 must not be used on computed properties", (DeclAttribute)) -ERROR(type_wrapper_ignored_on_static_properties,none, - "%0 must not be used on static properties", (DeclAttribute)) -ERROR(type_wrapper_ignored_on_lazy_properties,none, - "%0 must not be used on lazy properties", (DeclAttribute)) - //------------------------------------------------------------------------------ // MARK: #_hasSymbol //------------------------------------------------------------------------------ diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index f1b197325969e..57cbab1f95226 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -309,15 +309,6 @@ IDENTIFIER(decodeNextArgument) IDENTIFIER(SerializationRequirement) IDENTIFIER_WITH_NAME(builderSelf, "$builderSelf") -// Type wrappers -IDENTIFIER_WITH_NAME(TypeWrapperStorage, "$Storage") -IDENTIFIER_WITH_NAME(TypeWrapperProperty, "$storage") -IDENTIFIER(storageKeyPath) -IDENTIFIER(propertyKeyPath) -IDENTIFIER(wrappedSelf) -IDENTIFIER(storageWrapper) -IDENTIFIER_WITH_NAME(localStorageVar, "_storage") - // Attribute options IDENTIFIER_(_always) IDENTIFIER_(assumed) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 6973e086091ee..9e5e12c8eb9c3 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -66,7 +66,6 @@ struct TypeWitnessAndDecl; class ValueDecl; enum class OpaqueReadOwnership: uint8_t; class StorageImplInfo; -struct TypeWrapperInfo; /// Display a nominal type or extension thereof. void simple_display( @@ -3440,10 +3439,6 @@ enum class CustomAttrTypeKind { /// unbound generic types. PropertyWrapper, - /// Just like property wrappers, type wrappers are represented - /// as custom type attributes and allow unbound generic types. - TypeWrapper, - /// Global actors are represented as custom type attributes. They don't /// have any particularly interesting semantics. GlobalActor, @@ -3618,173 +3613,6 @@ class GetSourceFileAsyncNode bool isCached() const { return true; } }; -/// Return a type wrapper (if any) associated with the given declaration. -class GetTypeWrapper - : public SimpleRequest(NominalTypeDecl *), - RequestFlags::Cached> { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - Optional evaluate(Evaluator &evaluator, - NominalTypeDecl *) const; - -public: - bool isCached() const { return true; } -}; - -/// Inject or get `$Storage` type which has all of the stored properties -/// of the given type with a type wrapper. -class GetTypeWrapperStorage - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - TypeDecl *evaluate(Evaluator &evaluator, NominalTypeDecl *) const; - -public: - bool isCached() const { return true; } -}; - -/// Inject or get `$storage` property which is used to route accesses through -/// to all stored properties of a type that has a type wrapper. -class GetTypeWrapperProperty - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - VarDecl *evaluate(Evaluator &evaluator, NominalTypeDecl *) const; - -public: - bool isCached() const { return true; } -}; - -/// Given a stored property associated with a type wrapped type, -/// produce a property that mirrors it in the type wrapper context. -class GetTypeWrapperStorageForProperty - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - VarDecl *evaluate(Evaluator &evaluator, VarDecl *) const; - -public: - bool isCached() const { return true; } -}; - -/// Synthesize the body of a getter for a stored property that belongs to -/// a type wrapped type. -class SynthesizeTypeWrappedPropertyGetterBody - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - BraceStmt *evaluate(Evaluator &evaluator, AccessorDecl *) const; - -public: - bool isCached() const { return true; } -}; - -/// Synthesize the body of a setter for a stored property that belongs to -/// a type wrapped type. -class SynthesizeTypeWrappedPropertySetterBody - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - BraceStmt *evaluate(Evaluator &evaluator, AccessorDecl *) const; - -public: - bool isCached() const { return true; } -}; - -/// Inject or get `$Storage` type which has all of the stored properties -/// of the given type with a type wrapper. -class IsPropertyAccessedViaTypeWrapper - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - bool evaluate(Evaluator &evaluator, VarDecl *) const; - -public: - bool isCached() const { return true; } -}; - -class SynthesizeTypeWrappedTypeMemberwiseInitializer - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - ConstructorDecl *evaluate(Evaluator &evaluator, NominalTypeDecl *) const; - -public: - bool isCached() const { return true; } -}; - -class SynthesizeTypeWrappedTypeStorageWrapperInitializer - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - ConstructorDecl *evaluate(Evaluator &evaluator, NominalTypeDecl *) const; - -public: - bool isCached() const { return true; } -}; - -class SynthesizeLocalVariableForTypeWrapperStorage - : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - VarDecl *evaluate(Evaluator &evaluator, ConstructorDecl *) const; - -public: - bool isCached() const { return true; } -}; - /// Lookup the target of a break statement. class BreakTargetRequest : public SimpleRequest { -public: - using SimpleRequest::SimpleRequest; - -private: - friend SimpleRequest; - - ConstructorDecl *evaluate(Evaluator &evaluator, NominalTypeDecl *) const; - -public: - bool isCached() const { return true; } -}; - /// Find the definition of a given macro. class MacroDefinitionRequest : public SimpleRequest(NominalTypeDecl *), - Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, GetTypeWrapperStorage, - TypeDecl *(NominalTypeDecl *), - Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, GetTypeWrapperProperty, - VarDecl *(NominalTypeDecl *), - Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, GetTypeWrapperStorageForProperty, - VarDecl *(VarDecl *), - Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, SynthesizeTypeWrappedPropertyGetterBody, - BraceStmt *(AccessorDecl *), - Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, SynthesizeTypeWrappedPropertySetterBody, - BraceStmt *(AccessorDecl *), - Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, IsPropertyAccessedViaTypeWrapper, - bool(VarDecl *), - Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, SynthesizeTypeWrappedTypeMemberwiseInitializer, - ConstructorDecl *(NominalTypeDecl *), - Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, SynthesizeTypeWrappedTypeStorageWrapperInitializer, - ConstructorDecl *(NominalTypeDecl *), - Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, SynthesizeLocalVariableForTypeWrapperStorage, - VarDecl *(ConstructorDecl *), - Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, BreakTargetRequest, LabeledStmt *(const BreakStmt *), Cached, NoLocationInfo) @@ -449,9 +419,6 @@ SWIFT_REQUEST(TypeChecker, PreCheckReturnStmtRequest, SWIFT_REQUEST(TypeChecker, IsSingleValueStmtRequest, IsSingleValueStmtResult(const Stmt *), Cached, NoLocationInfo) -SWIFT_REQUEST(TypeChecker, GetTypeWrapperInitializer, - ConstructorDecl *(NominalTypeDecl *), - Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, MacroDefinitionRequest, MacroDefinition(MacroDecl *), Cached, NoLocationInfo) diff --git a/include/swift/AST/TypeWrappers.h b/include/swift/AST/TypeWrappers.h deleted file mode 100644 index 03a11ef38c92d..0000000000000 --- a/include/swift/AST/TypeWrappers.h +++ /dev/null @@ -1,41 +0,0 @@ -//===--------- TypeWrappers.h - Type Wrapper ASTs ---------------*- C++ -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -// -// This file defines helper types for type wrappers. -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_AST_TYPE_WRAPPERS_H -#define SWIFT_AST_TYPE_WRAPPERS_H - -namespace swift { - -struct TypeWrapperInfo { - CustomAttr *Attr; - Type AttrType; - NominalTypeDecl *Wrapper; - NominalTypeDecl *AttachedTo; - bool IsInferred; - - TypeWrapperInfo(CustomAttr *attr, Type attrType, NominalTypeDecl *wrapperDecl, - NominalTypeDecl *attachedTo, bool isInferred) - : Attr(attr), AttrType(attrType), Wrapper(wrapperDecl), - AttachedTo(attachedTo), IsInferred(isInferred) {} - - TypeWrapperInfo asInferred() const { - return {Attr, AttrType, Wrapper, AttachedTo, true}; - } -}; - -} // end namespace swift - -#endif // SWIFT_AST_TYPE_WRAPPERS_H diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 5dec0247856c5..0927499081c47 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -1186,16 +1186,6 @@ void PrintAST::printAttributes(const Decl *D) { D->getAttrs().print(Printer, Options, D); - // We need to check whether this is a type with an inferred - // type wrapper attribute and if so print it explicitly. - if (auto *NTD = dyn_cast(D)) { - auto typeWrapperInfo = NTD->getTypeWrapper(); - // The attribute has been inferred and we have to print it. - if (typeWrapperInfo && typeWrapperInfo->IsInferred) { - typeWrapperInfo->Attr->print(Printer, Options, D); - } - } - // Print the implicit 'final' attribute. if (auto VD = dyn_cast(D)) { auto VarD = dyn_cast(D); @@ -2975,10 +2965,6 @@ static bool usesFeatureSpecializeAttributeWithAvailability(Decl *decl) { return false; } -static bool usesFeatureTypeWrappers(Decl *decl) { - return false; -} - static bool usesFeatureRuntimeDiscoverableAttrs(Decl *decl) { return false; } diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index a0011d8a68c25..3cb71e63bc27f 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -116,7 +116,6 @@ add_swift_host_library(swiftAST STATIC TypeRefinementContext.cpp TypeRepr.cpp TypeWalker.cpp - TypeWrapper.cpp UnqualifiedLookup.cpp USRGeneration.cpp diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 08d8398404218..ec0b025c7b687 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -1539,10 +1539,6 @@ void swift::simple_display(llvm::raw_ostream &out, CustomAttrTypeKind value) { out << "property-wrapper"; return; - case CustomAttrTypeKind::TypeWrapper: - out << "type-wrapper"; - return; - case CustomAttrTypeKind::GlobalActor: out << "global-actor"; return; diff --git a/lib/AST/TypeWrapper.cpp b/lib/AST/TypeWrapper.cpp deleted file mode 100644 index a3dd1349f2051..0000000000000 --- a/lib/AST/TypeWrapper.cpp +++ /dev/null @@ -1,46 +0,0 @@ -//===--- TypeWrapper.cpp - Type Traversal ---------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -// -// This file implements functionality related to type wrapper feature. -// -//===----------------------------------------------------------------------===// - -#include "swift/AST/ASTContext.h" -#include "swift/AST/Decl.h" -#include "swift/AST/TypeCheckRequests.h" -#include "swift/AST/TypeResolutionStage.h" -#include "swift/AST/TypeWrappers.h" - -using namespace swift; - -Optional NominalTypeDecl::getTypeWrapper() const { - auto *mutableSelf = const_cast(this); - return evaluateOrDefault(getASTContext().evaluator, - GetTypeWrapper{mutableSelf}, - Optional()); -} - -VarDecl *ConstructorDecl::getLocalTypeWrapperStorageVar() const { - auto &ctx = getASTContext(); - auto *mutableSelf = const_cast(this); - return evaluateOrDefault( - ctx.evaluator, SynthesizeLocalVariableForTypeWrapperStorage{mutableSelf}, - nullptr); -} - -bool VarDecl::isTypeWrapperLocalStorageForInitializer() const { - if (auto *ctor = - dyn_cast_or_null(getDeclContext()->getAsDecl())) { - return this == ctor->getLocalTypeWrapperStorageVar(); - } - return false; -} diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index a9eb8c67134ed..8506fbb79a574 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -62,7 +62,6 @@ add_swift_host_library(swiftSema STATIC TypeCheckNameLookup.cpp TypeCheckPattern.cpp TypeCheckPropertyWrapper.cpp - TypeCheckTypeWrapper.cpp TypeCheckProtocol.cpp TypeCheckProtocolInference.cpp TypeCheckRegex.cpp diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index b19cb0e5772e8..4bcda2a8c1359 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -1690,51 +1690,6 @@ namespace { return fnTy->castTo()->getResult(); }; - auto isAssignmentDestination = [&](ConstraintLocatorBuilder locator) { - if (auto *anchor = getAsExpr(locator.getAnchor())) { - if (auto *assignment = - getAsExpr(cs.getParentExpr(anchor))) - return assignment->getDest() == anchor; - } - return false; - }; - - // If this is a reference to an immutable type wrapper - // managed property used as an assignment destination - // i.e. `self. = ...` in an initializer context, - // let's rewrite member access from `self` to `_storage` - // injected by the compiler to support type wrapper - // initialization. - // - // Note that this is safe to do only for immutable - // properties because they do no support re-assignment. - if (isa(cs.DC) && varDecl->isLet() && - varDecl->isAccessedViaTypeWrapper() && - isAssignmentDestination(memberLocator)) { - auto *ctor = cast(cs.DC); - auto *storageVar = ctor->getLocalTypeWrapperStorageVar(); - auto *storageVarTy = - storageVar->getInterfaceType()->castTo(); - - base = - new (context) DeclRefExpr(storageVar, DeclNameLoc(base->getLoc()), - /*implicit=*/true); - base->setType( - LValueType::get(ctor->mapTypeIntoContext(storageVarTy))); - - cs.cacheType(base); - - Expr *memberRefExpr = new (context) TupleElementExpr( - base, /*DotLoc=*/SourceLoc(), - storageVarTy->getNamedElementId(varDecl->getName()), - memberLoc.getBaseNameLoc(), resultType(refTy)); - memberRefExpr->setImplicit(); - - cs.cacheType(memberRefExpr); - - return forceUnwrapIfExpected(memberRefExpr, memberLocator); - } - if (isUnboundInstanceMember) { assert(memberLocator.getBaseLocator() && cs.UnevaluatedRootExprs.count( diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 55bfc480795d8..831f0ca64edac 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -189,35 +189,6 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var, arg->setDefaultArgumentKind(DefaultArgumentKind::StoredProperty); } -static void maybeAddTypeWrapperDefaultArg(ParamDecl *arg, VarDecl *var, - ASTContext &ctx) { - assert(var->isAccessedViaTypeWrapper() || var->hasAttachedPropertyWrapper()); - - if (!(var->getParentPattern() && var->getParentPattern()->getSingleVar())) - return; - - auto *PBD = var->getParentPatternBinding(); - - Expr *initExpr = nullptr; - - if (var->hasAttachedPropertyWrapper()) { - auto initInfo = var->getPropertyWrapperInitializerInfo(); - - if (initInfo.hasInitFromWrappedValue()) { - initExpr = - initInfo.getWrappedValuePlaceholder()->getOriginalWrappedValue(); - } - } else { - initExpr = PBD->getInit(/*index=*/0); - } - - if (!initExpr) - return; - - arg->setDefaultExpr(initExpr, PBD->isInitializerChecked(/*index=*/0)); - arg->setDefaultArgumentKind(DefaultArgumentKind::Normal); -} - /// Describes the kind of implicit constructor that will be /// generated. enum class ImplicitConstructorKind { @@ -232,12 +203,6 @@ enum class ImplicitConstructorKind { /// the instance variables from a parameter of the same type and /// name. Memberwise, - /// The constructor of a type wrapped type that accepts an instance of - /// type wrapper i.e. `init(storageWrapper: Wrapper)`. - TypeWrapperStorage, - /// The memberwise constructor of a type wrapped type which is going to - /// initialize underlying storage for all applicable properties. - TypeWrapperMemberwise, }; static ParamDecl *createMemberwiseInitParameter(DeclContext *DC, @@ -359,122 +324,6 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl, arg->setInterfaceType(systemTy); arg->setImplicit(); - params.push_back(arg); - } - } else if (ICK == ImplicitConstructorKind::TypeWrapperStorage) { - accessLevel = decl->getTypeWrapperStorageDecl()->getFormalAccess(); - - auto typeWrapperInfo = decl->getTypeWrapper(); - assert(typeWrapperInfo); - - auto *typeWrapper = typeWrapperInfo->Wrapper; - - auto *arg = new (ctx) ParamDecl(SourceLoc(), Loc, ctx.Id_storageWrapper, - Loc, ctx.Id_storageWrapper, decl); - - auto typeWrapperType = typeWrapper->getDeclaredInterfaceType(); - - TypeSubstitutionMap subs; - { - auto genericParams = - typeWrapper->getGenericSignature().getInnermostGenericParams(); - // Wrapped -> wrapped type - subs[genericParams[0]->getCanonicalType()->castTo()] = - decl->getDeclaredInterfaceType(); - // Storage -> $Storage - subs[genericParams[1]->getCanonicalType()->castTo()] = - decl->getTypeWrapperStorageDecl()->getDeclaredInterfaceType(); - } - - auto paramType = typeWrapperType.subst(SubstitutionMap::get( - typeWrapper->getGenericSignature(), QueryTypeSubstitutionMap{subs}, - LookUpConformanceInModule(decl->getParentModule()))); - - arg->setSpecifier(ParamSpecifier::Default); - arg->setInterfaceType(paramType); - arg->setImplicit(); - - params.push_back(arg); - } else if (ICK == ImplicitConstructorKind::TypeWrapperMemberwise) { - // Access to the initializer should match that of its parent type. - accessLevel = decl->getEffectiveAccess(); - - for (auto *member : decl->getMembers()) { - auto *var = dyn_cast(member); - if (!var) - continue; - - if (!var->isAccessedViaTypeWrapper()) { - // Compiler synthesized properties are not included. - if (var->isImplicit()) - continue; - - // Computed properties are not included, except in cases - // where property has a property wrapper and `@typeWrapperIgnored` - // attribute. - if (!var->hasStorage() && - !(var->hasAttachedPropertyWrapper() && - var->getAttrs().hasAttribute())) - continue; - - // If this is a memberwise initializeable property include - // it into the type wrapper initializer otherwise the instance - // of type wrapped type wouldn't be completely initialized. - if (var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) - params.push_back(createMemberwiseInitParameter(decl, Loc, var)); - - continue; - } - - Identifier argName = var->getName(); - Identifier paramName = argName; - - auto paramInterfaceType = var->getValueInterfaceType(); - DeclAttributes attrs; - - // If this is a backing storage of a property wrapped property - // let's use wrapped property as a parameter and synthesize - // appropriate property wrapper initialization upon assignment. - if (auto *wrappedVar = var->getOriginalWrappedProperty( - PropertyWrapperSynthesizedPropertyKind::Backing)) { - // If there is `init(wrappedValue:)` or default value for a wrapped - // property we should use wrapped type, otherwise let's proceed with - // wrapper type. - if (wrappedVar->isPropertyMemberwiseInitializedWithWrappedType()) { - var = wrappedVar; - // If parameter have to get wrapped type, let's re-map both argument - // and parameter name to match wrapped property and let property - // wrapper attributes generate wrapped value and projection variables. - argName = wrappedVar->getName(); - paramName = argName; - - paramInterfaceType = var->getPropertyWrapperInitValueInterfaceType(); - // The parameter needs to have all of the property wrapper - // attributes to generate projection and wrapper variables. - for (auto *attr : wrappedVar->getAttachedPropertyWrappers()) - attrs.add(attr); - } else { - // If parameter has to have wrapper type then argument type should - // match that of a wrapped property but parameter name stays the same - // since it represents the type of backing storage and could be passed - // to `$Storage` constructor directly. - argName = wrappedVar->getName(); - } - } - - if (!paramInterfaceType || paramInterfaceType->hasError()) - continue; - - auto *arg = - new (ctx) ParamDecl(SourceLoc(), Loc, argName, Loc, paramName, decl); - - arg->getAttrs().add(attrs); - arg->setSpecifier(ParamSpecifier::Default); - arg->setInterfaceType(paramInterfaceType); - arg->setImplicit(); - - maybeAddTypeWrapperDefaultArg(arg, var, ctx); - params.push_back(arg); } } @@ -1270,10 +1119,6 @@ static bool shouldAttemptInitializerSynthesis(const NominalTypeDecl *decl) { if (decl->isInvalid()) return false; - // Don't attempt if the decl has a type wrapper. - if (decl->hasTypeWrapper()) - return false; - return true; } @@ -1283,20 +1128,6 @@ void TypeChecker::addImplicitConstructors(NominalTypeDecl *decl) { return; if (!shouldAttemptInitializerSynthesis(decl)) { - if (decl->hasTypeWrapper()) { - auto &ctx = decl->getASTContext(); - - // Synthesize a special `init(storageWrapper: )` - // initializer if possible. - (void)decl->getTypeWrappedTypeStorageInitializer(); - - // If declaration is type wrapped and there are no - // designated initializers, synthesize a special - // memberwise initializer that would instantiate `$storage`. - if (!hasUserDefinedDesignatedInit(ctx.evaluator, decl)) - (void)decl->getTypeWrappedTypeMemberwiseInitializer(); - } - decl->setAddedImplicitInitializers(); return; } @@ -1617,202 +1448,3 @@ bool swift::addNonIsolatedToSynthesized(NominalTypeDecl *nominal, value->getAttrs().add(new (ctx) NonisolatedAttr(/*isImplicit=*/true)); return true; } - -static std::pair -synthesizeTypeWrappedTypeStorageWrapperInitializerBody( - AbstractFunctionDecl *decl, void *) { - auto &ctx = decl->getASTContext(); - auto *ctor = cast(decl); - auto *wrappedType = ctor->getDeclContext()->getSelfNominalTypeDecl(); - auto *storageProperty = wrappedType->getTypeWrapperProperty(); - - // self.$storage = storageWrapper - SmallVector body; - { - auto *storageVarRef = UnresolvedDotExpr::createImplicit( - ctx, - new (ctx) DeclRefExpr({ctor->getImplicitSelfDecl()}, - /*Loc=*/DeclNameLoc(), /*Implicit=*/true), - storageProperty->getName()); - - auto *paramRef = new (ctx) - DeclRefExpr(ctor->getParameters()->get(0), /*Loc=*/DeclNameLoc(), - /*Implicit=*/true); - - body.push_back(new (ctx) AssignExpr(storageVarRef, /*EqualLoc=*/SourceLoc(), - paramRef, - /*Implicit=*/true)); - } - - return {BraceStmt::create(ctx, /*lbloc=*/ctor->getLoc(), body, - /*rbloc=*/ctor->getLoc(), /*implicit=*/true), - /*isTypeChecked=*/false}; -} - -ConstructorDecl *SynthesizeTypeWrappedTypeStorageWrapperInitializer::evaluate( - Evaluator &evaluator, NominalTypeDecl *wrappedType) const { - if (isa(wrappedType)) - return nullptr; - - if (!wrappedType->hasTypeWrapper()) - return nullptr; - - auto &ctx = wrappedType->getASTContext(); - - // .swiftinterfaces have both attribute and a synthesized member - // (if it's public), so in this case we need use existing declaration - // if available. - { - auto parentSF = wrappedType->getDeclContext()->getParentSourceFile(); - if (parentSF && parentSF->Kind == SourceFileKind::Interface) { - DeclName initName(ctx, DeclBaseName::createConstructor(), - /*labels=*/{ctx.Id_storageWrapper}); - auto results = wrappedType->lookupDirect(initName); - if (results.size() == 1) - return cast(results.front()); - } - } - - // `@typeWrapperIgnored` properties suppress this initializer. - if (llvm::any_of(wrappedType->getMembers(), [&](Decl *member) { - return member->getAttrs().hasAttribute(); - })) - return nullptr; - - // Create the implicit type wrapper storage constructor. - auto ctor = createImplicitConstructor( - wrappedType, ImplicitConstructorKind::TypeWrapperStorage, ctx); - wrappedType->addMember(ctor); - - ctor->setBodySynthesizer( - synthesizeTypeWrappedTypeStorageWrapperInitializerBody); - return ctor; -} - -static std::pair -synthesizeTypeWrappedTypeMemberwiseInitializerBody(AbstractFunctionDecl *decl, - void *) { - auto *ctor = cast(decl); - auto &ctx = ctor->getASTContext(); - auto *parent = ctor->getDeclContext()->getSelfNominalTypeDecl(); - - assert(!isa(parent)); - - // self.$storage = .init(storage: $Storage(...)) - auto *storageType = - cast(parent->getTypeWrapperStorageDecl()); - assert(storageType); - - auto *typeWrapperVar = parent->getTypeWrapperProperty(); - assert(typeWrapperVar); - - auto *storageVarRef = UnresolvedDotExpr::createImplicit( - ctx, - new (ctx) DeclRefExpr({ctor->getImplicitSelfDecl()}, - /*Loc=*/DeclNameLoc(), /*Implicit=*/true), - typeWrapperVar->getName()); - - // Check whether given parameter requires a direct assignment to - // intialize the property. - // - // If `$Storage` doesn't have a member that corresponds - // to the current parameter it means that this is a property - // that not managed by the type wrapper which has to be - // initialized by direct assignment: `self. = ` - auto useDirectAssignment = [&](ParamDecl *param) { - // Properties with property wrappers are always managed by the type wrapper - if (param->hasAttachedPropertyWrapper()) - return false; - return storageType->lookupDirect(param->getName()).empty(); - }; - - SmallVector body; - - SmallVector initArgs; - { - for (auto *param : *ctor->getParameters()) { - VarDecl *arg = param; - - if (useDirectAssignment(param)) { - auto *propRef = UnresolvedDotExpr::createImplicit( - ctx, - new (ctx) DeclRefExpr({ctor->getImplicitSelfDecl()}, - /*Loc=*/DeclNameLoc(), /*Implicit=*/true), - arg->getName()); - - body.push_back(new (ctx) AssignExpr( - propRef, /*EqualLoc=*/SourceLoc(), - new (ctx) DeclRefExpr({arg}, /*DeclNameLoc=*/DeclNameLoc(), - /*Implicit=*/true), - /*Implicit=*/true)); - continue; - } - - // type wrappers wrap only backing storage of a wrapped - // property, so in this case we need to pass `_` to - // `$Storage` constructor. - if (param->hasAttachedPropertyWrapper()) { - arg = param->getPropertyWrapperBackingProperty(); - (void)param->getPropertyWrapperBackingPropertyType(); - } - - initArgs.push_back({/*labelLoc=*/SourceLoc(), arg->getName(), - new (ctx) DeclRefExpr(arg, /*Loc=*/DeclNameLoc(), - /*Implicit=*/true)}); - } - } - - auto *storageInit = CallExpr::createImplicit( - ctx, - TypeExpr::createImplicitForDecl( - /*Loc=*/DeclNameLoc(), storageType, ctor, - ctor->mapTypeIntoContext(storageType->getInterfaceType())), - ArgumentList::createImplicit(ctx, initArgs)); - - auto *initRef = new (ctx) UnresolvedMemberExpr( - /*dotLoc=*/SourceLoc(), /*declNameLoc=*/DeclNameLoc(), - DeclNameRef::createConstructor(), /*implicit=*/true); - { initRef->setFunctionRefKind(FunctionRefKind::DoubleApply); } - - auto *selfTypeRef = TypeExpr::createImplicitForDecl( - DeclNameLoc(), parent, parent->getDeclContext(), - ctor->mapTypeIntoContext(parent->getInterfaceType())); - - auto *selfRef = new (ctx) - DotSelfExpr(selfTypeRef, /*dot=*/SourceLoc(), /*self=*/SourceLoc()); - selfRef->setImplicit(); - - // .init($Storage(for:storage:)) - Expr *typeWrapperInit = CallExpr::createImplicit( - ctx, initRef, - ArgumentList::createImplicit( - ctx, - {Argument(/*labelLoc=*/SourceLoc(), ctx.Id_for, selfRef), - Argument(/*labelLoc=*/SourceLoc(), ctx.Id_storage, storageInit)})); - - body.push_back(new (ctx) AssignExpr(storageVarRef, /*EqualLoc=*/SourceLoc(), - typeWrapperInit, - /*Implicit=*/true)); - - return {BraceStmt::create(ctx, /*lbloc=*/ctor->getLoc(), body, - /*rbloc=*/ctor->getLoc(), /*implicit=*/true), - /*isTypeChecked=*/false}; -} - -ConstructorDecl *SynthesizeTypeWrappedTypeMemberwiseInitializer::evaluate( - Evaluator &evaluator, NominalTypeDecl *wrappedType) const { - if (isa(wrappedType)) - return nullptr; - - if (!wrappedType->hasTypeWrapper()) - return nullptr; - - // Create the implicit memberwise constructor. - auto &ctx = wrappedType->getASTContext(); - auto ctor = createImplicitConstructor( - wrappedType, ImplicitConstructorKind::TypeWrapperMemberwise, ctx); - wrappedType->addMember(ctor); - - ctor->setBodySynthesizer(synthesizeTypeWrappedTypeMemberwiseInitializerBody); - return ctor; -} diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 9f70928d3c4bb..b177682b6338f 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -297,8 +297,6 @@ class AttributeChecker : public AttributeVisitor { void visitCustomAttr(CustomAttr *attr); void visitPropertyWrapperAttr(PropertyWrapperAttr *attr); - void visitTypeWrapperAttr(TypeWrapperAttr *attr); - void visitTypeWrapperIgnoredAttr(TypeWrapperIgnoredAttr *attr); void visitResultBuilderAttr(ResultBuilderAttr *attr); void visitImplementationOnlyAttr(ImplementationOnlyAttr *attr); @@ -3643,18 +3641,6 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) { return; } - if (nominal->getAttrs().hasAttribute()) { - if (!(isa(D) || isa(D) || isa(D))) { - diagnose(attr->getLocation(), - diag::type_wrapper_attribute_not_allowed_here, - nominal->getName()); - attr->setInvalid(); - return; - } - - return; - } - if (nominal->getAttrs().hasAttribute()) { auto markInvalidApplication = [&]() { diagnoseAndRemoveAttr(attr, @@ -3884,439 +3870,6 @@ void AttributeChecker::visitPropertyWrapperAttr(PropertyWrapperAttr *attr) { (void)nominal->getPropertyWrapperTypeInfo(); } -void AttributeChecker::visitTypeWrapperAttr(TypeWrapperAttr *attr) { - auto isEnabled = [&]() { - if (Ctx.LangOpts.hasFeature(Feature::TypeWrappers)) - return true; - - // Accept attributes that come from swiftinterface files. - auto *parentSF = D->getDeclContext()->getParentSourceFile(); - return parentSF && parentSF->Kind == SourceFileKind::Interface; - }; - - if (!isEnabled()) { - diagnose(attr->getLocation(), diag::type_wrappers_are_experimental); - attr->setInvalid(); - return; - } - - auto nominal = dyn_cast(D); - if (!nominal) - return; - - auto &ctx = D->getASTContext(); - - enum class UnviabilityReason { - Failable, - InvalidWrappedSelfType, - InvalidPropertyType, - InvalidStorageType, - Inaccessible - }; - - auto findMembersOrDiagnose = [&](DeclName memberName, - SmallVectorImpl &results, - Diag notFoundDiagnostic) -> bool { - nominal->lookupQualified(nominal, DeclNameRef(memberName), - NL_QualifiedDefault, results); - - if (results.empty()) { - diagnose(nominal->getLoc(), notFoundDiagnostic, nominal->getName()); - attr->setInvalid(); - return true; - } - return false; - }; - - // Check whether type marked as @typeWrapper is valid: - // - // - Has two generic parameters - `Wrapped` and `Storage` - // - Has `init(for: .Type, storage: )` - // - Has at least one `subscript(storedKeyPath: KeyPath<...>)` overload - - // Has a single generic parameter. - auto *genericParams = nominal->getGenericParams(); - { - if (!genericParams || genericParams->size() != 2) { - diagnose(nominal->getLoc(), - diag::type_wrapper_requires_two_generic_params); - attr->setInvalid(); - return; - } - } - - // `init(for:storage:)` - { - DeclName initName(ctx, DeclBaseName::createConstructor(), - {ctx.Id_for, ctx.Id_storage}); - - SmallVector inits; - if (findMembersOrDiagnose(initName, inits, - diag::type_wrapper_requires_memberwise_init)) - return; - - llvm::SmallDenseMap, 2> - nonViableInits; - for (auto *decl : inits) { - auto *init = cast(decl); - - if (init->isFailable()) - nonViableInits[init].push_back(UnviabilityReason::Failable); - - if (isMemberLessAccessibleThanType(nominal, init)) - nonViableInits[init].push_back(UnviabilityReason::Inaccessible); - } - - unsigned numViable = inits.size() - nonViableInits.size(); - - switch (numViable) { - case 0: { - // If there are no viable initializers, let's complain. - for (const auto &entry : nonViableInits) { - auto *init = entry.first; - - for (auto reason : entry.second) { - switch (reason) { - case UnviabilityReason::Failable: - diagnose(init, diag::type_wrapper_failable_init, init->getName()); - break; - - case UnviabilityReason::Inaccessible: - diagnose(init, diag::type_wrapper_type_requirement_not_accessible, - init->getFormalAccess(), init->getDescriptiveKind(), - init->getName(), nominal->getDeclaredType(), - nominal->getFormalAccess()); - break; - - case UnviabilityReason::InvalidStorageType: - case UnviabilityReason::InvalidPropertyType: - case UnviabilityReason::InvalidWrappedSelfType: - llvm_unreachable("init(storage:) type is not checked"); - } - } - } - - attr->setInvalid(); - return; - } - - case 1: { - // If there is only one choice let's check whether it's correct. - for (auto *decl : inits) { - auto *ctor = cast(decl); - - if (nonViableInits.count(ctor)) - continue; - - auto wrappedType = ctor->getParameters()->get(0)->getInterfaceType(); - auto storageType = ctor->getParameters()->get(1)->getInterfaceType(); - - auto typeWrapperGenericParams = genericParams->getParams(); - - // Let's check wrapped type - it should be a metatype of the first - // generic parameter - . - { - auto wrappedTypeParamTy = - typeWrapperGenericParams[0]->getInterfaceType(); - if (!wrappedType->isEqual(wrappedTypeParamTy)) { - diagnose( - ctor, - diag::cannot_declare_type_wrapper_init_with_invalid_first_param, - wrappedTypeParamTy); - ctor->setInvalid(); - } - } - - // Second parameter should be generic parameter type. - { - auto storageTypeParamTy = typeWrapperGenericParams[1] - ->getInterfaceType() - ->getMetatypeInstanceType(); - if (!storageType->isEqual(storageTypeParamTy)) { - diagnose( - ctor, - diag:: - cannot_declare_type_wrapper_init_with_invalid_second_param, - storageTypeParamTy); - ctor->setInvalid(); - } - } - - if (ctor->isInvalid()) { - attr->setInvalid(); - return; - } - } - break; - } - - default: - diagnose(inits.front(), diag::cannot_overload_type_wrapper_initializer); - return; - } - } - - // subscript([wrappedSelf: Wrapped], propertyKeyPath: KeyPath, storedKeypath: - // {Writable}KeyPath) - { - SmallVector subscripts; - - // Let's try to find all of the required subscripts. - { - DeclName subscriptName(ctx, DeclBaseName::createSubscript(), - {ctx.Id_propertyKeyPath, ctx.Id_storageKeyPath}); - - if (findMembersOrDiagnose(subscriptName, subscripts, - diag::type_wrapper_requires_subscript)) - return; - } - - llvm::SmallDenseMap, 2> - nonViableSubscripts; - - bool hasReadOnly = false; - bool hasWritable = false; - - auto hasKeyPathType = [](ParamDecl *PD) { - if (auto *BGT = PD->getInterfaceType()->getAs()) { - return BGT->isKeyPath() || BGT->isWritableKeyPath() || - BGT->isReferenceWritableKeyPath(); - } - return false; - }; - - auto getPropertyKeyPathParamIndex = - [](SubscriptDecl *subscript) -> unsigned { - return subscript->getIndices()->size() == 2 ? 0 : 1; - }; - - auto getStorageKeyPathParamIndex = - [&](SubscriptDecl *subscript) -> unsigned { - return getPropertyKeyPathParamIndex(subscript) + 1; - }; - - auto diagnoseSubscript = [&](SubscriptDecl *subscript, - ArrayRef reasons) { - for (auto reason : reasons) { - switch (reason) { - case UnviabilityReason::InvalidWrappedSelfType: { - auto wrappedSelfExpectedTy = - genericParams->getParams()[0]->getDeclaredInterfaceType(); - auto paramTy = subscript->getIndices()->get(0)->getInterfaceType(); - diagnose(subscript, - diag::type_wrapper_subscript_invalid_parameter_type, - ctx.Id_wrappedSelf, wrappedSelfExpectedTy, paramTy); - break; - } - - case UnviabilityReason::InvalidPropertyType: { - auto paramTy = subscript->getIndices() - ->get(getPropertyKeyPathParamIndex(subscript)) - ->getInterfaceType(); - diagnose(subscript, - diag::type_wrapper_subscript_invalid_keypath_parameter, - ctx.Id_propertyKeyPath, paramTy); - break; - } - - case UnviabilityReason::InvalidStorageType: { - auto paramTy = subscript->getIndices() - ->get(getStorageKeyPathParamIndex(subscript)) - ->getInterfaceType(); - diagnose(subscript, - diag::type_wrapper_subscript_invalid_keypath_parameter, - ctx.Id_storageKeyPath, paramTy); - break; - } - - case UnviabilityReason::Inaccessible: - diagnose(subscript, - diag::type_wrapper_type_requirement_not_accessible, - subscript->getFormalAccess(), - subscript->getDescriptiveKind(), subscript->getName(), - nominal->getDeclaredType(), nominal->getFormalAccess()); - break; - - case UnviabilityReason::Failable: - llvm_unreachable("subscripts cannot be failable"); - } - } - }; - - for (auto *decl : subscripts) { - auto *subscript = cast(decl); - - auto *indices = subscript->getIndices(); - - // Ignore `wrappedSelf`. - bool forReferenceType = indices->size() == 3; - - if (forReferenceType) { - auto wrappedTypeParamTy = - genericParams->getParams()[0]->getDeclaredInterfaceType(); - - auto wrappedSelf = indices->get(0); - if (!wrappedSelf->getInterfaceType()->isEqual(wrappedTypeParamTy)) { - nonViableSubscripts[subscript].push_back( - UnviabilityReason::InvalidWrappedSelfType); - } - } - - auto *propertyKeyPathParam = - indices->get(getPropertyKeyPathParamIndex(subscript)); - { - if (!hasKeyPathType(propertyKeyPathParam)) { - nonViableSubscripts[subscript].push_back( - UnviabilityReason::InvalidPropertyType); - } - } - - auto *storageKeyPathParam = - indices->get(getStorageKeyPathParamIndex(subscript)); - { - if (hasKeyPathType(storageKeyPathParam)) { - auto type = storageKeyPathParam->getInterfaceType(); - hasReadOnly |= type->isKeyPath(); - hasWritable |= - type->isWritableKeyPath() || type->isReferenceWritableKeyPath(); - } else { - nonViableSubscripts[subscript].push_back( - UnviabilityReason::InvalidStorageType); - } - - if (isMemberLessAccessibleThanType(nominal, subscript)) - nonViableSubscripts[subscript].push_back( - UnviabilityReason::Inaccessible); - } - } - - if (!hasReadOnly) { - auto &DE = ctx.Diags; - DE.diagnoseWithNotes( - DE.diagnose(nominal->getLoc(), - diag::type_wrapper_requires_readonly_subscript, - nominal->getName()), - [&]() { - DE.diagnose(nominal->getLoc(), - diag::add_type_wrapper_subscript_stub_note) - .fixItInsertAfter( - nominal->getBraces().Start, - "\nsubscript(propertyKeyPath propPath: " - "KeyPath<<#WrappedType#>, Value>, storageKeyPath " - "storagePath: KeyPath<<#Base#>, " - "Value>) -> Value { get { <#code#> } }"); - }); - attr->setInvalid(); - } - - if (!hasWritable) { - auto &DE = ctx.Diags; - DE.diagnoseWithNotes( - DE.diagnose(nominal->getLoc(), - diag::type_wrapper_requires_writable_subscript, - nominal->getName()), - [&]() { - DE.diagnose(nominal->getLoc(), - diag::add_type_wrapper_subscript_stub_note) - .fixItInsertAfter( - nominal->getBraces().Start, - "\nsubscript(propertyKeyPath propPath: " - "KeyPath<<#WrappedType#>, Value>, storageKeyPath " - "storagePath: " - "WritableKeyPath<<#Base#>, " - "Value>) -> Value { get { <#code#> } set { <#code#> } " - "}"); - }); - attr->setInvalid(); - } - - if (subscripts.size() - nonViableSubscripts.size() == 0) { - for (const auto &entry : nonViableSubscripts) { - diagnoseSubscript(entry.first, entry.second); - } - - attr->setInvalid(); - return; - } - - // If there were no issues with required subscripts, let's look - // for subscripts that are applicable only to reference types and - // diagnose them inline. - { - DeclName subscriptName( - ctx, DeclBaseName::createSubscript(), - {ctx.Id_wrappedSelf, ctx.Id_propertyKeyPath, ctx.Id_storageKeyPath}); - - for (auto *candidate : nominal->lookupDirect(subscriptName)) { - auto *subscript = cast(candidate); - auto *indices = subscript->getIndices(); - - auto wrappedTypeParamTy = - genericParams->getParams()[0]->getDeclaredInterfaceType(); - - auto wrappedSelf = indices->get(0); - if (!wrappedSelf->getInterfaceType()->isEqual(wrappedTypeParamTy)) { - diagnoseSubscript(subscript, - UnviabilityReason::InvalidWrappedSelfType); - } - - auto *propertyKeyPathParam = - indices->get(getPropertyKeyPathParamIndex(subscript)); - if (!hasKeyPathType(propertyKeyPathParam)) - diagnoseSubscript(subscript, UnviabilityReason::InvalidPropertyType); - - auto *storageKeyPathParam = - indices->get(getStorageKeyPathParamIndex(subscript)); - if (!hasKeyPathType(storageKeyPathParam)) - diagnoseSubscript(subscript, UnviabilityReason::InvalidStorageType); - - if (isMemberLessAccessibleThanType(nominal, subscript)) - diagnoseSubscript(subscript, UnviabilityReason::Inaccessible); - } - } - } -} - -void AttributeChecker::visitTypeWrapperIgnoredAttr(TypeWrapperIgnoredAttr *attr) { - auto *var = cast(D); - - // @typeWrapperIgnored applies only to properties that type wrapper can manage. - if (var->getDeclContext()->isLocalContext()) { - diagnoseAndRemoveAttr(attr, diag::type_wrapper_ignored_on_local_properties, attr); - return; - } - - if (var->isLet()) { - diagnoseAndRemoveAttr(attr, diag::attr_only_one_decl_kind, attr, "var"); - return; - } - - if (var->getAttrs().hasAttribute()) { - diagnoseAndRemoveAttr(attr, diag::type_wrapper_ignored_on_lazy_properties, attr); - return; - } - - if (var->isStatic()) { - diagnoseAndRemoveAttr(attr, diag::type_wrapper_ignored_on_static_properties, attr); - return; - } - - // computed properties - { - SmallVector accessors{AccessorKind::Get, AccessorKind::Set, - AccessorKind::Modify, - AccessorKind::MutableAddress}; - - if (llvm::any_of(accessors, [&var](const auto &accessor) { - return var->getParsedAccessor(accessor); - })) { - diagnoseAndRemoveAttr( - attr, diag::type_wrapper_ignored_on_computed_properties, attr); - return; - } - } -} - void AttributeChecker::visitResultBuilderAttr(ResultBuilderAttr *attr) { auto *nominal = dyn_cast(D); auto &ctx = D->getASTContext(); diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 8377e03e7619a..0b7be225393b8 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2785,13 +2785,6 @@ static ArrayRef evaluateMembersRequest( ResolveImplicitMemberRequest{nominal, ImplicitMemberAction::ResolveCodingKeys}, {}); - - // Synthesize $Storage type and `var $storage` associated with - // type wrapped type. - if (nominal->hasTypeWrapper()) { - (void)nominal->getTypeWrapperStorageDecl(); - (void)nominal->getTypeWrapperProperty(); - } } // Expand synthesized member macros. diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 1d4c4eff52094..a6c7376cc42e2 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1587,8 +1587,6 @@ namespace { UNINTERESTING_ATTR(SPIOnly) UNINTERESTING_ATTR(Custom) UNINTERESTING_ATTR(PropertyWrapper) - UNINTERESTING_ATTR(TypeWrapper) - UNINTERESTING_ATTR(TypeWrapperIgnored) UNINTERESTING_ATTR(DisfavoredOverload) UNINTERESTING_ATTR(ResultBuilder) UNINTERESTING_ATTR(ProjectedValueProperty) diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index a5a548c5fe23d..7fc38d7ed5c21 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -2493,21 +2493,6 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator, } } } else if (auto *ctor = dyn_cast(AFD)) { - // If this is user-defined constructor that requires `_storage` - // variable injection, do so now so now. - if (auto *storageVar = ctor->getLocalTypeWrapperStorageVar()) { - SmallVector Elts; - - Elts.push_back(storageVar->getParentPatternBinding()); - Elts.push_back(storageVar); - - Elts.append(body->getElements().begin(), - body->getElements().end()); - - body = BraceStmt::create(ctx, body->getLBraceLoc(), Elts, - body->getRBraceLoc(), body->isImplicit()); - } - if (body->empty() || !isKnownEndOfConstructor(body->getLastElement())) { // For constructors, we make sure that the body ends with a "return" stmt, diff --git a/lib/Sema/TypeCheckStorage.cpp b/lib/Sema/TypeCheckStorage.cpp index 7ca2b131df899..1afc177d9b9a3 100644 --- a/lib/Sema/TypeCheckStorage.cpp +++ b/lib/Sema/TypeCheckStorage.cpp @@ -108,11 +108,6 @@ static bool hasStoredProperties(NominalTypeDecl *decl, static void computeLoweredStoredProperties(NominalTypeDecl *decl, IterableDeclContext *implDecl) { - // If declaration has a type wrapper, make sure that - // `$storage` property is synthesized. - if (decl->hasTypeWrapper()) - (void)decl->getTypeWrapperProperty(); - // Expand synthesized member macros. auto &ctx = decl->getASTContext(); evaluateOrDefault(ctx.evaluator, @@ -133,10 +128,6 @@ static void computeLoweredStoredProperties(NominalTypeDecl *decl, (void) var->getPropertyWrapperAuxiliaryVariables(); (void) var->getPropertyWrapperInitializerInfo(); } - - if (var->isAccessedViaTypeWrapper()) { - (void)var->getUnderlyingTypeWrapperStorage(); - } } // If this is an actor, check conformance to the Actor protocol to @@ -955,9 +946,7 @@ static Expr *buildStorageReference(AccessorDecl *accessor, underlyingVars.push_back({ wrappedValue, isWrapperRefLValue }); } } - semantics = backing->isAccessedViaTypeWrapper() - ? AccessSemantics::DirectToImplementation - : AccessSemantics::DirectToStorage; + semantics = AccessSemantics::DirectToStorage; selfAccessKind = SelfAccessorKind::Peer; break; } @@ -986,9 +975,7 @@ static Expr *buildStorageReference(AccessorDecl *accessor, { var->getAttachedPropertyWrapperTypeInfo(0).projectedValueVar, isLValue }); } - semantics = backing->isAccessedViaTypeWrapper() - ? AccessSemantics::DirectToImplementation - : AccessSemantics::DirectToStorage; + semantics = AccessSemantics::DirectToStorage; selfAccessKind = SelfAccessorKind::Peer; break; } @@ -1547,35 +1534,12 @@ synthesizeInvalidAccessor(AccessorDecl *accessor, ASTContext &ctx) { return { BraceStmt::create(ctx, loc, ArrayRef(), loc, true), true }; } -/// Synthesize the body of a getter for a stored property that belongs to -/// a type wrapped type. -static std::pair -synthesizeTypeWrappedPropertyGetterBody(AccessorDecl *getter, ASTContext &ctx) { - auto *body = evaluateOrDefault( - ctx.evaluator, SynthesizeTypeWrappedPropertyGetterBody{getter}, nullptr); - return body ? std::make_pair(body, /*isTypeChecked=*/false) - : synthesizeInvalidAccessor(getter, ctx); -} - -static std::pair -synthesizeTypeWrappedPropertySetterBody(AccessorDecl *getter, ASTContext &ctx) { - auto *body = evaluateOrDefault( - ctx.evaluator, SynthesizeTypeWrappedPropertySetterBody{getter}, nullptr); - return body ? std::make_pair(body, /*isTypeChecked=*/false) - : synthesizeInvalidAccessor(getter, ctx); -} - static std::pair synthesizeGetterBody(AccessorDecl *getter, ASTContext &ctx) { auto storage = getter->getStorage(); // Synthesize the getter for a lazy property or property wrapper. if (auto var = dyn_cast(storage)) { - // If the type this stored property belongs to has a type wrapper - // we need to route getter/setter through the wrapper. - if (var->isAccessedViaTypeWrapper()) - return synthesizeTypeWrappedPropertyGetterBody(getter, ctx); - if (var->getAttrs().hasAttribute()) { auto *storage = var->getLazyStorageProperty(); return synthesizeLazyGetterBody(getter, var, storage, ctx); @@ -1812,11 +1776,6 @@ synthesizeSetterBody(AccessorDecl *setter, ASTContext &ctx) { // Synthesize the setter for a lazy property or property wrapper. if (auto var = dyn_cast(storage)) { - // If the type this stored property belongs to has a type wrapper - // we need to route getter/setter through the wrapper. - if (var->isAccessedViaTypeWrapper()) - return synthesizeTypeWrappedPropertySetterBody(setter, ctx); - if (var->getAttrs().hasAttribute()) { // Lazy property setters write to the underlying storage. if (var->hasObservers()) { @@ -2500,12 +2459,6 @@ IsAccessorTransparentRequest::evaluate(Evaluator &evaluator, switch (accessor->getAccessorKind()) { case AccessorKind::Get: - // Synthesized getter for a type wrapped variable is never transparent. - if (auto var = dyn_cast(storage)) { - if (var->isAccessedViaTypeWrapper()) - return false; - } - break; case AccessorKind::Set: @@ -2524,10 +2477,6 @@ IsAccessorTransparentRequest::evaluate(Evaluator &evaluator, PropertyWrapperSynthesizedPropertyKind::Projection)) { break; } - - // Synthesized setter for a type wrapped variable is never transparent. - if (var->isAccessedViaTypeWrapper()) - return false; } if (auto subscript = dyn_cast(storage)) { @@ -3322,9 +3271,6 @@ static void finishStorageImplInfo(AbstractStorageDecl *storage, finishNSManagedImplInfo(var, info); } else if (var->hasAttachedPropertyWrapper()) { finishPropertyWrapperImplInfo(var, info); - } else if (var->isAccessedViaTypeWrapper()) { - info = var->isLet() ? StorageImplInfo::getImmutableComputed() - : StorageImplInfo::getMutableComputed(); } } @@ -3453,12 +3399,6 @@ StorageImplInfoRequest::evaluate(Evaluator &evaluator, readWriteImpl = ReadWriteImplKind::MaterializeToTemporary; } else if (storage->getParsedAccessor(AccessorKind::Get)) { readImpl = ReadImplKind::Get; - } else if (storage->getName() == - storage->getASTContext().Id_TypeWrapperProperty) { - // Type wrapper `$storage` property is `get set` requirement. - readImpl = ReadImplKind::Get; - writeImpl = WriteImplKind::Set; - readWriteImpl = ReadWriteImplKind::Modify; } StorageImplInfo info(readImpl, writeImpl, readWriteImpl); diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 393c3294c3272..a5dfc1a05a09e 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -5220,7 +5220,6 @@ Type CustomAttrTypeRequest::evaluate(Evaluator &eval, CustomAttr *attr, OpenUnboundGenericTypeFn unboundTyOpener = nullptr; // Property delegates allow their type to be an unbound generic. if (typeKind == CustomAttrTypeKind::PropertyWrapper || - typeKind == CustomAttrTypeKind::TypeWrapper || typeKind == CustomAttrTypeKind::RuntimeMetadata) { unboundTyOpener = [](auto unboundTy) { // FIXME: Don't let unbound generic types diff --git a/lib/Sema/TypeCheckTypeWrapper.cpp b/lib/Sema/TypeCheckTypeWrapper.cpp deleted file mode 100644 index 4a9052e69940a..0000000000000 --- a/lib/Sema/TypeCheckTypeWrapper.cpp +++ /dev/null @@ -1,632 +0,0 @@ -//===--- TypeCheckTypeWrapper.cpp - type wrappers -------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -// -// This file implements semantic analysis for type wrappers. -// -//===----------------------------------------------------------------------===// -#include "TypeChecker.h" -#include "swift/AST/ASTContext.h" -#include "swift/AST/Decl.h" -#include "swift/AST/NameLookupRequests.h" -#include "swift/AST/Pattern.h" -#include "swift/AST/Stmt.h" -#include "swift/AST/TypeCheckRequests.h" -#include "swift/AST/TypeWrappers.h" -#include "swift/Basic/LLVM.h" -#include "swift/Basic/SourceLoc.h" - -using namespace swift; - -/// Check whether given declaration comes from the .swiftinterface file. -static bool inSwiftInterfaceContext(NominalTypeDecl *typeDecl) { - auto *SF = typeDecl->getDeclContext()->getParentSourceFile(); - return SF && SF->Kind == SourceFileKind::Interface; -} - -static ValueDecl *findMember(NominalTypeDecl *typeDecl, Identifier memberName) { - auto members = typeDecl->lookupDirect(memberName); - return members.size() == 1 ? members.front() : nullptr; -} - -static PatternBindingDecl *injectVariable(DeclContext *DC, Identifier name, - Type type, - VarDecl::Introducer introducer, - Expr *initializer = nullptr) { - auto &ctx = DC->getASTContext(); - - auto *var = new (ctx) VarDecl(/*isStatic=*/false, introducer, - /*nameLoc=*/SourceLoc(), name, DC); - - var->setImplicit(); - var->setSynthesized(); - var->setInterfaceType(type); - - Pattern *pattern = NamedPattern::createImplicit(ctx, var); - pattern->setType(type); - - pattern = TypedPattern::createImplicit(ctx, pattern, type); - - return PatternBindingDecl::createImplicit(ctx, StaticSpellingKind::None, - pattern, initializer, DC); -} - -/// Create a property declaration and inject it into the given type. -static VarDecl *injectProperty(NominalTypeDecl *parent, Identifier name, - Type type, VarDecl::Introducer introducer, - AccessLevel accessLevel, - Expr *initializer = nullptr) { - auto *PBD = injectVariable(parent, name, type, introducer, initializer); - auto *var = PBD->getSingleVar(); - - var->setAccess(accessLevel); - - parent->addMember(PBD); - parent->addMember(var); - - return var; -} - -bool VarDecl::isAccessedViaTypeWrapper() const { - auto *mutableSelf = const_cast(this); - return evaluateOrDefault(getASTContext().evaluator, - IsPropertyAccessedViaTypeWrapper{mutableSelf}, - false); -} - -VarDecl *VarDecl::getUnderlyingTypeWrapperStorage() const { - auto *mutableSelf = const_cast(this); - return evaluateOrDefault(getASTContext().evaluator, - GetTypeWrapperStorageForProperty{mutableSelf}, - nullptr); -} - -static void -getDeclaredProtocolConformances(NominalTypeDecl *decl, - SmallVectorImpl &protocols) { - for (unsigned i : indices(decl->getInherited())) { - auto inheritedType = evaluateOrDefault( - decl->getASTContext().evaluator, - InheritedTypeRequest{decl, i, TypeResolutionStage::Interface}, Type()); - - if (!(inheritedType && inheritedType->isConstraintType())) - continue; - - if (auto *protocol = - dyn_cast_or_null(inheritedType->getAnyNominal())) { - protocols.push_back(protocol); - } - - if (auto composition = inheritedType->getAs()) { - for (auto member : composition->getMembers()) { - if (auto *protocol = - dyn_cast_or_null(member->getAnyNominal())) { - protocols.push_back(protocol); - } - } - } - } -} - -static void getTypeWrappers(NominalTypeDecl *decl, - SmallVectorImpl &typeWrappers) { - auto &ctx = decl->getASTContext(); - - // Attributes applied directly to the type. - for (auto *attr : decl->getAttrs().getAttributes()) { - auto *mutableAttr = const_cast(attr); - auto *nominal = evaluateOrDefault( - ctx.evaluator, - CustomAttrNominalRequest{mutableAttr, decl->getDeclContext()}, - nullptr); - - if (!nominal) - continue; - - auto *typeWrapper = nominal->getAttrs().getAttribute(); - if (typeWrapper && typeWrapper->isValid()) { - auto attrType = evaluateOrDefault( - ctx.evaluator, - CustomAttrTypeRequest{mutableAttr, decl, - CustomAttrTypeKind::TypeWrapper}, - Type()); - - if (!attrType || attrType->hasError()) - continue; - - typeWrappers.push_back( - {mutableAttr, attrType, nominal, decl, /*isInferred=*/false}); - } - } - - // Do not allow transitive protocol inference between protocols. - if (isa(decl)) - return; - - // Attributes inferred from (explicit) protocol conformances - // associated with the declaration of the type. - SmallVector protocols; - getDeclaredProtocolConformances(decl, protocols); - - for (auto *protocol : protocols) { - SmallVector inferredAttrs; - getTypeWrappers(protocol, inferredAttrs); - - // De-duplicate inferred type wrappers. This also makes sure - // that if both protocol and conforming type explicitly declare - // the same type wrapper there is no clash between them. - for (const auto &inferredAttr : inferredAttrs) { - if (llvm::find_if(typeWrappers, [&](const TypeWrapperInfo &attr) { - return attr.Wrapper == inferredAttr.Wrapper; - }) == typeWrappers.end()) - typeWrappers.push_back(inferredAttr.asInferred()); - } - } -} - -Optional -GetTypeWrapper::evaluate(Evaluator &evaluator, NominalTypeDecl *decl) const { - auto &ctx = decl->getASTContext(); - - // Note that we don't actually care whether there are duplicates, - // using the same type wrapper multiple times is still an error. - SmallVector typeWrappers; - - getTypeWrappers(decl, typeWrappers); - - if (typeWrappers.empty()) - return None; - - if (typeWrappers.size() != 1) { - ctx.Diags.diagnose(decl, diag::cannot_use_multiple_type_wrappers, - decl->getDescriptiveKind(), decl->getName()); - - for (const auto &entry : typeWrappers) { - if (entry.AttachedTo == decl) { - ctx.Diags.diagnose(entry.Attr->getLocation(), diag::decl_declared_here, - entry.Wrapper->getName()); - } else { - ctx.Diags.diagnose(decl, diag::type_wrapper_inferred_from, - entry.Wrapper->getName(), - entry.AttachedTo->getDescriptiveKind(), - entry.AttachedTo->getName()); - } - } - - return None; - } - - return typeWrappers.front(); -} - -VarDecl *NominalTypeDecl::getTypeWrapperProperty() const { - auto *mutableSelf = const_cast(this); - return evaluateOrDefault(getASTContext().evaluator, - GetTypeWrapperProperty{mutableSelf}, nullptr); -} - -ConstructorDecl *NominalTypeDecl::getTypeWrappedTypeStorageInitializer() const { - auto *mutableSelf = const_cast(this); - return evaluateOrDefault( - getASTContext().evaluator, - SynthesizeTypeWrappedTypeStorageWrapperInitializer{mutableSelf}, nullptr); -} - -ConstructorDecl * -NominalTypeDecl::getTypeWrappedTypeMemberwiseInitializer() const { - auto *mutableSelf = const_cast(this); - return evaluateOrDefault( - getASTContext().evaluator, - SynthesizeTypeWrappedTypeMemberwiseInitializer{mutableSelf}, nullptr); -} - -TypeDecl *NominalTypeDecl::getTypeWrapperStorageDecl() const { - auto *mutableSelf = const_cast(this); - return evaluateOrDefault(getASTContext().evaluator, - GetTypeWrapperStorage{mutableSelf}, nullptr); -} - -static AccessLevel -getAccessLevelForTypeWrapperStorage(NominalTypeDecl *attachedTo) { - auto &ctx = attachedTo->getASTContext(); - - if (isa(attachedTo)) - return attachedTo->getFormalAccess(); - - llvm::SmallDenseMap visitedProtocols; - std::function hasPublicStorageAssociatedType = - [&](ProtocolDecl *protocol) { - if (visitedProtocols.count(protocol)) - return visitedProtocols[protocol]; - - auto recordResult = [&](ProtocolDecl *P, bool hasWrapper) { - visitedProtocols[P] = hasWrapper; - return hasWrapper; - }; - - if (auto *storage = - protocol->getAssociatedType(ctx.Id_TypeWrapperStorage)) { - if (storage->getFormalAccess() == AccessLevel::Public) - return recordResult(protocol, true); - } - - // Recursively check whether any of the parents have that - // requirement. - for (auto *parent : protocol->getProtocolDependencies()) { - bool hasPublicStorage = hasPublicStorageAssociatedType(parent); - (void)recordResult(parent, hasPublicStorage); - - if (hasPublicStorage) - return recordResult(protocol, true); - } - - return recordResult(protocol, false); - }; - - SmallVector protocols; - getDeclaredProtocolConformances(attachedTo, protocols); - - for (auto *protocol : protocols) { - if (hasPublicStorageAssociatedType(protocol)) - return AccessLevel::Public; - } - - return AccessLevel::Internal; -} - -TypeDecl *GetTypeWrapperStorage::evaluate(Evaluator &evaluator, - NominalTypeDecl *parent) const { - if (!parent->hasTypeWrapper()) - return nullptr; - - auto &ctx = parent->getASTContext(); - - // .swiftinterfaces have both attribute and a synthesized member - // (if it's public), so in this case we need use existing declaration - // if available. - if (inSwiftInterfaceContext(parent)) { - if (auto *storage = dyn_cast_or_null( - findMember(parent, ctx.Id_TypeWrapperStorage))) - return storage; - } - - TypeDecl *storage = nullptr; - if (isa(parent)) { - // If type wrapper is associated with a protocol, we need to - // inject a new associated type - $Storage. - storage = new (ctx) - AssociatedTypeDecl(parent, /*keywordLoc=*/SourceLoc(), - ctx.Id_TypeWrapperStorage, /*nameLoc=*/SourceLoc(), - /*defaultDefinition=*/nullptr, - /*trailingWhere=*/nullptr); - } else { - // For classes and structs we inject a new member struct - $Storage. - storage = new (ctx) - StructDecl(/*StructLoc=*/SourceLoc(), ctx.Id_TypeWrapperStorage, - /*NameLoc=*/SourceLoc(), - /*Inheritted=*/{}, - /*GenericParams=*/nullptr, parent); - } - - storage->setImplicit(); - storage->setSynthesized(); - storage->setAccess(getAccessLevelForTypeWrapperStorage(parent)); - - parent->addMember(storage); - - return storage; -} - -VarDecl * -GetTypeWrapperProperty::evaluate(Evaluator &evaluator, - NominalTypeDecl *parent) const { - auto &ctx = parent->getASTContext(); - - auto typeWrapper = parent->getTypeWrapper(); - if (!typeWrapper) - return nullptr; - - // .swiftinterfaces have both attribute and a synthesized member - // (if it's public), so in this case we need use existing declaration - // if available. - if (inSwiftInterfaceContext(parent)) { - if (auto *storage = dyn_cast_or_null( - findMember(parent, ctx.Id_TypeWrapperProperty))) - return storage; - } - - auto *storage = parent->getTypeWrapperStorageDecl(); - assert(storage); - - auto *typeWrapperType = typeWrapper->AttrType->castTo(); - - // $storage: Wrapper<, .$Storage> - auto propertyTy = BoundGenericType::get( - typeWrapper->Wrapper, /*Parent=*/typeWrapperType->getParent(), - /*genericArgs=*/ - {parent->getSelfInterfaceType(), storage->getDeclaredInterfaceType()}); - - return injectProperty(parent, ctx.Id_TypeWrapperProperty, propertyTy, - VarDecl::Introducer::Var, - getAccessLevelForTypeWrapperStorage(parent)); -} - -VarDecl *GetTypeWrapperStorageForProperty::evaluate(Evaluator &evaluator, - VarDecl *property) const { - auto *wrappedType = property->getDeclContext()->getSelfNominalTypeDecl(); - if (!(wrappedType && wrappedType->hasTypeWrapper())) - return nullptr; - - // Type wrappers support only stored `var`s. - if (!property->isAccessedViaTypeWrapper()) - return nullptr; - - assert(!isa(wrappedType)); - - auto *storage = - cast(wrappedType->getTypeWrapperStorageDecl()); - assert(storage); - - // Type wrapper variables are never initialized directly, - // initialization expression (if any) becomes an default - // argument of the initializer synthesized by the type wrapper. - if (auto *PBD = property->getParentPatternBinding()) { - PBD->setInitializerSubsumed(/*index=*/0); - } - - return injectProperty(storage, property->getName(), - property->getValueInterfaceType(), - property->getIntroducer(), AccessLevel::Internal); -} - -/// Given the property create a subscript to reach its type wrapper storage: -/// `$storage[storageKeyPath: \$Storage.]`. -static SubscriptExpr *subscriptTypeWrappedProperty(VarDecl *var, - AccessorDecl *useDC) { - auto &ctx = useDC->getASTContext(); - auto *parent = var->getDeclContext()->getSelfNominalTypeDecl(); - - if (!(parent && parent->hasTypeWrapper())) - return nullptr; - - auto *typeWrapperVar = parent->getTypeWrapperProperty(); - auto *storageVar = var->getUnderlyingTypeWrapperStorage(); - - assert(typeWrapperVar); - assert(storageVar); - - auto createRefToSelf = [&]() { - return new (ctx) DeclRefExpr({useDC->getImplicitSelfDecl()}, - /*Loc=*/DeclNameLoc(), /*Implicit=*/true); - }; - - // \$Storage. - auto *storageKeyPath = KeyPathExpr::createImplicit( - ctx, /*backslashLoc=*/SourceLoc(), - {KeyPathExpr::Component::forProperty( - {storageVar}, - useDC->mapTypeIntoContext(storageVar->getInterfaceType()), - /*Loc=*/SourceLoc())}, - /*endLoc=*/SourceLoc()); - - // WrappedType. - auto *propertyKeyPath = KeyPathExpr::createImplicit( - ctx, /*backslashLoc=*/SourceLoc(), - {KeyPathExpr::Component::forUnresolvedProperty( - DeclNameRef(var->getName()), /*Loc=*/SourceLoc())}, - /*endLoc=*/SourceLoc()); - - auto *subscriptBaseExpr = UnresolvedDotExpr::createImplicit( - ctx, createRefToSelf(), typeWrapperVar->getName()); - - SmallVector subscriptArgs; - - // If this is a reference type, let's see whether type wrapper supports - // `wrappedSelf:propertyKeyPath:storageKeyPath:` overload. - if (isa(parent)) { - DeclName subscriptName( - ctx, DeclBaseName::createSubscript(), - {ctx.Id_wrappedSelf, ctx.Id_propertyKeyPath, ctx.Id_storageKeyPath}); - - auto *typeWrapper = parent->getTypeWrapper()->Wrapper; - auto candidates = typeWrapper->lookupDirect(subscriptName); - - if (!candidates.empty()) { - subscriptArgs.push_back( - Argument(/*loc=*/SourceLoc(), ctx.Id_wrappedSelf, createRefToSelf())); - } - } - - subscriptArgs.push_back( - Argument(/*loc=*/SourceLoc(), ctx.Id_propertyKeyPath, propertyKeyPath)); - subscriptArgs.push_back( - Argument(/*loc=*/SourceLoc(), ctx.Id_storageKeyPath, storageKeyPath)); - - // $storage[storageKeyPath: \$Storage.] - return SubscriptExpr::create(ctx, subscriptBaseExpr, - ArgumentList::createImplicit(ctx, subscriptArgs), - ConcreteDeclRef(), /*implicit=*/true); -} - -BraceStmt * -SynthesizeTypeWrappedPropertyGetterBody::evaluate(Evaluator &evaluator, - AccessorDecl *getter) const { - assert(getter->isGetter()); - - auto &ctx = getter->getASTContext(); - - auto *var = dyn_cast(getter->getStorage()); - if (!var) - return nullptr; - - auto *subscript = subscriptTypeWrappedProperty(var, getter); - if (!subscript) - return nullptr; - - ASTNode body = new (ctx) ReturnStmt(SourceLoc(), subscript, - /*isImplicit=*/true); - return BraceStmt::create(ctx, /*lbloc=*/var->getLoc(), body, - /*rbloc=*/var->getLoc(), /*implicit=*/true); -} - -BraceStmt * -SynthesizeTypeWrappedPropertySetterBody::evaluate(Evaluator &evaluator, - AccessorDecl *setter) const { - assert(setter->isSetter()); - - auto &ctx = setter->getASTContext(); - - auto *var = dyn_cast(setter->getStorage()); - if (!var) - return nullptr; - - auto *subscript = subscriptTypeWrappedProperty(var, setter); - if (!subscript) - return nullptr; - - VarDecl *newValueParam = setter->getParameters()->get(0); - - auto *assignment = new (ctx) AssignExpr( - subscript, /*EqualLoc=*/SourceLoc(), - new (ctx) DeclRefExpr(newValueParam, DeclNameLoc(), /*IsImplicit=*/true), - /*Implicit=*/true); - - ASTNode body = new (ctx) ReturnStmt(SourceLoc(), assignment, - /*isImplicit=*/true); - return BraceStmt::create(ctx, /*lbloc=*/var->getLoc(), body, - /*rbloc=*/var->getLoc(), /*implicit=*/true); -} - -bool IsPropertyAccessedViaTypeWrapper::evaluate(Evaluator &evaluator, - VarDecl *property) const { - auto *parent = property->getDeclContext()->getSelfNominalTypeDecl(); - if (!(parent && parent->hasTypeWrapper())) - return false; - - if (property->isStatic()) - return false; - - // If this property has `@typeWrapperIgnored` attribute - // it should not be managed by a type wrapper. - if (property->getAttrs().hasAttribute()) - return false; - - // `lazy` properties are not wrapped. - if (property->getAttrs().hasAttribute() || - property->isLazyStorageProperty()) - return false; - - // Properties with attached property wrappers are not considered - // accessible via type wrapper directly, only their backing storage is. - { - // Wrapped property itself `` - if (property->hasAttachedPropertyWrapper()) - return false; - - // Projection - `$` - if (property->getOriginalWrappedProperty( - PropertyWrapperSynthesizedPropertyKind::Projection)) - return false; - - // Backing storage (or wrapper property) - `_`. - // - // This is the only thing that wrapper needs to handle because - // all access to the wrapped variable and it's projection - // is routed through it. - if (auto *wrappedProperty = property->getOriginalWrappedProperty( - PropertyWrapperSynthesizedPropertyKind::Backing)) { - // If wrapped property is ignored - its backing storage is - // ignored as well. - return !wrappedProperty->getAttrs() - .hasAttribute(); - } - } - - // Don't wrap any compiler synthesized properties except to - // property wrapper backing storage (checked above). - if (property->isImplicit()) - return false; - - // Check whether this is a computed property. - { - auto declaresAccessor = [&](ArrayRef kinds) -> bool { - return llvm::any_of(kinds, [&](const AccessorKind &kind) { - return bool(property->getParsedAccessor(kind)); - }); - }; - - // property has a getter. - if (declaresAccessor( - {AccessorKind::Get, AccessorKind::Read, AccessorKind::Address})) - return false; - - // property has a setter. - if (declaresAccessor({AccessorKind::Set, AccessorKind::Modify, - AccessorKind::MutableAddress})) - return false; - } - - return true; -} - -VarDecl *SynthesizeLocalVariableForTypeWrapperStorage::evaluate( - Evaluator &evaluator, ConstructorDecl *ctor) const { - auto &ctx = ctor->getASTContext(); - - if (ctor->isImplicit() || !ctor->isDesignatedInit()) - return nullptr; - - auto *DC = ctor->getDeclContext()->getSelfNominalTypeDecl(); - if (!(DC && DC->hasTypeWrapper())) - return nullptr; - - // Default protocol initializers do not get transformed. - if (isa(DC)) - return nullptr; - - auto *storageDecl = cast(DC->getTypeWrapperStorageDecl()); - assert(storageDecl); - - SmallVector members; - for (auto *member : storageDecl->getMembers()) { - if (auto *var = dyn_cast(member)) { - assert(var->hasStorage() && - "$Storage should have stored properties only"); - members.push_back({var->getValueInterfaceType(), var->getName()}); - } - } - - auto *PBD = - injectVariable(ctor, ctx.Id_localStorageVar, TupleType::get(members, ctx), - VarDecl::Introducer::Var); - return PBD->getSingleVar(); -} - -ConstructorDecl *NominalTypeDecl::getTypeWrapperInitializer() const { - auto *mutableSelf = const_cast(this); - return evaluateOrDefault(getASTContext().evaluator, - GetTypeWrapperInitializer{mutableSelf}, nullptr); -} - -ConstructorDecl * -GetTypeWrapperInitializer::evaluate(Evaluator &evaluator, - NominalTypeDecl *typeWrapper) const { - auto &ctx = typeWrapper->getASTContext(); - assert(typeWrapper->getAttrs().hasAttribute()); - - auto ctors = typeWrapper->lookupDirect(DeclName( - ctx, DeclBaseName::createConstructor(), {ctx.Id_for, ctx.Id_storage})); - - if (ctors.size() != 1) - return nullptr; - - return cast(ctors.front()); -} diff --git a/test/IDE/complete_decl_attribute.swift b/test/IDE/complete_decl_attribute.swift index 99444a4ee51d4..8d502dfd9c3fd 100644 --- a/test/IDE/complete_decl_attribute.swift +++ b/test/IDE/complete_decl_attribute.swift @@ -118,7 +118,6 @@ actor MyGlobalActor { // KEYWORD3-NEXT: Keyword/None: resultBuilder[#Class Attribute#]; name=resultBuilder // KEYWORD3-NEXT: Keyword/None: globalActor[#Class Attribute#]; name=globalActor // KEYWORD3-NEXT: Keyword/None: preconcurrency[#Class Attribute#]; name=preconcurrency -// KEYWORD3-NEXT: Keyword/None: typeWrapper[#Class Attribute#]; name=typeWrapper // KEYWORD3-NEXT: Keyword/None: runtimeMetadata[#Class Attribute#]; name=runtimeMetadata // KEYWORD3-NEXT: End completions @@ -138,7 +137,6 @@ actor MyGlobalActor { // KEYWORD4-NEXT: Keyword/None: resultBuilder[#Enum Attribute#]; name=resultBuilder // KEYWORD4-NEXT: Keyword/None: globalActor[#Enum Attribute#]; name=globalActor // KEYWORD4-NEXT: Keyword/None: preconcurrency[#Enum Attribute#]; name=preconcurrency -// KEYWORD4-NEXT: Keyword/None: typeWrapper[#Enum Attribute#]; name=typeWrapper // KEYWORD4-NEXT: Keyword/None: runtimeMetadata[#Enum Attribute#]; name=runtimeMetadata // KEYWORD4-NEXT: End completions @@ -154,7 +152,6 @@ actor MyGlobalActor { // KEYWORD5-NEXT: Keyword/None: resultBuilder[#Struct Attribute#]; name=resultBuilder // KEYWORD5-NEXT: Keyword/None: globalActor[#Struct Attribute#]; name=globalActor // KEYWORD5-NEXT: Keyword/None: preconcurrency[#Struct Attribute#]; name=preconcurrency -// KEYWORD5-NEXT: Keyword/None: typeWrapper[#Struct Attribute#]; name=typeWrapper // KEYWORD5-NEXT: Keyword/None: runtimeMetadata[#Struct Attribute#]; name=runtimeMetadata // KEYWORD5-NEXT: End completions @@ -175,7 +172,7 @@ actor MyGlobalActor { // ON_GLOBALVAR-DAG: Keyword/None: noDerivative[#Var Attribute#]; name=noDerivative // ON_GLOBALVAR-DAG: Keyword/None: exclusivity[#Var Attribute#]; name=exclusivity // ON_GLOBALVAR-DAG: Keyword/None: preconcurrency[#Var Attribute#]; name=preconcurrency -// ON_GLOBALVAR-DAG: Keyword/None: typeWrapperIgnored[#Var Attribute#]; name=typeWrapperIgnored +// ON_GLOBALVAR-DAG: Keyword/None: backDeployed[#Var Attribute#]; name=backDeployed // ON_GLOBALVAR-NOT: Keyword // ON_GLOBALVAR-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_GLOBALVAR-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: MyPropertyWrapper[#MyPropertyWrapper#]; name=MyPropertyWrapper @@ -213,7 +210,7 @@ struct _S { // ON_PROPERTY-DAG: Keyword/None: noDerivative[#Var Attribute#]; name=noDerivative // ON_PROPERTY-DAG: Keyword/None: exclusivity[#Var Attribute#]; name=exclusivity // ON_PROPERTY-DAG: Keyword/None: preconcurrency[#Var Attribute#]; name=preconcurrency -// ON_PROPERTY-DAG: Keyword/None: typeWrapperIgnored[#Var Attribute#]; name=typeWrapperIgnored +// ON_PROPERTY-DAG: Keyword/None: backDeployed[#Var Attribute#]; name=backDeployed // ON_PROPERTY-NOT: Keyword // ON_PROPERTY-DAG: Decl[Struct]/CurrModule: MyStruct[#MyStruct#]; name=MyStruct // ON_PROPERTY-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: MyPropertyWrapper[#MyPropertyWrapper#]; name=MyPropertyWrapper @@ -314,8 +311,6 @@ struct _S { // ON_MEMBER_LAST-DAG: Keyword/None: Sendable[#Declaration Attribute#]; name=Sendable // ON_MEMBER_LAST-DAG: Keyword/None: exclusivity[#Declaration Attribute#]; name=exclusivity // ON_MEMBER_LAST-DAG: Keyword/None: preconcurrency[#Declaration Attribute#]; name=preconcurrency -// ON_MEMBER_LAST-DAG: Keyword/None: typeWrapper[#Declaration Attribute#]; name=typeWrapper -// ON_MEMBER_LAST-DAG: Keyword/None: typeWrapperIgnored[#Declaration Attribute#]; name=typeWrapperIgnored // ON_MEMBER_LAST-DAG: Keyword/None: runtimeMetadata[#Declaration Attribute#]; name=runtimeMetadata // ON_MEMBER_LAST-DAG: Keyword/None: attached[#Declaration Attribute#]; name=attached // ON_MEMBER_LAST-NOT: Keyword @@ -387,8 +382,6 @@ func dummy2() {} // KEYWORD_LAST-DAG: Keyword/None: Sendable[#Declaration Attribute#]; name=Sendable // KEYWORD_LAST-DAG: Keyword/None: exclusivity[#Declaration Attribute#]; name=exclusivity // KEYWORD_LAST-DAG: Keyword/None: preconcurrency[#Declaration Attribute#]; name=preconcurrency -// KEYWORD_LAST-DAG: Keyword/None: typeWrapper[#Declaration Attribute#]; name=typeWrapper -// KEYWORD_LAST-DAG: Keyword/None: typeWrapperIgnored[#Declaration Attribute#]; name=typeWrapperIgnored // KEYWORD_LAST-DAG: Keyword/None: runtimeMetadata[#Declaration Attribute#]; name=runtimeMetadata // KEYWORD_LAST-DAG: Keyword/None: attached[#Declaration Attribute#]; name=attached // KEYWORD_LAST-NOT: Keyword diff --git a/test/Serialization/AllowErrors/invalid-inheritance.swift b/test/Serialization/AllowErrors/invalid-inheritance.swift index 8a6506022ae8d..b97f307002616 100644 --- a/test/Serialization/AllowErrors/invalid-inheritance.swift +++ b/test/Serialization/AllowErrors/invalid-inheritance.swift @@ -32,8 +32,8 @@ extension undefined: SomeProto {} // expected-error {{cannot find type 'undefine public extension undefined { // expected-error {{cannot find type 'undefined' in scope}} protocol SomeProtoInner: undefined {} // expected-error {{cannot find type 'undefined' in scope}} - class SomeClassInner: undefined {} // expected-error {{cannot find type 'undefined' in scope}} - struct SomeStructInner: undefined {} // expected-error {{cannot find type 'undefined' in scope}} + class SomeClassInner: undefined {} + struct SomeStructInner: undefined {} enum SomeEnumInner: undefined { // expected-error {{cannot find type 'undefined' in scope}} case a } From d2e022d5b4e5484959d194e976a542adbbe717e0 Mon Sep 17 00:00:00 2001 From: Anton Korobeynikov Date: Wed, 8 Feb 2023 07:42:54 -0800 Subject: [PATCH 31/98] Remove linear map structs and use plain tuples instead. (#63444) The changes are intentionally were made close to the original implementation w/o possible simplifications to ease the review Fixes #63207, supersedes #63379 (and fixes #63234) --- .../Differentiation/LinearMapInfo.h | 85 ++---- lib/SIL/IR/SILPrinter.cpp | 31 ++ lib/SIL/Parser/ParseSIL.cpp | 4 +- .../Differentiation/JVPCloner.cpp | 93 +++--- .../Differentiation/LinearMapInfo.cpp | 286 +++++++----------- .../Differentiation/PullbackCloner.cpp | 106 +++---- .../Differentiation/VJPCloner.cpp | 113 +++---- .../SILOptimizer/derivative_sil.swift | 30 +- .../differentiation_control_flow_sil.swift | 144 +++++---- .../semantic_member_accessors_sil.swift | 4 +- ...8660-conflicting-debug-info-inlining.swift | 2 +- .../validation-test/simple_math.swift | 2 +- 12 files changed, 416 insertions(+), 484 deletions(-) diff --git a/include/swift/SILOptimizer/Differentiation/LinearMapInfo.h b/include/swift/SILOptimizer/Differentiation/LinearMapInfo.h index 7831a761c3269..d3cd760d9043b 100644 --- a/include/swift/SILOptimizer/Differentiation/LinearMapInfo.h +++ b/include/swift/SILOptimizer/Differentiation/LinearMapInfo.h @@ -69,8 +69,8 @@ class LinearMapInfo { /// Differentiation indices of the function. const AutoDiffConfig config; - /// Mapping from original basic blocks to linear map structs. - llvm::DenseMap linearMapStructs; + /// Mapping from original basic blocks to linear map tuple types. + llvm::DenseMap linearMapTuples; /// Mapping from original basic blocks to branching trace enums. /// For pullbacks: these are predecessor enums. @@ -78,17 +78,14 @@ class LinearMapInfo { llvm::DenseMap branchingTraceDecls; /// Mapping from `apply` instructions in the original function to the - /// corresponding linear map field declaration in the linear map struct. - llvm::DenseMap linearMapFieldMap; + /// corresponding linear map tuple type index. + llvm::DenseMap linearMapIndexMap; /// Mapping from predecessor-successor basic block pairs in the original /// function to the corresponding branching trace enum case. llvm::DenseMap, EnumElementDecl *> branchingTraceEnumCases; - /// Mapping from linear map structs to their branching trace enum fields. - llvm::DenseMap linearMapStructEnumFields; - /// Blocks in a loop. llvm::SmallSetVector blocksInLoop; @@ -102,37 +99,21 @@ class LinearMapInfo { /// Remaps the given type into the derivative function's context. SILType remapTypeInDerivative(SILType ty); - /// Adds a `VarDecl` member with the given name and type to the given nominal - /// declaration. - VarDecl *addVarDecl(NominalTypeDecl *nominal, StringRef name, Type type); - /// Retrieves the file unit that contains implicit declarations in the /// current Swift module. SynthesizedFileUnit &getSynthesizedFile() { return synthesizedFile; } - /// Computes and sets the access level for the given nominal type, given the - /// original function linkage. - void computeAccessLevel(NominalTypeDecl *nominal, SILLinkage originalLinkage); - /// Creates an enum declaration with the given JVP/VJP generic signature, /// whose cases represent the predecessors/successors of the given original /// block. EnumDecl *createBranchingTraceDecl(SILBasicBlock *originalBB, - CanGenericSignature genericSig, - SILLoopInfo *loopInfo); + CanGenericSignature genericSig); + void populateBranchingTraceDecl(SILBasicBlock *originalBB, + SILLoopInfo *loopInfo); - /// Creates a struct declaration with the given JVP/VJP generic signature, for - /// storing the linear map values and predecessor/successor basic block of the - /// given original block. - StructDecl *createLinearMapStruct(SILBasicBlock *originalBB, - CanGenericSignature genericSig); - - /// Adds a linear map field to the linear map struct. - VarDecl *addLinearMapDecl(ApplyInst *ai, SILType linearMapType); - - /// Given an `apply` instruction, conditionally adds a linear map struct field - /// for its linear map function if it is active. - void addLinearMapToStruct(ADContext &context, ApplyInst *ai); + /// Given an `apply` instruction, conditionally gets a linear map tuple field + /// AST type for its linear map function if it is active. + Type getLinearMapType(ADContext &context, ApplyInst *ai); /// Generates linear map struct and branching enum declarations for the given /// function. Linear map structs are populated with linear map fields and a @@ -153,22 +134,20 @@ class LinearMapInfo { const DifferentiableActivityInfo &activityInfo, SILLoopInfo *loopInfo); - /// Returns the linear map struct associated with the given original block. - StructDecl *getLinearMapStruct(SILBasicBlock *origBB) const { - return linearMapStructs.lookup(origBB); + /// Returns the linear map tuple associated with the given original block. + TupleType *getLinearMapTupleType(SILBasicBlock *origBB) const { + return linearMapTuples.lookup(origBB); } - /// Returns the lowered SIL type of the linear map struct associated with the + /// Returns the lowered SIL type of the linear map tuple associated with the /// given original block. - SILType getLinearMapStructLoweredType(SILBasicBlock *origBB) const { + SILType getLinearMapTupleLoweredType(SILBasicBlock *origBB) const { auto derivativeGenSig = derivative->getLoweredFunctionType()->getSubstGenericSignature(); - auto *linMapStruct = getLinearMapStruct(origBB); - auto linMapStructType = - linMapStruct->getDeclaredInterfaceType()->getReducedType( - derivativeGenSig); - Lowering::AbstractionPattern pattern(derivativeGenSig, linMapStructType); - return typeConverter.getLoweredType(pattern, linMapStructType, + auto linMapTupleType = + getLinearMapTupleType(origBB)->getReducedType(derivativeGenSig); + Lowering::AbstractionPattern pattern(derivativeGenSig, linMapTupleType); + return typeConverter.getLoweredType(pattern, linMapTupleType, TypeExpansionContext::minimal()); } @@ -199,29 +178,21 @@ class LinearMapInfo { return branchingTraceEnumCases.lookup({origPredBB, origSuccBB}); } - /// Returns the mapping from linear map structs to their branching trace enum - /// fields. - llvm::DenseMap &getLinearMapStructEnumFields() { - return linearMapStructEnumFields; - } - - /// Returns the branching trace enum field for the linear map struct of the - /// given original block. - VarDecl *lookUpLinearMapStructEnumField(SILBasicBlock *origBB) const { - auto *linearMapStruct = getLinearMapStruct(origBB); - return linearMapStructEnumFields.lookup(linearMapStruct); - } - - /// Finds the linear map declaration in the pullback struct for the given + /// Finds the linear map index in the pullback tuple for the given /// `apply` instruction in the original function. - VarDecl *lookUpLinearMapDecl(ApplyInst *ai) const { + unsigned lookUpLinearMapIndex(ApplyInst *ai) const { assert(ai->getFunction() == original); - auto lookup = linearMapFieldMap.find(ai); - assert(lookup != linearMapFieldMap.end() && + auto lookup = linearMapIndexMap.find(ai); + assert(lookup != linearMapIndexMap.end() && "No linear map field corresponding to the given `apply`"); return lookup->getSecond(); } + Type lookUpLinearMapType(ApplyInst *ai) const { + unsigned idx = lookUpLinearMapIndex(ai); + return getLinearMapTupleType(ai->getParentBlock())->getElement(idx).getType(); + } + bool hasLoops() const { return !blocksInLoop.empty(); } diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 7da70173ea009..dd83c82b1e9c6 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -3449,6 +3449,36 @@ static void printSILDifferentiabilityWitnesses( dw->print(Ctx.OS(), Ctx.printVerbose()); } +static void printSILLinearMapTypes(SILPrintContext &Ctx, + const ModuleDecl *M) { + auto &OS = Ctx.OS(); + + PrintOptions Options = PrintOptions::printSIL(); + Options.TypeDefinitions = true; + Options.VarInitializers = true; + Options.ExplodePatternBindingDecls = true; + Options.SkipImplicit = false; + Options.PrintGetSetOnRWProperties = true; + Options.PrintInSILBody = false; + + SmallVector topLevelDecls; + M->getTopLevelDecls(topLevelDecls); + for (const Decl *D : topLevelDecls) { + if (D->getDeclContext() == M) + continue; + + if (!isa(D) && !isa(D)) + continue; + + StringRef Name = cast(D)->getNameStr(); + if (!Name.startswith("_AD__")) + continue; + + D->print(OS, Options); + OS << "\n\n"; + } +} + static void printSILCoverageMaps(SILPrintContext &Ctx, const SILModule::CoverageMapCollectionType &CoverageMaps) { @@ -3624,6 +3654,7 @@ void SILModule::print(SILPrintContext &PrintCtx, ModuleDecl *M, printSILGlobals(PrintCtx, getSILGlobalList()); printSILDifferentiabilityWitnesses(PrintCtx, getDifferentiabilityWitnessList()); + printSILLinearMapTypes(PrintCtx, getSwiftModule()); printSILFunctions(PrintCtx, getFunctionList()); printSILVTables(PrintCtx, getVTables()); printSILWitnessTables(PrintCtx, getWitnessTableList()); diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index b94367d5aee9b..7cb8bbce81705 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -2416,7 +2416,9 @@ static bool parseSILDifferentiabilityWitnessConfigAndFunction( auto origFnType = resultOrigFn->getLoweredFunctionType(); auto *parameterIndices = IndexSubset::get( P.Context, origFnType->getNumParameters(), rawParameterIndices); - auto *resultIndices = IndexSubset::get(P.Context, origFnType->getNumResults(), + auto *resultIndices = IndexSubset::get(P.Context, + origFnType->getNumResults() + + origFnType->getNumIndirectMutatingParameters(), rawResultIndices); resultConfig = AutoDiffConfig(parameterIndices, resultIndices, witnessGenSig); return false; diff --git a/lib/SILOptimizer/Differentiation/JVPCloner.cpp b/lib/SILOptimizer/Differentiation/JVPCloner.cpp index 41b26a826e3af..1e819c1642d97 100644 --- a/lib/SILOptimizer/Differentiation/JVPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/JVPCloner.cpp @@ -97,7 +97,7 @@ class JVPCloner::Implementation final /// elements destructured from the linear map basic block argument. In the /// beginning of each differential basic block, the block's differential /// struct is destructured into the individual elements stored here. - llvm::DenseMap differentialStructElements; + llvm::DenseMap differentialTupleElements; /// An auxiliary differential local allocation builder. TangentBuilder diffLocalAllocBuilder; @@ -119,23 +119,17 @@ class JVPCloner::Implementation final TangentBuilder &getDifferentialBuilder() { return differentialBuilder; } SILFunction &getDifferential() { return differentialBuilder.getFunction(); } SILArgument *getDifferentialStructArgument(SILBasicBlock *origBB) { -#ifndef NDEBUG - auto *diffStruct = differentialStructArguments[origBB] - ->getType() - .getStructOrBoundGenericStruct(); - assert(diffStruct == differentialInfo.getLinearMapStruct(origBB)); -#endif return differentialStructArguments[origBB]; } //--------------------------------------------------------------------------// - // Differential struct mapping + // Differential tuple mapping //--------------------------------------------------------------------------// - void initializeDifferentialStructElements(SILBasicBlock *origBB, - SILInstructionResultArray values); + void initializeDifferentialTupleElements(SILBasicBlock *origBB, + SILInstructionResultArray values); - SILValue getDifferentialStructElement(SILBasicBlock *origBB, VarDecl *field); + SILValue getDifferentialTupleElement(ApplyInst *ai); //--------------------------------------------------------------------------// // General utilities @@ -158,22 +152,21 @@ class JVPCloner::Implementation final /// Build a differential struct value for the original block corresponding to /// the given terminator. - StructInst *buildDifferentialValueStructValue(TermInst *termInst) { + TupleInst *buildDifferentialValueStructValue(TermInst *termInst) { assert(termInst->getFunction() == original); auto loc = termInst->getFunction()->getLocation(); auto *origBB = termInst->getParent(); auto *jvpBB = BBMap[origBB]; assert(jvpBB && "Basic block mapping should exist"); - auto *diffStruct = differentialInfo.getLinearMapStruct(origBB); - assert(diffStruct && "The differential struct should have been declared"); - auto structLoweredTy = getNominalDeclLoweredType(diffStruct); + auto tupleLoweredTy = + remapType(differentialInfo.getLinearMapTupleLoweredType(origBB)); auto bbDifferentialValues = differentialValues[origBB]; if (!origBB->isEntry()) { auto *enumArg = jvpBB->getArguments().back(); bbDifferentialValues.insert(bbDifferentialValues.begin(), enumArg); } - return getBuilder().createStruct(loc, structLoweredTy, - bbDifferentialValues); + return getBuilder().createTuple(loc, tupleLoweredTy, + bbDifferentialValues); } //--------------------------------------------------------------------------// @@ -438,8 +431,8 @@ class JVPCloner::Implementation final auto *mainDifferentialStruct = diffBB->getArguments().back(); diffBuilder.setInsertionPoint(diffBB); auto *dsi = - diffBuilder.createDestructureStruct(diffLoc, mainDifferentialStruct); - initializeDifferentialStructElements(bb, dsi->getResults()); + diffBuilder.createDestructureTuple(diffLoc, mainDifferentialStruct); + initializeDifferentialTupleElements(bb, dsi->getResults()); TypeSubstCloner::visitInstructionsInBlock(bb); } @@ -667,12 +660,11 @@ class JVPCloner::Implementation final // Add the differential function for when we create the struct we partially // apply to the differential we are generating. auto differential = jvpDirectResults.back(); - auto *differentialDecl = differentialInfo.lookUpLinearMapDecl(ai); + auto differentialType = differentialInfo.lookUpLinearMapType(ai); auto originalDifferentialType = getOpType(differential->getType()).getAs(); auto loweredDifferentialType = - getOpType(getLoweredType(differentialDecl->getInterfaceType())) - .castTo(); + getOpType(getLoweredType(differentialType)).castTo(); // If actual differential type does not match lowered differential type, // reabstract the differential using a thunk. if (!loweredDifferentialType->isEqual(originalDifferentialType)) { @@ -1218,9 +1210,7 @@ class JVPCloner::Implementation final auto &diffBuilder = getDifferentialBuilder(); // Get the differential value. - auto *field = differentialInfo.lookUpLinearMapDecl(ai); - assert(field); - SILValue differential = getDifferentialStructElement(bb, field); + SILValue differential = getDifferentialTupleElement(ai); auto differentialType = remapSILTypeInDifferential(differential->getType()) .castTo(); @@ -1432,31 +1422,27 @@ JVPCloner::~JVPCloner() { delete &impl; } // Differential struct mapping //--------------------------------------------------------------------------// -void JVPCloner::Implementation::initializeDifferentialStructElements( - SILBasicBlock *origBB, SILInstructionResultArray values) { - auto *diffStructDecl = differentialInfo.getLinearMapStruct(origBB); - assert(diffStructDecl->getStoredProperties().size() == values.size() && - "The number of differential struct fields must equal the number of " +void JVPCloner::Implementation::initializeDifferentialTupleElements( + SILBasicBlock *origBB, SILInstructionResultArray values) { + auto *diffTupleTyple = differentialInfo.getLinearMapTupleType(origBB); + assert(diffTupleTyple->getNumElements() == values.size() && + "The number of differential tuple fields must equal the number of " "differential struct element values"); - for (auto pair : llvm::zip(diffStructDecl->getStoredProperties(), values)) { - assert(std::get<1>(pair)->getOwnershipKind() != OwnershipKind::Guaranteed && - "Differential struct elements must be @owned"); - auto insertion = differentialStructElements.insert( - {std::get<0>(pair), std::get<1>(pair)}); - (void)insertion; - assert(insertion.second && - "A differential struct element mapping already exists!"); - } + auto res = differentialTupleElements.insert({origBB, values}); + (void)res; + assert(res.second && "A pullback struct element already exists!"); } -SILValue -JVPCloner::Implementation::getDifferentialStructElement(SILBasicBlock *origBB, - VarDecl *field) { - assert(differentialInfo.getLinearMapStruct(origBB) == - cast(field->getDeclContext())); - assert(differentialStructElements.count(field) && - "Differential struct element for this field does not exist!"); - return differentialStructElements.lookup(field); +/// Returns the differential tuple element value corresponding to the given +/// original block and apply inst. +SILValue JVPCloner::Implementation::getDifferentialTupleElement(ApplyInst *ai) { + unsigned idx = differentialInfo.lookUpLinearMapIndex(ai); + assert((idx > 0 || (idx == 0 && ai->getParentBlock()->isEntry())) && + "impossible linear map index"); + auto values = differentialTupleElements.lookup(ai->getParentBlock()); + assert(idx < values.size() && + "differential tuple element for this apply does not exist!"); + return values[idx]; } //--------------------------------------------------------------------------// @@ -1481,9 +1467,9 @@ void JVPCloner::Implementation::prepareForDifferentialGeneration() { createEntryArguments(&differential); auto *lastArg = diffBB->getArguments().back(); #ifndef NDEBUG - auto diffStructLoweredType = remapSILTypeInDifferential( - differentialInfo.getLinearMapStructLoweredType(&origBB)); - assert(lastArg->getType() == diffStructLoweredType); + auto diffTupleLoweredType = remapSILTypeInDifferential( + differentialInfo.getLinearMapTupleLoweredType(&origBB)); + assert(lastArg->getType() == diffTupleLoweredType); #endif differentialStructArguments[&origBB] = lastArg; } @@ -1671,10 +1657,9 @@ void JVPCloner::Implementation::prepareForDifferentialGeneration() { // Accept a differential struct in the differential parameter list. This is // the returned differential's closure context. auto *origEntry = original->getEntryBlock(); - auto *dfStruct = linearMapInfo->getLinearMapStruct(origEntry); - auto dfStructType = - dfStruct->getDeclaredInterfaceType()->getReducedType(witnessCanGenSig); - dfParams.push_back({dfStructType, ParameterConvention::Direct_Owned}); + auto dfTupleType = + linearMapInfo->getLinearMapTupleLoweredType(origEntry).getASTType(); + dfParams.push_back({dfTupleType, ParameterConvention::Direct_Owned}); Mangle::DifferentiationMangler mangler; auto diffName = mangler.mangleLinearMap( diff --git a/lib/SILOptimizer/Differentiation/LinearMapInfo.cpp b/lib/SILOptimizer/Differentiation/LinearMapInfo.cpp index 7867dd9a9cb38..62845a498675a 100644 --- a/lib/SILOptimizer/Differentiation/LinearMapInfo.cpp +++ b/lib/SILOptimizer/Differentiation/LinearMapInfo.cpp @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// // -// Linear map struct and branching trace enum information for differentiation. +// Linear map tuple and branching trace enum information for differentiation. // //===----------------------------------------------------------------------===// @@ -69,56 +69,11 @@ SILType LinearMapInfo::remapTypeInDerivative(SILType ty) { return derivative->mapTypeIntoContext(ty); } -VarDecl *LinearMapInfo::addVarDecl(NominalTypeDecl *nominal, StringRef name, - Type type) { - auto &astCtx = nominal->getASTContext(); - auto id = astCtx.getIdentifier(name); - auto *varDecl = new (astCtx) VarDecl( - /*IsStatic*/ false, VarDecl::Introducer::Var, - SourceLoc(), id, nominal); - varDecl->setAccess(AccessLevel::Private); - if (type->hasArchetype()) - varDecl->setInterfaceType(type->mapTypeOutOfContext()); - else - varDecl->setInterfaceType(type); - nominal->addMember(varDecl); - return varDecl; -} - -void LinearMapInfo::computeAccessLevel(NominalTypeDecl *nominal, - SILLinkage originalLinkage) { - auto &astCtx = nominal->getASTContext(); - switch (originalLinkage) { - case swift::SILLinkage::Public: - case swift::SILLinkage::PublicNonABI: - nominal->setAccess(AccessLevel::Internal); - nominal->getAttrs().add(new (astCtx) - UsableFromInlineAttr(/*Implicit*/ true)); - break; - case swift::SILLinkage::Hidden: - case swift::SILLinkage::Shared: - nominal->setAccess(AccessLevel::Internal); - break; - case swift::SILLinkage::Private: - nominal->setAccess(AccessLevel::FilePrivate); - break; - default: - // When the original function has external linkage, we create an internal - // struct for use by our own module. This is necessary for cross-cell - // differentiation in Jupyter. - // TODO: Add a test in the compiler that exercises a similar situation as - // cross-cell differentiation in Jupyter. - nominal->setAccess(AccessLevel::Internal); - } -} - EnumDecl * LinearMapInfo::createBranchingTraceDecl(SILBasicBlock *originalBB, - CanGenericSignature genericSig, - SILLoopInfo *loopInfo) { + CanGenericSignature genericSig) { assert(originalBB->getParent() == original); auto &astCtx = original->getASTContext(); - auto *moduleDecl = original->getModule().getSwiftModule(); auto &file = getSynthesizedFile(); // Create a branching trace enum. Mangle::ASTMangler mangler; @@ -137,31 +92,58 @@ LinearMapInfo::createBranchingTraceDecl(SILBasicBlock *originalBB, // Note: must mark enum as implicit to satisfy assertion in // `Parser::parseDeclListDelayed`. branchingTraceDecl->setImplicit(); - // Branching trace enums shall not be resilient. - branchingTraceDecl->getAttrs().add(new (astCtx) FrozenAttr(/*implicit*/ true)); if (genericSig) branchingTraceDecl->setGenericSignature(genericSig); - computeAccessLevel(branchingTraceDecl, original->getEffectiveSymbolLinkage()); + switch (original->getEffectiveSymbolLinkage()) { + case swift::SILLinkage::Public: + case swift::SILLinkage::PublicNonABI: + // Branching trace enums shall not be resilient. + branchingTraceDecl->getAttrs().add(new (astCtx) FrozenAttr(/*implicit*/ true)); + branchingTraceDecl->getAttrs().add(new (astCtx) UsableFromInlineAttr(/*Implicit*/ true)); + LLVM_FALLTHROUGH; + case swift::SILLinkage::Hidden: + case swift::SILLinkage::Shared: + branchingTraceDecl->setAccess(AccessLevel::Internal); + break; + case swift::SILLinkage::Private: + branchingTraceDecl->setAccess(AccessLevel::FilePrivate); + break; + default: + // When the original function has external linkage, we create an internal + // struct for use by our own module. This is necessary for cross-cell + // differentiation in Jupyter. + // TODO: Add a test in the compiler that exercises a similar situation as + // cross-cell differentiation in Jupyter. + branchingTraceDecl->setAccess(AccessLevel::Internal); + } file.addTopLevelDecl(branchingTraceDecl); + + return branchingTraceDecl; +} + +void LinearMapInfo::populateBranchingTraceDecl(SILBasicBlock *originalBB, + SILLoopInfo *loopInfo) { + auto &astCtx = original->getASTContext(); + auto *moduleDecl = original->getModule().getSwiftModule(); + auto loc = original->getLocation().getSourceLoc(); + auto *branchingTraceDecl = getBranchingTraceDecl(originalBB); + // Add basic block enum cases. for (auto *predBB : originalBB->getPredecessorBlocks()) { // Create dummy declaration representing enum case parameter. auto *decl = new (astCtx) ParamDecl(loc, loc, Identifier(), loc, Identifier(), moduleDecl); decl->setSpecifier(ParamDecl::Specifier::Default); - // If predecessor block is in a loop, its linear map struct will be + // If predecessor block is in a loop, its linear map tuple will be // indirectly referenced in memory owned by the context object. The payload // is just a raw pointer. if (loopInfo->getLoopFor(predBB)) { blocksInLoop.insert(predBB); decl->setInterfaceType(astCtx.TheRawPointerType); } - // Otherwise the payload is the linear map struct. + // Otherwise the payload is the linear map tuple. else { - auto *linearMapStruct = getLinearMapStruct(predBB); - assert(linearMapStruct); - auto linearMapStructTy = - linearMapStruct->getDeclaredInterfaceType()->getCanonicalType(); + auto linearMapStructTy = getLinearMapTupleType(predBB)->getCanonicalType(); decl->setInterfaceType( linearMapStructTy->hasArchetype() ? linearMapStructTy->mapTypeOutOfContext() : linearMapStructTy); @@ -181,85 +163,10 @@ LinearMapInfo::createBranchingTraceDecl(SILBasicBlock *originalBB, // Record enum element declaration. branchingTraceEnumCases.insert({{predBB, originalBB}, enumEltDecl}); } - return branchingTraceDecl; } -StructDecl * -LinearMapInfo::createLinearMapStruct(SILBasicBlock *originalBB, - CanGenericSignature genericSig) { - assert(originalBB->getParent() == original); - auto *original = originalBB->getParent(); - auto &astCtx = original->getASTContext(); - auto &file = getSynthesizedFile(); - // Create a linear map struct. - Mangle::ASTMangler mangler; - auto config = this->config.withGenericSignature(genericSig); - auto structName = mangler.mangleAutoDiffGeneratedDeclaration( - AutoDiffGeneratedDeclarationKind::LinearMapStruct, - original->getName().str(), originalBB->getDebugID(), kind, config); - auto structId = astCtx.getIdentifier(structName); - GenericParamList *genericParams = nullptr; - if (genericSig) - genericParams = cloneGenericParameters(astCtx, &file, genericSig); - auto *linearMapStruct = new (astCtx) StructDecl( - /*StructLoc*/ SourceLoc(), /*Name*/ structId, /*NameLoc*/ SourceLoc(), - /*Inherited*/ {}, /*GenericParams*/ genericParams, /*DC*/ &file); - // Note: must mark struct as implicit to satisfy assertion in - // `Parser::parseDeclListDelayed`. - linearMapStruct->setImplicit(); - // Linear map structs shall not be resilient. - linearMapStruct->getAttrs().add(new (astCtx) FrozenAttr(/*implicit*/ true)); - if (genericSig) - linearMapStruct->setGenericSignature(genericSig); - computeAccessLevel(linearMapStruct, original->getEffectiveSymbolLinkage()); - file.addTopLevelDecl(linearMapStruct); - return linearMapStruct; -} - -VarDecl *LinearMapInfo::addLinearMapDecl(ApplyInst *ai, SILType linearMapType) { - // IRGen requires decls to have AST types (not `SILFunctionType`), so we - // convert the `SILFunctionType` of the linear map to a `FunctionType` with - // the same parameters and results. - auto silFnTy = linearMapType.castTo(); - SmallVector params; - for (auto ¶m : silFnTy->getParameters()) { - ParameterTypeFlags flags; - if (param.isIndirectMutating()) - flags = flags.withInOut(true); - params.push_back( - AnyFunctionType::Param(param.getInterfaceType(), Identifier(), flags)); - } - - AnyFunctionType *astFnTy; - if (auto genSig = silFnTy->getSubstGenericSignature()) { - // FIXME: Verify ExtInfo state is correct, not working by accident. - GenericFunctionType::ExtInfo info; - astFnTy = GenericFunctionType::get( - genSig, params, silFnTy->getAllResultsInterfaceType().getASTType(), - info); - } else { - FunctionType::ExtInfo info; - astFnTy = FunctionType::get( - params, silFnTy->getAllResultsInterfaceType().getASTType(), info); - } - - auto *origBB = ai->getParent(); - auto *linMapStruct = getLinearMapStruct(origBB); - std::string linearMapName; - switch (kind) { - case AutoDiffLinearMapKind::Differential: - linearMapName = "differential_" + llvm::itostr(linearMapFieldMap.size()); - break; - case AutoDiffLinearMapKind::Pullback: - linearMapName = "pullback_" + llvm::itostr(linearMapFieldMap.size()); - break; - } - auto *linearMapDecl = addVarDecl(linMapStruct, linearMapName, astFnTy); - linearMapFieldMap.insert({ai, linearMapDecl}); - return linearMapDecl; -} -void LinearMapInfo::addLinearMapToStruct(ADContext &context, ApplyInst *ai) { +Type LinearMapInfo::getLinearMapType(ADContext &context, ApplyInst *ai) { SmallVector allResults; SmallVector activeParamIndices; SmallVector activeResultIndices; @@ -285,9 +192,9 @@ void LinearMapInfo::addLinearMapToStruct(ADContext &context, ApplyInst *ai) { } } if (!hasActiveArguments) - return; + return {}; if (!hasActiveResults && !hasActiveInoutArgument) - return; + return {}; // Compute differentiability parameters. // - If the callee has `@differentiable` function type, use differentiation @@ -342,7 +249,7 @@ void LinearMapInfo::addLinearMapToStruct(ADContext &context, ApplyInst *ai) { return false; }; if (checkNondifferentiableOriginalFunctionType(remappedOrigFnSubstTy)) - return; + return nullptr; AutoDiffDerivativeFunctionKind derivativeFnKind(kind); auto derivativeFnType = @@ -363,7 +270,37 @@ void LinearMapInfo::addLinearMapToStruct(ADContext &context, ApplyInst *ai) { linearMapSILType = SILType::getPrimitiveObjectType( fnTy->getUnsubstitutedType(original->getModule())); } - addLinearMapDecl(ai, linearMapSILType); + + // IRGen requires decls to have AST types (not `SILFunctionType`), so we + // convert the `SILFunctionType` of the linear map to a `FunctionType` with + // the same parameters and results. + auto silFnTy = linearMapSILType.castTo(); + SmallVector params; + for (auto ¶m : silFnTy->getParameters()) { + ParameterTypeFlags flags; + if (param.isIndirectMutating()) + flags = flags.withInOut(true); + params.push_back( + AnyFunctionType::Param(param.getInterfaceType(), Identifier(), flags)); + } + + AnyFunctionType *astFnTy; + if (auto genSig = silFnTy->getSubstGenericSignature()) { + // FIXME: Verify ExtInfo state is correct, not working by accident. + GenericFunctionType::ExtInfo info; + astFnTy = GenericFunctionType::get( + genSig, params, silFnTy->getAllResultsInterfaceType().getASTType(), + info); + } else { + FunctionType::ExtInfo info; + astFnTy = FunctionType::get( + params, silFnTy->getAllResultsInterfaceType().getASTType(), info); + } + + if (astFnTy->hasArchetype()) + return astFnTy->mapTypeOutOfContext(); + + return astFnTy; } void LinearMapInfo::generateDifferentiationDataStructures( @@ -375,12 +312,6 @@ void LinearMapInfo::generateDifferentiationDataStructures( derivativeFnGenSig = derivativeFnGenEnv->getGenericSignature().getCanonicalSignature(); - // Create linear map struct for each original block. - for (auto &origBB : *original) { - auto *linearMapStruct = createLinearMapStruct(&origBB, derivativeFnGenSig); - linearMapStructs.insert({&origBB, linearMapStruct}); - } - // Create branching trace enum for each original block and add it as a field // in the corresponding struct. StringRef traceEnumFieldName; @@ -392,44 +323,52 @@ void LinearMapInfo::generateDifferentiationDataStructures( traceEnumFieldName = "predecessor"; break; } + for (auto &origBB : *original) { auto *traceEnum = - createBranchingTraceDecl(&origBB, derivativeFnGenSig, loopInfo); + createBranchingTraceDecl(&origBB, derivativeFnGenSig); branchingTraceDecls.insert({&origBB, traceEnum}); - if (origBB.isEntry()) - continue; - // Add branching trace enum field to corresponding linear map struct. - auto *linearMapStruct = getLinearMapStruct(&origBB); - auto *traceEnumField = addVarDecl( - linearMapStruct, astCtx.getIdentifier(traceEnumFieldName).str(), - traceEnum->getDeclaredInterfaceType()); - linearMapStructEnumFields.insert({linearMapStruct, traceEnumField}); } - // Do not add linear map fields for semantic member accessors, which have - // special-case pullback generation. Linear map structs should be empty. - if (isSemanticMemberAccessor(original)) - return; - - // Add linear map fields to the linear map structs. + // Add linear map fields to the linear map tuples. for (auto &origBB : *original) { - for (auto &inst : origBB) { - if (auto *ai = dyn_cast(&inst)) { - // Add linear map field to struct for active `apply` instructions. - // Skip array literal intrinsic applications since array literal - // initialization is linear and handled separately. - if (!shouldDifferentiateApplySite(ai) || - ArraySemanticsCall(ai, semantics::ARRAY_UNINITIALIZED_INTRINSIC)) - continue; - if (ArraySemanticsCall(ai, semantics::ARRAY_FINALIZE_INTRINSIC)) - continue; - LLVM_DEBUG(getADDebugStream() - << "Adding linear map struct field for " << *ai); - addLinearMapToStruct(context, ai); + SmallVector linearTupleTypes; + if (!origBB.isEntry()) { + CanType traceEnumType = getBranchingTraceEnumLoweredType(&origBB).getASTType(); + linearTupleTypes.emplace_back(traceEnumType, + astCtx.getIdentifier(traceEnumFieldName)); + } + + if (isSemanticMemberAccessor(original)) { + // Do not add linear map fields for semantic member accessors, which have + // special-case pullback generation. Linear map tuples should be empty. + } else { + for (auto &inst : origBB) { + if (auto *ai = dyn_cast(&inst)) { + // Add linear map field to struct for active `apply` instructions. + // Skip array literal intrinsic applications since array literal + // initialization is linear and handled separately. + if (!shouldDifferentiateApplySite(ai) || + ArraySemanticsCall(ai, semantics::ARRAY_UNINITIALIZED_INTRINSIC)) + continue; + if (ArraySemanticsCall(ai, semantics::ARRAY_FINALIZE_INTRINSIC)) + continue; + LLVM_DEBUG(getADDebugStream() + << "Adding linear map tuple field for " << *ai); + if (Type linearMapType = getLinearMapType(context, ai)) { + linearMapIndexMap.insert({ai, linearTupleTypes.size()}); + linearTupleTypes.emplace_back(linearMapType); + } + } } } + + linearMapTuples.insert({&origBB, TupleType::get(linearTupleTypes, astCtx)}); } + for (auto &origBB : *original) + populateBranchingTraceDecl(&origBB, loopInfo); + // Print generated linear map structs and branching trace enums. // These declarations do not show up with `-emit-sil` because they are // implicit. Instead, use `-Xllvm -debug-only=differentiation` to test @@ -440,13 +379,14 @@ void LinearMapInfo::generateDifferentiationDataStructures( printOptions.TypeDefinitions = true; printOptions.ExplodePatternBindingDecls = true; printOptions.SkipImplicit = false; - s << "Generated linear map structs and branching trace enums for @" + s << "Generated linear map tuples and branching trace enums for @" << original->getName() << ":\n"; for (auto &origBB : *original) { - auto *linearMapStruct = getLinearMapStruct(&origBB); - linearMapStruct->print(s, printOptions); + auto *linearMapTuple = getLinearMapTupleType(&origBB); + linearMapTuple->print(s, printOptions); s << '\n'; } + for (auto &origBB : *original) { auto *traceEnum = getBranchingTraceDecl(&origBB); traceEnum->print(s, printOptions); diff --git a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp index 0d1a7f7438cbf..37604f1420150 100644 --- a/lib/SILOptimizer/Differentiation/PullbackCloner.cpp +++ b/lib/SILOptimizer/Differentiation/PullbackCloner.cpp @@ -90,7 +90,7 @@ class PullbackCloner::Implementation final /// elements destructured from the linear map basic block argument. In the /// beginning of each pullback basic block, the block's pullback struct is /// destructured into individual elements stored here. - llvm::DenseMap pullbackStructElements; + llvm::DenseMap pullbackTupleElements; /// Mapping from original basic blocks and successor basic blocks to /// corresponding pullback trampoline basic blocks. Trampoline basic blocks @@ -158,31 +158,36 @@ class PullbackCloner::Implementation final // Pullback struct mapping //--------------------------------------------------------------------------// - void initializePullbackStructElements(SILBasicBlock *origBB, - SILInstructionResultArray values) { - auto *pbStructDecl = getPullbackInfo().getLinearMapStruct(origBB); - assert(pbStructDecl->getStoredProperties().size() == values.size() && - "The number of pullback struct fields must equal the number of " + void initializePullbackTupleElements(SILBasicBlock *origBB, + SILInstructionResultArray values) { + auto *pbTupleTyple = getPullbackInfo().getLinearMapTupleType(origBB); + assert(pbTupleTyple->getNumElements() == values.size() && + "The number of pullback tuple fields must equal the number of " "pullback struct element values"); - for (auto pair : llvm::zip(pbStructDecl->getStoredProperties(), values)) { - assert(std::get<1>(pair)->getOwnershipKind() != - OwnershipKind::Guaranteed && - "Pullback struct elements must be @owned"); - auto insertion = - pullbackStructElements.insert({std::get<0>(pair), std::get<1>(pair)}); - (void)insertion; - assert(insertion.second && "A pullback struct element already exists!"); - } + auto res = pullbackTupleElements.insert({origBB, values}); + (void)res; + assert(res.second && "A pullback struct element already exists!"); + } + + /// Returns the pullback tuple element value corresponding to the given + /// original block and apply inst. + SILValue getPullbackTupleElement(ApplyInst *ai) { + unsigned idx = getPullbackInfo().lookUpLinearMapIndex(ai); + assert((idx > 0 || (idx == 0 && ai->getParentBlock()->isEntry())) && + "impossible linear map index"); + auto values = pullbackTupleElements.lookup(ai->getParentBlock()); + assert(idx < values.size() && + "pullback tuple element for this apply does not exist!"); + return values[idx]; } - /// Returns the pullback struct element value corresponding to the given - /// original block and pullback struct field. - SILValue getPullbackStructElement(SILBasicBlock *origBB, VarDecl *field) { - assert(getPullbackInfo().getLinearMapStruct(origBB) == - cast(field->getDeclContext())); - assert(pullbackStructElements.count(field) && - "Pullback struct element for this field does not exist!"); - return pullbackStructElements.lookup(field); + /// Returns the pullback tuple element value corresponding to the predecessor + /// for the given original block. + SILValue getPullbackPredTupleElement(SILBasicBlock *origBB) { + assert(!origBB->isEntry() && "no predecessors for entry block"); + auto values = pullbackTupleElements.lookup(origBB); + assert(values.size() && "pullback tuple cannot be empty"); + return values[0]; } //--------------------------------------------------------------------------// @@ -878,11 +883,6 @@ class PullbackCloner::Implementation final } auto applyInfo = applyInfoLookup->getSecond(); - // Get the pullback. - auto *field = getPullbackInfo().lookUpLinearMapDecl(ai); - assert(field); - auto pullback = getPullbackStructElement(ai->getParent(), field); - // Get the original result of the `apply` instruction. SmallVector origDirectResults; forEachApplyDirectResult(ai, [&](SILValue directResult) { @@ -905,8 +905,10 @@ class PullbackCloner::Implementation final // Handle callee pullback indirect results. // Create local allocations for these and destroy them after the call. + auto pullback = getPullbackTupleElement(ai); auto pullbackType = remapType(pullback->getType()).castTo(); + auto actualPullbackType = applyInfo.originalPullbackType ? *applyInfo.originalPullbackType : pullbackType; @@ -1902,8 +1904,8 @@ bool PullbackCloner::Implementation::run() { for (auto *origBB : originalBlocks) { auto *pullbackBB = pullback.createBasicBlock(); pullbackBBMap.insert({origBB, pullbackBB}); - auto pbStructLoweredType = - remapType(getPullbackInfo().getLinearMapStructLoweredType(origBB)); + auto pbTupleLoweredType = + remapType(getPullbackInfo().getLinearMapTupleLoweredType(origBB)); // If the BB is the original exit, then the pullback block that we just // created must be the pullback function's entry. For the pullback entry, // create entry arguments and continue to the next block. @@ -1915,7 +1917,7 @@ bool PullbackCloner::Implementation::run() { builder.setInsertionPoint(pullbackBB); // Obtain the context object, if any, and the top-level subcontext, i.e. // the main pullback struct. - SILValue mainPullbackStruct; + SILValue mainPullbackTuple; if (getPullbackInfo().hasLoops()) { // The last argument is the context object (`Builtin.NativeObject`). contextValue = pullbackBB->getArguments().back(); @@ -1923,19 +1925,19 @@ bool PullbackCloner::Implementation::run() { SILType::getNativeObjectType(getASTContext())); // Load the pullback struct. auto subcontextAddr = emitProjectTopLevelSubcontext( - builder, pbLoc, contextValue, pbStructLoweredType); - mainPullbackStruct = builder.createLoad( + builder, pbLoc, contextValue, pbTupleLoweredType); + mainPullbackTuple = builder.createLoad( pbLoc, subcontextAddr, - pbStructLoweredType.isTrivial(getPullback()) ? + pbTupleLoweredType.isTrivial(getPullback()) ? LoadOwnershipQualifier::Trivial : LoadOwnershipQualifier::Take); } else { // Obtain and destructure pullback struct elements. - mainPullbackStruct = pullbackBB->getArguments().back(); - assert(mainPullbackStruct->getType() == pbStructLoweredType); + mainPullbackTuple = pullbackBB->getArguments().back(); + assert(mainPullbackTuple->getType() == pbTupleLoweredType); } - auto *dsi = builder.createDestructureStruct(pbLoc, mainPullbackStruct); - initializePullbackStructElements(origBB, dsi->getResults()); + auto *dsi = builder.createDestructureTuple(pbLoc, mainPullbackTuple); + initializePullbackTupleElements(origBB, dsi->getResults()); continue; } // Get all active values in the original block. @@ -1972,15 +1974,15 @@ bool PullbackCloner::Implementation::run() { } } } - // Add a pullback struct argument. - auto *pbStructArg = pullbackBB->createPhiArgument(pbStructLoweredType, - OwnershipKind::Owned); + // Add a pullback tuple argument. + auto *pbTupleArg = pullbackBB->createPhiArgument(pbTupleLoweredType, + OwnershipKind::Owned); // Destructure the pullback struct to get the elements. builder.setCurrentDebugScope( remapScope(origBB->getTerminator()->getDebugScope())); builder.setInsertionPoint(pullbackBB); - auto *dsi = builder.createDestructureStruct(pbLoc, pbStructArg); - initializePullbackStructElements(origBB, dsi->getResults()); + auto *dsi = builder.createDestructureTuple(pbLoc, pbTupleArg); + initializePullbackTupleElements(origBB, dsi->getResults()); // - Create pullback trampoline blocks for each successor block of the // original block. Pullback trampoline blocks only have a pullback @@ -2420,14 +2422,14 @@ SILBasicBlock *PullbackCloner::Implementation::buildPullbackSuccessor( if (vjpCloner.getLoopInfo()->getLoopFor(origPredBB)) { assert(pullbackTrampolineBBArg->getType() == SILType::getRawPointerType(getASTContext())); - auto pbStructType = - remapType(getPullbackInfo().getLinearMapStructLoweredType(origPredBB)); - auto predPbStructAddr = pullbackTrampolineBBBuilder.createPointerToAddress( - loc, pullbackTrampolineBBArg, pbStructType.getAddressType(), + auto pbTupleType = + remapType(getPullbackInfo().getLinearMapTupleLoweredType(origPredBB)); + auto predPbTupleAddr = pullbackTrampolineBBBuilder.createPointerToAddress( + loc, pullbackTrampolineBBArg, pbTupleType.getAddressType(), /*isStrict*/ true); auto predPbStructVal = pullbackTrampolineBBBuilder.createLoad( - loc, predPbStructAddr, - pbStructType.isTrivial(getPullback()) ? + loc, predPbTupleAddr, + pbTupleType.isTrivial(getPullback()) ? LoadOwnershipQualifier::Trivial : LoadOwnershipQualifier::Take); trampolineArguments.push_back(predPbStructVal); } else { @@ -2481,8 +2483,7 @@ void PullbackCloner::Implementation::visitSILBasicBlock(SILBasicBlock *bb) { // 2. Extract the predecessor enum value from the pullback struct value. auto *predEnum = getPullbackInfo().getBranchingTraceDecl(bb); (void)predEnum; - auto *predEnumField = getPullbackInfo().lookUpLinearMapStructEnumField(bb); - auto predEnumVal = getPullbackStructElement(bb, predEnumField); + auto predEnumVal = getPullbackPredTupleElement(bb); // Propagate adjoint values from active basic block arguments to // incoming values (predecessor terminator operands). @@ -2597,7 +2598,8 @@ void PullbackCloner::Implementation::visitSILBasicBlock(SILBasicBlock *bb) { // Branch to pullback successor blocks. assert(pullbackSuccessorCases.size() == predEnum->getNumElements()); builder.createSwitchEnum(pbLoc, predEnumVal, /*DefaultBB*/ nullptr, - pullbackSuccessorCases); + pullbackSuccessorCases, None, ProfileCounter(), + OwnershipKind::Owned); } //--------------------------------------------------------------------------// diff --git a/lib/SILOptimizer/Differentiation/VJPCloner.cpp b/lib/SILOptimizer/Differentiation/VJPCloner.cpp index 1587a7d775e63..04051ea372546 100644 --- a/lib/SILOptimizer/Differentiation/VJPCloner.cpp +++ b/lib/SILOptimizer/Differentiation/VJPCloner.cpp @@ -114,11 +114,11 @@ class VJPCloner::Implementation final return; // Get linear map struct size. auto *returnBB = &*original->findReturnBB(); - auto pullbackStructType = - remapType(pullbackInfo.getLinearMapStructLoweredType(returnBB)); + auto pullbackTupleType = + remapASTType(pullbackInfo.getLinearMapTupleType(returnBB)->getCanonicalType()); Builder.setInsertionPoint(vjp->getEntryBlock()); auto topLevelSubcontextSize = emitMemoryLayoutSize( - Builder, original->getLocation(), pullbackStructType.getASTType()); + Builder, original->getLocation(), pullbackTupleType); // Create an context. pullbackContextValue = Builder.createBuiltin( original->getLocation(), @@ -130,7 +130,8 @@ class VJPCloner::Implementation final original->getLocation(), pullbackContextValue); LLVM_DEBUG(getADDebugStream() << "Context object initialized because there are loops\n" - << *vjp->getEntryBlock() << '\n'); + << *vjp->getEntryBlock() << '\n' + << "pullback tuple type: " << pullbackTupleType << '\n'); } /// Get the lowered SIL type of the given AST type. @@ -164,19 +165,19 @@ class VJPCloner::Implementation final // requirements on successor block arguments, where an additional predecessor // enum argument is not acceptable. SILBasicBlock *createTrampolineBasicBlock(TermInst *termInst, - StructInst *pbStructVal, + TupleInst *pbTupleVal, SILBasicBlock *succBB); - /// Build a pullback struct value for the given original terminator + /// Build a pullback tuple value for the given original terminator /// instruction. - StructInst *buildPullbackValueStructValue(TermInst *termInst); + TupleInst *buildPullbackValueTupleValue(TermInst *termInst); /// Build a predecessor enum instance using the given builder for the given /// original predecessor/successor blocks and pullback struct value. EnumInst *buildPredecessorEnumValue(SILBuilder &builder, SILBasicBlock *predBB, SILBasicBlock *succBB, - SILValue pbStructVal); + SILValue pbTupleVal); public: /// Remap original basic blocks, adding predecessor enum arguments. @@ -222,7 +223,7 @@ class VJPCloner::Implementation final auto loc = ri->getOperand().getLoc(); // Build pullback struct value for original block. auto *origExit = ri->getParent(); - auto *pbStructVal = buildPullbackValueStructValue(ri); + auto *pbTupleVal = buildPullbackValueTupleValue(ri); // Get the value in the VJP corresponding to the original result. auto *origRetInst = cast(origExit->getTerminator()); @@ -238,17 +239,17 @@ class VJPCloner::Implementation final SILValue partialApplyArg; if (borrowedPullbackContextValue) { // Initialize the top-level subcontext buffer with the top-level pullback - // struct. + // tuple. auto addr = emitProjectTopLevelSubcontext( - Builder, loc, borrowedPullbackContextValue, pbStructVal->getType()); + Builder, loc, borrowedPullbackContextValue, pbTupleVal->getType()); Builder.createStore( - loc, pbStructVal, addr, - pbStructVal->getType().isTrivial(*pullback) ? + loc, pbTupleVal, addr, + pbTupleVal->getType().isTrivial(*pullback) ? StoreOwnershipQualifier::Trivial : StoreOwnershipQualifier::Init); partialApplyArg = pullbackContextValue; Builder.createEndBorrow(loc, borrowedPullbackContextValue); } else { - partialApplyArg = pbStructVal; + partialApplyArg = pbTupleVal; } auto *pullbackPartialApply = Builder.createPartialApply( @@ -290,9 +291,9 @@ class VJPCloner::Implementation final // Build pullback struct value for original block. // Build predecessor enum value for destination block. auto *origBB = bi->getParent(); - auto *pbStructVal = buildPullbackValueStructValue(bi); + auto *pbTupleVal = buildPullbackValueTupleValue(bi); auto *enumVal = buildPredecessorEnumValue(getBuilder(), origBB, - bi->getDestBB(), pbStructVal); + bi->getDestBB(), pbTupleVal); // Remap arguments, appending the new enum values. SmallVector args; @@ -308,31 +309,31 @@ class VJPCloner::Implementation final void visitCondBranchInst(CondBranchInst *cbi) { Builder.setCurrentDebugScope(getOpScope(cbi->getDebugScope())); // Build pullback struct value for original block. - auto *pbStructVal = buildPullbackValueStructValue(cbi); + auto *pbTupleVal = buildPullbackValueTupleValue(cbi); // Create a new `cond_br` instruction. getBuilder().createCondBranch( cbi->getLoc(), getOpValue(cbi->getCondition()), - createTrampolineBasicBlock(cbi, pbStructVal, cbi->getTrueBB()), - createTrampolineBasicBlock(cbi, pbStructVal, cbi->getFalseBB())); + createTrampolineBasicBlock(cbi, pbTupleVal, cbi->getTrueBB()), + createTrampolineBasicBlock(cbi, pbTupleVal, cbi->getFalseBB())); } void visitSwitchEnumTermInst(SwitchEnumTermInst inst) { Builder.setCurrentDebugScope(getOpScope(inst->getDebugScope())); - // Build pullback struct value for original block. - auto *pbStructVal = buildPullbackValueStructValue(*inst); + // Build pullback tuple value for original block. + auto *pbTupleVal = buildPullbackValueTupleValue(*inst); // Create trampoline successor basic blocks. SmallVector, 4> caseBBs; for (unsigned i : range(inst.getNumCases())) { auto caseBB = inst.getCase(i); auto *trampolineBB = - createTrampolineBasicBlock(inst, pbStructVal, caseBB.second); + createTrampolineBasicBlock(inst, pbTupleVal, caseBB.second); caseBBs.push_back({caseBB.first, trampolineBB}); } // Create trampoline default basic block. SILBasicBlock *newDefaultBB = nullptr; if (auto *defaultBB = inst.getDefaultBBOrNull().getPtrOrNull()) - newDefaultBB = createTrampolineBasicBlock(inst, pbStructVal, defaultBB); + newDefaultBB = createTrampolineBasicBlock(inst, pbTupleVal, defaultBB); // Create a new `switch_enum` instruction. switch (inst->getKind()) { @@ -360,29 +361,29 @@ class VJPCloner::Implementation final void visitCheckedCastBranchInst(CheckedCastBranchInst *ccbi) { Builder.setCurrentDebugScope(getOpScope(ccbi->getDebugScope())); // Build pullback struct value for original block. - auto *pbStructVal = buildPullbackValueStructValue(ccbi); + auto *pbTupleVal = buildPullbackValueTupleValue(ccbi); // Create a new `checked_cast_branch` instruction. getBuilder().createCheckedCastBranch( ccbi->getLoc(), ccbi->isExact(), getOpValue(ccbi->getOperand()), getOpType(ccbi->getTargetLoweredType()), getOpASTType(ccbi->getTargetFormalType()), - createTrampolineBasicBlock(ccbi, pbStructVal, ccbi->getSuccessBB()), - createTrampolineBasicBlock(ccbi, pbStructVal, ccbi->getFailureBB()), + createTrampolineBasicBlock(ccbi, pbTupleVal, ccbi->getSuccessBB()), + createTrampolineBasicBlock(ccbi, pbTupleVal, ccbi->getFailureBB()), ccbi->getTrueBBCount(), ccbi->getFalseBBCount()); } void visitCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *ccabi) { Builder.setCurrentDebugScope(getOpScope(ccabi->getDebugScope())); // Build pullback struct value for original block. - auto *pbStructVal = buildPullbackValueStructValue(ccabi); + auto *pbTupleVal = buildPullbackValueTupleValue(ccabi); // Create a new `checked_cast_addr_branch` instruction. getBuilder().createCheckedCastAddrBranch( ccabi->getLoc(), ccabi->getConsumptionKind(), getOpValue(ccabi->getSrc()), getOpASTType(ccabi->getSourceFormalType()), getOpValue(ccabi->getDest()), getOpASTType(ccabi->getTargetFormalType()), - createTrampolineBasicBlock(ccabi, pbStructVal, ccabi->getSuccessBB()), - createTrampolineBasicBlock(ccabi, pbStructVal, ccabi->getFailureBB()), + createTrampolineBasicBlock(ccabi, pbTupleVal, ccabi->getSuccessBB()), + createTrampolineBasicBlock(ccabi, pbTupleVal, ccabi->getFailureBB()), ccabi->getTrueBBCount(), ccabi->getFalseBBCount()); } @@ -654,15 +655,14 @@ class VJPCloner::Implementation final mapValue(ai, originalDirectResult); // Checkpoint the pullback. - auto *pullbackDecl = pullbackInfo.lookUpLinearMapDecl(ai); + auto pullbackType = pullbackInfo.lookUpLinearMapType(ai); // If actual pullback type does not match lowered pullback type, reabstract // the pullback using a thunk. auto actualPullbackType = getOpType(pullback->getType()).getAs(); auto loweredPullbackType = - getOpType(getLoweredType(pullbackDecl->getInterfaceType())) - .castTo(); + getOpType(getLoweredType(pullbackType)).castTo(); if (!loweredPullbackType->isEqual(actualPullbackType)) { // Set non-reabstracted original pullback type in nested apply info. nestedApplyInfo.originalPullbackType = actualPullbackType; @@ -688,14 +688,14 @@ class VJPCloner::Implementation final void visitTryApplyInst(TryApplyInst *tai) { Builder.setCurrentDebugScope(getOpScope(tai->getDebugScope())); // Build pullback struct value for original block. - auto *pbStructVal = buildPullbackValueStructValue(tai); + auto *pbTupleVal = buildPullbackValueTupleValue(tai); // Create a new `try_apply` instruction. auto args = getOpValueArray<8>(tai->getArguments()); getBuilder().createTryApply( tai->getLoc(), getOpValue(tai->getCallee()), getOpSubstitutionMap(tai->getSubstitutionMap()), args, - createTrampolineBasicBlock(tai, pbStructVal, tai->getNormalBB()), - createTrampolineBasicBlock(tai, pbStructVal, tai->getErrorBB()), + createTrampolineBasicBlock(tai, pbTupleVal, tai->getNormalBB()), + createTrampolineBasicBlock(tai, pbTupleVal, tai->getErrorBB()), tai->getApplyOptions()); } @@ -942,10 +942,9 @@ SILFunction *VJPCloner::Implementation::createEmptyPullback() { // Accept a pullback struct in the pullback parameter list. This is the // returned pullback's closure context. auto *origExit = &*original->findReturnBB(); - auto *pbStruct = pullbackInfo.getLinearMapStruct(origExit); - auto pbStructType = - pbStruct->getDeclaredInterfaceType()->getReducedType(witnessCanGenSig); - pbParams.push_back({pbStructType, ParameterConvention::Direct_Owned}); + auto pbTupleType = + pullbackInfo.getLinearMapTupleLoweredType(origExit).getASTType(); + pbParams.emplace_back(pbTupleType, ParameterConvention::Direct_Owned); } // Add pullback results for the requested wrt parameters. @@ -991,7 +990,7 @@ SILFunction *VJPCloner::Implementation::createEmptyPullback() { } SILBasicBlock *VJPCloner::Implementation::createTrampolineBasicBlock( - TermInst *termInst, StructInst *pbStructVal, SILBasicBlock *succBB) { + TermInst *termInst, TupleInst *pbTupleVal, SILBasicBlock *succBB) { assert(llvm::find(termInst->getSuccessorBlocks(), succBB) != termInst->getSuccessorBlocks().end() && "Basic block is not a successor of terminator instruction"); @@ -1006,7 +1005,7 @@ SILBasicBlock *VJPCloner::Implementation::createTrampolineBasicBlock( trampolineBuilder.setCurrentDebugScope(getOpScope(termInst->getDebugScope())); auto *origBB = termInst->getParent(); auto *succEnumVal = - buildPredecessorEnumValue(trampolineBuilder, origBB, succBB, pbStructVal); + buildPredecessorEnumValue(trampolineBuilder, origBB, succBB, pbTupleVal); SmallVector forwardedArguments( trampolineBB->getArguments().begin(), trampolineBB->getArguments().end()); forwardedArguments.push_back(succEnumVal); @@ -1015,25 +1014,25 @@ SILBasicBlock *VJPCloner::Implementation::createTrampolineBasicBlock( return trampolineBB; } -StructInst * -VJPCloner::Implementation::buildPullbackValueStructValue(TermInst *termInst) { +TupleInst * +VJPCloner::Implementation::buildPullbackValueTupleValue(TermInst *termInst) { assert(termInst->getFunction() == original); auto loc = RegularLocation::getAutoGeneratedLocation(); auto origBB = termInst->getParent(); auto *vjpBB = BBMap[origBB]; - auto structLoweredTy = - remapType(pullbackInfo.getLinearMapStructLoweredType(origBB)); + auto tupleLoweredTy = + remapType(pullbackInfo.getLinearMapTupleLoweredType(origBB)); auto bbPullbackValues = pullbackValues[origBB]; if (!origBB->isEntry()) { auto *predEnumArg = vjpBB->getArguments().back(); bbPullbackValues.insert(bbPullbackValues.begin(), predEnumArg); } - return getBuilder().createStruct(loc, structLoweredTy, bbPullbackValues); + return getBuilder().createTuple(loc, tupleLoweredTy, bbPullbackValues); } EnumInst *VJPCloner::Implementation::buildPredecessorEnumValue( SILBuilder &builder, SILBasicBlock *predBB, SILBasicBlock *succBB, - SILValue pbStructVal) { + SILValue pbTupleVal) { auto loc = RegularLocation::getAutoGeneratedLocation(); auto enumLoweredTy = remapType(pullbackInfo.getBranchingTraceEnumLoweredType(succBB)); @@ -1046,25 +1045,27 @@ EnumInst *VJPCloner::Implementation::buildPredecessorEnumValue( if (loopInfo->getLoopFor(predBB)) { auto rawPtrType = SILType::getRawPointerType(getASTContext()); assert(enumEltType == rawPtrType); - auto pbStructType = pbStructVal->getType(); - SILValue pbStructSize = - emitMemoryLayoutSize(Builder, loc, pbStructType.getASTType()); + auto pbTupleType = + remapASTType(pullbackInfo.getLinearMapTupleType(predBB)->getCanonicalType()); + SILValue pbTupleSize = + emitMemoryLayoutSize(Builder, loc, pbTupleType); auto rawBufferValue = builder.createBuiltin( loc, getASTContext().getIdentifier( getBuiltinName(BuiltinValueKind::AutoDiffAllocateSubcontext)), rawPtrType, SubstitutionMap(), - {borrowedPullbackContextValue, pbStructSize}); - auto typedBufferValue = builder.createPointerToAddress( - loc, rawBufferValue, pbStructType.getAddressType(), + {borrowedPullbackContextValue, pbTupleSize}); + auto typedBufferValue = + builder.createPointerToAddress( + loc, rawBufferValue, pbTupleVal->getType().getAddressType(), /*isStrict*/ true); builder.createStore( - loc, pbStructVal, typedBufferValue, - pbStructType.isTrivial(*pullback) ? + loc, pbTupleVal, typedBufferValue, + pbTupleVal->getType().isTrivial(*pullback) ? StoreOwnershipQualifier::Trivial : StoreOwnershipQualifier::Init); return builder.createEnum(loc, rawBufferValue, enumEltDecl, enumLoweredTy); } - return builder.createEnum(loc, pbStructVal, enumEltDecl, enumLoweredTy); + return builder.createEnum(loc, pbTupleVal, enumEltDecl, enumLoweredTy); } bool VJPCloner::Implementation::run() { diff --git a/test/AutoDiff/SILOptimizer/derivative_sil.swift b/test/AutoDiff/SILOptimizer/derivative_sil.swift index db5d19b572939..bbb1b19cd68d5 100644 --- a/test/AutoDiff/SILOptimizer/derivative_sil.swift +++ b/test/AutoDiff/SILOptimizer/derivative_sil.swift @@ -26,6 +26,12 @@ func foo(_ x: Float) -> Float { return y } +// CHECK-SIL-LABEL: enum _AD__foo_bb0__Pred__src_0_wrt_0 { +// CHECK-SIL-NEXT: } + +// CHECK-SIL-LABEL: enum _AD__fooMethod_bb0__Pred__src_0_wrt_0 { +// CHECK-SIL-NEXT: } + // CHECK-SIL-LABEL: sil hidden [ossa] @fooTJfSpSr : $@convention(thin) (Float) -> (Float, @owned @callee_guaranteed (Float) -> Float) { // CHECK-SIL: bb0([[X:%.*]] : $Float): // CHECK-SIL: [[ADD_ORIG_REF:%.*]] = function_ref @add : $@convention(method) (Float, Float, @thin Float.Type) -> Float @@ -35,16 +41,16 @@ func foo(_ x: Float) -> Float { // CHECK-SIL: [[ADD_JVP_FN:%.*]] = differentiable_function_extract [jvp] [[ADD_DIFF_FN]] // CHECK-SIL: [[ADD_RESULT:%.*]] = apply [[ADD_JVP_FN]]([[X]], [[X]], {{.*}}) // CHECK-SIL: ([[ORIG_RES:%.*]], [[ADD_DF:%.*]]) = destructure_tuple [[ADD_RESULT]] -// CHECK-SIL: [[DF_STRUCT:%.*]] = struct $_AD__foo_bb0__DF__src_0_wrt_0 ([[ADD_DF]] : $@callee_guaranteed (Float, Float) -> Float) -// CHECK-SIL: [[DF_REF:%.*]] = function_ref @fooTJdSpSr : $@convention(thin) (Float, @owned _AD__foo_bb0__DF__src_0_wrt_0) -> Float +// CHECK-SIL: [[DF_STRUCT:%.*]] = tuple ([[ADD_DF]] : $@callee_guaranteed (Float, Float) -> Float) +// CHECK-SIL: [[DF_REF:%.*]] = function_ref @fooTJdSpSr : $@convention(thin) (Float, @owned (_: @callee_guaranteed (Float, Float) -> Float)) -> Float // CHECK-SIL: [[DF_FN:%.*]] = partial_apply [callee_guaranteed] [[DF_REF]]([[DF_STRUCT]]) // CHECK-SIL: [[VJP_RESULT:%.*]] = tuple ([[ORIG_RES]] : $Float, [[DF_FN]] : $@callee_guaranteed (Float) -> Float) // CHECK-SIL: return [[VJP_RESULT]] : $(Float, @callee_guaranteed (Float) -> Float) // CHECK-SIL: } -// CHECK-SIL-LABEL: sil private [ossa] @fooTJdSpSr : $@convention(thin) (Float, @owned _AD__foo_bb0__DF__src_0_wrt_0) -> Float { -// CHECK-SIL: bb0([[DX:%.*]] : $Float, [[DF_STRUCT:%.*]] : @owned $_AD__foo_bb0__DF__src_0_wrt_0): -// CHECK-SIL: [[ADD_DF:%.*]] = destructure_struct [[DF_STRUCT]] : $_AD__foo_bb0__DF__src_0_wrt_0 +// CHECK-SIL-LABEL: sil private [ossa] @fooTJdSpSr : $@convention(thin) (Float, @owned (_: @callee_guaranteed (Float, Float) -> Float)) -> Float { +// CHECK-SIL: bb0([[DX:%.*]] : $Float, [[DF_STRUCT:%.*]] : @owned $(_: @callee_guaranteed (Float, Float) -> Float)): +// CHECK-SIL: [[ADD_DF:%.*]] = destructure_tuple [[DF_STRUCT]] : $(_: @callee_guaranteed (Float, Float) -> Float) // CHECK-SIL: [[DY:%.*]] = apply [[ADD_DF]]([[DX]], [[DX]]) : $@callee_guaranteed (Float, Float) -> Float // CHECK-SIL: destroy_value [[ADD_DF]] : $@callee_guaranteed (Float, Float) -> Float // CHECK-SIL: return [[DY]] : $Float @@ -59,16 +65,16 @@ func foo(_ x: Float) -> Float { // CHECK-SIL: [[ADD_VJP_FN:%.*]] = differentiable_function_extract [vjp] [[ADD_DIFF_FN]] // CHECK-SIL: [[ADD_RESULT:%.*]] = apply [[ADD_VJP_FN]]([[X]], [[X]], {{.*}}) // CHECK-SIL: ([[ORIG_RES:%.*]], [[ADD_PB:%.*]]) = destructure_tuple [[ADD_RESULT]] -// CHECK-SIL: [[PB_STRUCT:%.*]] = struct $_AD__foo_bb0__PB__src_0_wrt_0 ([[ADD_PB]] : $@callee_guaranteed (Float) -> (Float, Float)) -// CHECK-SIL: [[PB_REF:%.*]] = function_ref @fooTJpSpSr : $@convention(thin) (Float, @owned _AD__foo_bb0__PB__src_0_wrt_0) -> Float +// CHECK-SIL: [[PB_STRUCT:%.*]] = tuple ([[ADD_PB]] : $@callee_guaranteed (Float) -> (Float, Float)) +// CHECK-SIL: [[PB_REF:%.*]] = function_ref @fooTJpSpSr : $@convention(thin) (Float, @owned (_: @callee_guaranteed (Float) -> (Float, Float))) -> Float // CHECK-SIL: [[PB_FN:%.*]] = partial_apply [callee_guaranteed] [[PB_REF]]([[PB_STRUCT]]) // CHECK-SIL: [[VJP_RESULT:%.*]] = tuple ([[ORIG_RES]] : $Float, [[PB_FN]] : $@callee_guaranteed (Float) -> Float) // CHECK-SIL: return [[VJP_RESULT]] : $(Float, @callee_guaranteed (Float) -> Float) // CHECK-SIL: } -// CHECK-SIL-LABEL: sil private [ossa] @fooTJpSpSr : $@convention(thin) (Float, @owned _AD__foo_bb0__PB__src_0_wrt_0) -> Float { -// CHECK-SIL: bb0([[DY:%.*]] : $Float, [[PB_STRUCT:%.*]] : @owned $_AD__foo_bb0__PB__src_0_wrt_0): -// CHECK-SIL: [[ADD_PB:%.*]] = destructure_struct [[PB_STRUCT]] : $_AD__foo_bb0__PB__src_0_wrt_0 +// CHECK-SIL-LABEL: sil private [ossa] @fooTJpSpSr : $@convention(thin) (Float, @owned (_: @callee_guaranteed (Float) -> (Float, Float))) -> Float { +// CHECK-SIL: bb0([[DY:%.*]] : $Float, [[PB_STRUCT:%.*]] : @owned $(_: @callee_guaranteed (Float) -> (Float, Float))): +// CHECK-SIL: [[ADD_PB:%.*]] = destructure_tuple [[PB_STRUCT]] : $(_: @callee_guaranteed (Float) -> (Float, Float)) // CHECK-SIL: debug_value [[DY]] : $Float, let, name "y" // CHECK-SIL: [[ADD_PB_RES:%.*]] = apply [[ADD_PB]]([[DY]]) : $@callee_guaranteed (Float) -> (Float, Float) // CHECK-SIL: ([[DX_1:%.*]], [[DX_2:%.*]]) = destructure_tuple [[ADD_PB_RES]] : $(Float, Float) @@ -102,8 +108,8 @@ struct ExampleStruct { // CHECK-SIL-LABEL: sil hidden [ossa] @fooMethodTJfSUpSr : $@convention(method) (Float, ExampleStruct) -> (Float, @owned @callee_guaranteed (Float) -> Float) { -// CHECK-SIL-LABEL: sil private [ossa] @fooMethodTJdSUpSr : $@convention(thin) (Float, @owned _AD__fooMethod_bb0__DF__src_0_wrt_0) -> Float { +// CHECK-SIL-LABEL: sil private [ossa] @fooMethodTJdSUpSr : $@convention(thin) (Float, @owned (_: @callee_guaranteed (Float, Float) -> Float)) -> Float { // CHECK-SIL-LABEL: sil hidden [ossa] @fooMethodTJrSUpSr : $@convention(method) (Float, ExampleStruct) -> (Float, @owned @callee_guaranteed (Float) -> Float) { -// CHECK-SIL-LABEL: sil private [ossa] @fooMethodTJpSUpSr : $@convention(thin) (Float, @owned _AD__fooMethod_bb0__PB__src_0_wrt_0) -> Float { +// CHECK-SIL-LABEL: sil private [ossa] @fooMethodTJpSUpSr : $@convention(thin) (Float, @owned (_: @callee_guaranteed (Float) -> (Float, Float))) -> Float { diff --git a/test/AutoDiff/SILOptimizer/differentiation_control_flow_sil.swift b/test/AutoDiff/SILOptimizer/differentiation_control_flow_sil.swift index 78dd96f25d9fd..b08b7a4898fe0 100644 --- a/test/AutoDiff/SILOptimizer/differentiation_control_flow_sil.swift +++ b/test/AutoDiff/SILOptimizer/differentiation_control_flow_sil.swift @@ -19,85 +19,79 @@ func cond(_ x: Float) -> Float { return x - x } -// CHECK-DATA-STRUCTURES: struct _AD__cond_bb0__PB__src_0_wrt_0 { -// CHECK-DATA-STRUCTURES: } -// CHECK-DATA-STRUCTURES: struct _AD__cond_bb1__PB__src_0_wrt_0 { -// CHECK-DATA-STRUCTURES: var predecessor: _AD__cond_bb1__Pred__src_0_wrt_0 -// CHECK-DATA-STRUCTURES: var pullback_0: (Float) -> (Float, Float) -// CHECK-DATA-STRUCTURES: } -// CHECK-DATA-STRUCTURES: struct _AD__cond_bb2__PB__src_0_wrt_0 { -// CHECK-DATA-STRUCTURES: var predecessor: _AD__cond_bb2__Pred__src_0_wrt_0 -// CHECK-DATA-STRUCTURES: var pullback_1: (Float) -> (Float, Float) -// CHECK-DATA-STRUCTURES: } -// CHECK-DATA-STRUCTURES: struct _AD__cond_bb3__PB__src_0_wrt_0 { -// CHECK-DATA-STRUCTURES: var predecessor: _AD__cond_bb3__Pred__src_0_wrt_0 -// CHECK-DATA-STRUCTURES: } +() + +// CHECK-DATA-STRUCTURES-LABEL: Generated linear map tuples and branching trace enums for @cond +// CHECK-DATA-STRUCTURES: () +// CHECK-DATA-STRUCTURES: (predecessor: _AD__cond_bb1__Pred__src_0_wrt_0, (Float) -> (Float, Float)) +// CHECK-DATA-STRUCTURES: (predecessor: _AD__cond_bb2__Pred__src_0_wrt_0, (Float) -> (Float, Float)) +// CHECK-DATA-STRUCTURES: (predecessor: _AD__cond_bb3__Pred__src_0_wrt_0) // CHECK-DATA-STRUCTURES: enum _AD__cond_bb0__Pred__src_0_wrt_0 { // CHECK-DATA-STRUCTURES: } // CHECK-DATA-STRUCTURES: enum _AD__cond_bb1__Pred__src_0_wrt_0 { -// CHECK-DATA-STRUCTURES: case bb0(_AD__cond_bb0__PB__src_0_wrt_0) +// CHECK-DATA-STRUCTURES: case bb0(()) // CHECK-DATA-STRUCTURES: } // CHECK-DATA-STRUCTURES: enum _AD__cond_bb2__Pred__src_0_wrt_0 { -// CHECK-DATA-STRUCTURES: case bb0(_AD__cond_bb0__PB__src_0_wrt_0) +// CHECK-DATA-STRUCTURES: case bb0(()) // CHECK-DATA-STRUCTURES: } // CHECK-DATA-STRUCTURES: enum _AD__cond_bb3__Pred__src_0_wrt_0 { -// CHECK-DATA-STRUCTURES: case bb2(_AD__cond_bb2__PB__src_0_wrt_0) -// CHECK-DATA-STRUCTURES: case bb1(_AD__cond_bb1__PB__src_0_wrt_0) +// CHECK-DATA-STRUCTURES: case bb2((predecessor: _AD__cond_bb2__Pred__src_0_wrt_0, (Float) -> (Float, Float))) +// CHECK-DATA-STRUCTURES: case bb1((predecessor: _AD__cond_bb1__Pred__src_0_wrt_0, (Float) -> (Float, Float))) // CHECK-DATA-STRUCTURES: } // CHECK-SIL-LABEL: sil hidden [ossa] @condTJrSpSr : $@convention(thin) (Float) -> (Float, @owned @callee_guaranteed (Float) -> Float) { // CHECK-SIL: bb0([[INPUT_ARG:%.*]] : $Float): -// CHECK-SIL: [[BB0_PB_STRUCT:%.*]] = struct $_AD__cond_bb0__PB__src_0_wrt_0 () +// CHECK-SIL: [[BB0_PB_STRUCT:%.*]] = tuple () // CHECK-SIL: cond_br {{%.*}}, bb1, bb2 // CHECK-SIL: bb1: // CHECK-SIL: [[BB1_PRED:%.*]] = enum $_AD__cond_bb1__Pred__src_0_wrt_0, #_AD__cond_bb1__Pred__src_0_wrt_0.bb0!enumelt, [[BB0_PB_STRUCT]] -// CHECK-SIL: [[BB1_PB_STRUCT:%.*]] = struct $_AD__cond_bb1__PB__src_0_wrt_0 +// CHECK-SIL: [[BB1_PB_STRUCT:%.*]] = tuple $(predecessor: _AD__cond_bb1__Pred__src_0_wrt_0, @callee_guaranteed (Float) -> (Float, Float)) ([[BB1_PRED]] // CHECK-SIL: [[BB3_PRED_PRED1:%.*]] = enum $_AD__cond_bb3__Pred__src_0_wrt_0, #_AD__cond_bb3__Pred__src_0_wrt_0.bb1!enumelt, [[BB1_PB_STRUCT]] // CHECK-SIL: br bb3({{.*}} : $Float, [[BB3_PRED_PRED1]] : $_AD__cond_bb3__Pred__src_0_wrt_0) // CHECK-SIL: bb2: // CHECK-SIL: [[BB2_PRED:%.*]] = enum $_AD__cond_bb2__Pred__src_0_wrt_0, #_AD__cond_bb2__Pred__src_0_wrt_0.bb0!enumelt, [[BB0_PB_STRUCT]] -// CHECK-SIL: [[BB2_PB_STRUCT:%.*]] = struct $_AD__cond_bb2__PB__src_0_wrt_0 +// CHECK-SIL: [[BB2_PB_STRUCT:%.*]] = tuple $(predecessor: _AD__cond_bb2__Pred__src_0_wrt_0, @callee_guaranteed (Float) -> (Float, Float)) ([[BB2_PRED]] // CHECK-SIL: [[BB3_PRED_PRED2:%.*]] = enum $_AD__cond_bb3__Pred__src_0_wrt_0, #_AD__cond_bb3__Pred__src_0_wrt_0.bb2!enumelt, [[BB2_PB_STRUCT]] // CHECK-SIL: br bb3({{.*}} : $Float, [[BB3_PRED_PRED2]] : $_AD__cond_bb3__Pred__src_0_wrt_0) -// CHECK-SIL: bb3([[ORIG_RES:%.*]] : $Float, [[BB3_PRED_ARG:%.*]] : @owned $_AD__cond_bb3__Pred__src_0_wrt_0) -// CHECK-SIL: [[BB3_PB_STRUCT:%.*]] = struct $_AD__cond_bb3__PB__src_0_wrt_0 +// CHECK-SIL: bb3([[ORIG_RES:%.*]] : $Float, [[BB3_PRED_ARG:%.*]] : $_AD__cond_bb3__Pred__src_0_wrt_0) +// CHECK-SIL: [[BB3_PB_STRUCT:%.*]] = tuple $(predecessor: _AD__cond_bb3__Pred__src_0_wrt_0) ([[BB3_PRED_ARG]]) // CHECK-SIL: [[PULLBACK_REF:%.*]] = function_ref @condTJpSpSr // CHECK-SIL: [[PB:%.*]] = partial_apply [callee_guaranteed] [[PULLBACK_REF]]([[BB3_PB_STRUCT]]) // CHECK-SIL: [[VJP_RESULT:%.*]] = tuple ([[ORIG_RES]] : $Float, [[PB]] : $@callee_guaranteed (Float) -> Float) // CHECK-SIL: return [[VJP_RESULT]] -// CHECK-SIL-LABEL: sil private [ossa] @condTJpSpSr : $@convention(thin) (Float, @owned _AD__cond_bb3__PB__src_0_wrt_0) -> Float { -// CHECK-SIL: bb0([[SEED:%.*]] : $Float, [[BB3_PB_STRUCT:%.*]] : @owned $_AD__cond_bb3__PB__src_0_wrt_0): -// CHECK-SIL: [[BB3_PRED:%.*]] = destructure_struct [[BB3_PB_STRUCT]] : $_AD__cond_bb3__PB__src_0_wrt_0 +// CHECK-SIL-LABEL: sil private [ossa] @condTJpSpSr : $@convention(thin) (Float, @owned (predecessor: _AD__cond_bb3__Pred__src_0_wrt_0)) -> Float { +// CHECK-SIL: bb0([[SEED:%.*]] : $Float, [[BB3_PB_STRUCT:%.*]] : $(predecessor: _AD__cond_bb3__Pred__src_0_wrt_0)): +// CHECK-SIL: [[BB3_PRED:%.*]] = destructure_tuple [[BB3_PB_STRUCT]] : $(predecessor: _AD__cond_bb3__Pred__src_0_wrt_0) // CHECK-SIL: switch_enum [[BB3_PRED]] : $_AD__cond_bb3__Pred__src_0_wrt_0, case #_AD__cond_bb3__Pred__src_0_wrt_0.bb2!enumelt: bb1, case #_AD__cond_bb3__Pred__src_0_wrt_0.bb1!enumelt: bb3 -// CHECK-SIL: bb1([[BB3_PRED2_TRAMP_PB_STRUCT:%.*]] : @owned $_AD__cond_bb2__PB__src_0_wrt_0): -// CHECK-SIL: br bb2({{%.*}} : $Float, {{%.*}}: $Float, [[BB3_PRED2_TRAMP_PB_STRUCT]] : $_AD__cond_bb2__PB__src_0_wrt_0) +// CHECK-SIL: bb1([[BB3_PRED2_TRAMP_PB_STRUCT:%.*]] : @owned $(predecessor: _AD__cond_bb2__Pred__src_0_wrt_0, @callee_guaranteed (Float) -> (Float, Float))): +// CHECK-SIL: br bb2({{%.*}} : $Float, {{%.*}}: $Float, [[BB3_PRED2_TRAMP_PB_STRUCT]] : $(predecessor: _AD__cond_bb2__Pred__src_0_wrt_0, @callee_guaranteed (Float) -> (Float, Float))) -// CHECK-SIL: bb2({{%.*}} : $Float, {{%.*}} : $Float, [[BB2_PB_STRUCT:%.*]] : @owned $_AD__cond_bb2__PB__src_0_wrt_0): -// CHECK-SIL: ([[BB2_PRED:%.*]], [[BB2_PB:%.*]]) = destructure_struct [[BB2_PB_STRUCT]] +// CHECK-SIL: bb2({{%.*}} : $Float, {{%.*}} : $Float, [[BB2_PB_STRUCT:%.*]] : @owned $(predecessor: _AD__cond_bb2__Pred__src_0_wrt_0, @callee_guaranteed (Float) -> (Float, Float))): +// CHECK-SIL: ([[BB2_PRED:%.*]], [[BB2_PB:%.*]]) = destructure_tuple [[BB2_PB_STRUCT]] // CHECK-SIL: [[BB2_ADJVALS:%.*]] = apply [[BB2_PB]]([[SEED]]) : $@callee_guaranteed (Float) -> (Float, Float) // CHECK-SIL: switch_enum [[BB2_PRED]] : $_AD__cond_bb2__Pred__src_0_wrt_0, case #_AD__cond_bb2__Pred__src_0_wrt_0.bb0!enumelt: bb6 -// CHECK-SIL: bb3([[BB3_PRED1_TRAMP_PB_STRUCT:%.*]] : @owned $_AD__cond_bb1__PB__src_0_wrt_0): -// CHECK-SIL: br bb4({{%.*}} : $Float, {{%.*}}: $Float, [[BB3_PRED1_TRAMP_PB_STRUCT]] : $_AD__cond_bb1__PB__src_0_wrt_0) +// CHECK-SIL: bb3([[BB3_PRED1_TRAMP_PB_STRUCT:%.*]] : @owned $(predecessor: _AD__cond_bb1__Pred__src_0_wrt_0, @callee_guaranteed (Float) -> (Float, Float))): +// CHECK-SIL: br bb4({{%.*}} : $Float, {{%.*}}: $Float, [[BB3_PRED1_TRAMP_PB_STRUCT]] : $(predecessor: _AD__cond_bb1__Pred__src_0_wrt_0, @callee_guaranteed (Float) -> (Float, Float))) -// CHECK-SIL: bb4({{%.*}} : $Float, {{%.*}} : $Float, [[BB1_PB_STRUCT:%.*]] : @owned $_AD__cond_bb1__PB__src_0_wrt_0): -// CHECK-SIL: ([[BB1_PRED:%.*]], [[BB1_PB:%.*]]) = destructure_struct [[BB1_PB_STRUCT]] +// CHECK-SIL: bb4({{%.*}} : $Float, {{%.*}} : $Float, [[BB1_PB_STRUCT:%.*]] : @owned $(predecessor: _AD__cond_bb1__Pred__src_0_wrt_0, @callee_guaranteed (Float) -> (Float, Float))): +// CHECK-SIL: ([[BB1_PRED:%.*]], [[BB1_PB:%.*]]) = destructure_tuple [[BB1_PB_STRUCT]] // CHECK-SIL: [[BB1_ADJVALS:%.*]] = apply [[BB1_PB]]([[SEED]]) : $@callee_guaranteed (Float) -> (Float, Float) // CHECK-SIL: switch_enum [[BB1_PRED]] : $_AD__cond_bb1__Pred__src_0_wrt_0, case #_AD__cond_bb1__Pred__src_0_wrt_0.bb0!enumelt: bb5 -// CHECK-SIL: bb5([[BB1_PRED0_TRAMP_PB_STRUCT:%.*]] : $_AD__cond_bb0__PB__src_0_wrt_0): -// CHECK-SIL: br bb7({{%.*}} : $Float, [[BB1_PRED0_TRAMP_PB_STRUCT]] : $_AD__cond_bb0__PB__src_0_wrt_0) +// CHECK-SIL: bb5([[BB1_PRED0_TRAMP_PB_STRUCT:%.*]] : $()): +// CHECK-SIL: br bb7({{%.*}} : $Float, [[BB1_PRED0_TRAMP_PB_STRUCT]] : $()) -// CHECK-SIL: bb6([[BB2_PRED0_TRAMP_PB_STRUCT:%.*]] : $_AD__cond_bb0__PB__src_0_wrt_0): -// CHECK-SIL: br bb7({{%.*}} : $Float, [[BB2_PRED0_TRAMP_PB_STRUCT]] : $_AD__cond_bb0__PB__src_0_wrt_0) +// CHECK-SIL: bb6([[BB2_PRED0_TRAMP_PB_STRUCT:%.*]] : $()): +// CHECK-SIL: br bb7({{%.*}} : $Float, [[BB2_PRED0_TRAMP_PB_STRUCT]] : $()) -// CHECK-SIL: bb7({{%.*}} : $Float, [[BB0_PB_STRUCT:%.*]] : $_AD__cond_bb0__PB__src_0_wrt_0): +// CHECK-SIL: bb7({{%.*}} : $Float, [[BB0_PB_STRUCT:%.*]] : $()): // CHECK-SIL: return {{%.*}} : $Float @differentiable(reverse) @@ -157,23 +151,23 @@ func enum_notactive(_ e: Enum, _ x: Float) -> Float { // CHECK-SIL-LABEL: sil hidden [ossa] @enum_notactiveTJrUSpSr : $@convention(thin) (Enum, Float) -> (Float, @owned @callee_guaranteed (Float) -> Float) { // CHECK-SIL: bb0([[ENUM_ARG:%.*]] : $Enum, [[X_ARG:%.*]] : $Float): -// CHECK-SIL: [[BB0_PB_STRUCT:%.*]] = struct $_AD__enum_notactive_bb0__PB__src_0_wrt_1 () +// CHECK-SIL: [[BB0_PB_STRUCT:%.*]] = tuple () // CHECK-SIL: switch_enum [[ENUM_ARG]] : $Enum, case #Enum.a!enumelt: bb1, case #Enum.b!enumelt: bb2 // CHECK-SIL: bb1([[ENUM_A:%.*]] : $Float): -// CHECK-SIL: [[BB1_PRED_PRED0:%.*]] = enum $_AD__enum_notactive_bb1__Pred__src_0_wrt_1, #_AD__enum_notactive_bb1__Pred__src_0_wrt_1.bb0!enumelt, [[BB0_PB_STRUCT]] : $_AD__enum_notactive_bb0__PB__src_0_wrt_1 -// CHECK-SIL: [[BB1_PB_STRUCT:%.*]] = struct $_AD__enum_notactive_bb1__PB__src_0_wrt_1 ({{.*}}) -// CHECK-SIL: [[BB3_PRED_PRED1:%.*]] = enum $_AD__enum_notactive_bb3__Pred__src_0_wrt_1, #_AD__enum_notactive_bb3__Pred__src_0_wrt_1.bb1!enumelt, [[BB1_PB_STRUCT]] : $_AD__enum_notactive_bb1__PB__src_0_wrt_1 +// CHECK-SIL: [[BB1_PRED_PRED0:%.*]] = enum $_AD__enum_notactive_bb1__Pred__src_0_wrt_1, #_AD__enum_notactive_bb1__Pred__src_0_wrt_1.bb0!enumelt, [[BB0_PB_STRUCT]] : $() +// CHECK-SIL: [[BB1_PB_STRUCT:%.*]] = tuple $(predecessor: _AD__enum_notactive_bb1__Pred__src_0_wrt_1, @callee_guaranteed (Float) -> Float) ([[BB1_PRED_PRED0]] +// CHECK-SIL: [[BB3_PRED_PRED1:%.*]] = enum $_AD__enum_notactive_bb3__Pred__src_0_wrt_1, #_AD__enum_notactive_bb3__Pred__src_0_wrt_1.bb1!enumelt, [[BB1_PB_STRUCT]] : $(predecessor: _AD__enum_notactive_bb1__Pred__src_0_wrt_1, @callee_guaranteed (Float) -> Float) // CHECK-SIL: br bb3({{.*}} : $Float, [[BB3_PRED_PRED1]] : $_AD__enum_notactive_bb3__Pred__src_0_wrt_1) // CHECK-SIL: bb2([[ENUM_B:%.*]] : $(Float, Float)): -// CHECK-SIL: [[BB2_PRED_PRED0:%.*]] = enum $_AD__enum_notactive_bb2__Pred__src_0_wrt_1, #_AD__enum_notactive_bb2__Pred__src_0_wrt_1.bb0!enumelt, [[BB0_PB_STRUCT]] : $_AD__enum_notactive_bb0__PB__src_0_wrt_1 -// CHECK-SIL: [[BB2_PB_STRUCT:%.*]] = struct $_AD__enum_notactive_bb2__PB__src_0_wrt_1 ({{.*}}) -// CHECK-SIL: [[BB3_PRED_PRED2:%.*]] = enum $_AD__enum_notactive_bb3__Pred__src_0_wrt_1, #_AD__enum_notactive_bb3__Pred__src_0_wrt_1.bb2!enumelt, [[BB2_PB_STRUCT]] : $_AD__enum_notactive_bb2__PB__src_0_wrt_1 +// CHECK-SIL: [[BB2_PRED_PRED0:%.*]] = enum $_AD__enum_notactive_bb2__Pred__src_0_wrt_1, #_AD__enum_notactive_bb2__Pred__src_0_wrt_1.bb0!enumelt, [[BB0_PB_STRUCT]] : $() +// CHECK-SIL: [[BB2_PB_STRUCT:%.*]] = tuple $(predecessor: _AD__enum_notactive_bb2__Pred__src_0_wrt_1, @callee_guaranteed (Float) -> Float, @callee_guaranteed (Float) -> Float) ([[BB2_PRED_PRED0]] +// CHECK-SIL: [[BB3_PRED_PRED2:%.*]] = enum $_AD__enum_notactive_bb3__Pred__src_0_wrt_1, #_AD__enum_notactive_bb3__Pred__src_0_wrt_1.bb2!enumelt, [[BB2_PB_STRUCT]] : $(predecessor: _AD__enum_notactive_bb2__Pred__src_0_wrt_1, @callee_guaranteed (Float) -> Float, @callee_guaranteed (Float) -> Float) // CHECK-SIL: br bb3({{.*}} : $Float, [[BB3_PRED_PRED2]] : $_AD__enum_notactive_bb3__Pred__src_0_wrt_1) -// CHECK-SIL: bb3([[ORIG_RES:%.*]] : $Float, [[BB3_PRED_ARG:%.*]] : @owned $_AD__enum_notactive_bb3__Pred__src_0_wrt_1) -// CHECK-SIL: [[BB3_PB_STRUCT:%.*]] = struct $_AD__enum_notactive_bb3__PB__src_0_wrt_1 +// CHECK-SIL: bb3([[ORIG_RES:%.*]] : $Float, [[BB3_PRED_ARG:%.*]] : $_AD__enum_notactive_bb3__Pred__src_0_wrt_1) +// CHECK-SIL: [[BB3_PB_STRUCT:%.*]] = tuple $(predecessor: _AD__enum_notactive_bb3__Pred__src_0_wrt_1) ([[BB3_PRED_ARG]]) // CHECK-SIL: [[PULLBACK_REF:%.*]] = function_ref @enum_notactiveTJpUSpSr // CHECK-SIL: [[PB:%.*]] = partial_apply [callee_guaranteed] [[PULLBACK_REF]]([[BB3_PB_STRUCT]]) // CHECK-SIL: [[VJP_RESULT:%.*]] = tuple ([[ORIG_RES]] : $Float, [[PB]] : $@callee_guaranteed (Float) -> Float) @@ -200,30 +194,30 @@ func enum_addr_notactive(_ e: AddressOnlyEnum, _ x: Float) -> Float { // CHECK-SIL: bb0([[ENUM_ARG:%.*]] : $*AddressOnlyEnum<τ_0_0>, [[X_ARG:%.*]] : $Float): // CHECK-SIL: [[ENUM_ADDR:%.*]] = alloc_stack $AddressOnlyEnum<τ_0_0> // CHECK-SIL: copy_addr [[ENUM_ARG]] to [init] [[ENUM_ADDR]] : $*AddressOnlyEnum<τ_0_0> -// CHECK-SIL: [[BB0_PB_STRUCT:%.*]] = struct $_AD__enum_addr_notactive_bb0__PB__src_0_wrt_1_l<τ_0_0> () +// CHECK-SIL: [[BB0_PB_STRUCT:%.*]] = tuple () // CHECK-SIL: switch_enum_addr [[ENUM_ADDR]] : $*AddressOnlyEnum<τ_0_0>, case #AddressOnlyEnum.none!enumelt: bb1, case #AddressOnlyEnum.some!enumelt: bb2 -// CHECK-SIL: bb1: -// CHECK-SIL: [[BB1_PRED_PRED0:%.*]] = enum $_AD__enum_addr_notactive_bb1__Pred__src_0_wrt_1_l<τ_0_0>, #_AD__enum_addr_notactive_bb1__Pred__src_0_wrt_1_l.bb0!enumelt, [[BB0_PB_STRUCT]] : $_AD__enum_addr_notactive_bb0__PB__src_0_wrt_1_l<τ_0_0> +// CHECK-SIL-LABEL: bb1: +// CHECK-SIL: [[BB1_PRED_PRED0:%.*]] = enum $_AD__enum_addr_notactive_bb1__Pred__src_0_wrt_1_l<τ_0_0>, #_AD__enum_addr_notactive_bb1__Pred__src_0_wrt_1_l.bb0!enumelt, [[BB0_PB_STRUCT]] : $() // CHECK-SIL: dealloc_stack [[ENUM_ADDR]] : $*AddressOnlyEnum<τ_0_0> -// CHECK-SIL: [[BB1_PB_STRUCT:%.*]] = struct $_AD__enum_addr_notactive_bb1__PB__src_0_wrt_1_l<τ_0_0> ([[BB1_PRED_PRED0]] : $_AD__enum_addr_notactive_bb1__Pred__src_0_wrt_1_l<τ_0_0>) -// CHECK-SIL: [[BB3_PRED_PRED1:%.*]] = enum $_AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l<τ_0_0>, #_AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l.bb1!enumelt, [[BB1_PB_STRUCT]] : $_AD__enum_addr_notactive_bb1__PB__src_0_wrt_1_l<τ_0_0> +// CHECK-SIL: [[BB1_PB_STRUCT:%.*]] = tuple $(predecessor: _AD__enum_addr_notactive_bb1__Pred__src_0_wrt_1_l<τ_0_0>) ([[BB1_PRED_PRED0]]) +// CHECK-SIL: [[BB3_PRED_PRED1:%.*]] = enum $_AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l<τ_0_0>, #_AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l.bb1!enumelt, [[BB1_PB_STRUCT]] : $(predecessor: _AD__enum_addr_notactive_bb1__Pred__src_0_wrt_1_l<τ_0_0>) // CHECK-SIL: br bb3([[BB3_PRED_PRED1]] : $_AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l<τ_0_0>) -// CHECK-SIL: bb2: -// CHECK-SIL: [[BB2_PRED_PRED0:%.*]] = enum $_AD__enum_addr_notactive_bb2__Pred__src_0_wrt_1_l<τ_0_0>, #_AD__enum_addr_notactive_bb2__Pred__src_0_wrt_1_l.bb0!enumelt, [[BB0_PB_STRUCT]] : $_AD__enum_addr_notactive_bb0__PB__src_0_wrt_1_l<τ_0_0> +// CHECK-SIL-LABEL: bb2: +// CHECK-SIL: [[BB2_PRED_PRED0:%.*]] = enum $_AD__enum_addr_notactive_bb2__Pred__src_0_wrt_1_l<τ_0_0>, #_AD__enum_addr_notactive_bb2__Pred__src_0_wrt_1_l.bb0!enumelt, [[BB0_PB_STRUCT]] : $() // CHECK-SIL: [[ENUM_DATA:%.*]] = unchecked_take_enum_data_addr [[ENUM_ADDR]] : $*AddressOnlyEnum<τ_0_0>, #AddressOnlyEnum.some!enumelt // CHECK-SIL: destroy_addr [[ENUM_DATA]] : $*τ_0_0 // CHECK-SIL: dealloc_stack [[ENUM_ADDR]] : $*AddressOnlyEnum<τ_0_0> -// CHECK-SIL: [[BB2_PB_STRUCT:%.*]] = struct $_AD__enum_addr_notactive_bb2__PB__src_0_wrt_1_l<τ_0_0> ([[BB2_PRED_PRED0]] : $_AD__enum_addr_notactive_bb2__Pred__src_0_wrt_1_l<τ_0_0>) -// CHECK-SIL: [[BB3_PRED_PRED2:%.*]] = enum $_AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l<τ_0_0>, #_AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l.bb2!enumelt, [[BB2_PB_STRUCT]] : $_AD__enum_addr_notactive_bb2__PB__src_0_wrt_1_l<τ_0_0> +// CHECK-SIL: [[BB2_PB_STRUCT:%.*]] = tuple $(predecessor: _AD__enum_addr_notactive_bb2__Pred__src_0_wrt_1_l<τ_0_0>) ([[BB2_PRED_PRED0]]) +// CHECK-SIL: [[BB3_PRED_PRED2:%.*]] = enum $_AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l<τ_0_0>, #_AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l.bb2!enumelt, [[BB2_PB_STRUCT]] : $(predecessor: _AD__enum_addr_notactive_bb2__Pred__src_0_wrt_1_l<τ_0_0>) // CHECK-SIL: br bb3([[BB3_PRED_PRED2]] : $_AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l<τ_0_0>) // CHECK-SIL: bb3([[BB3_PRED_ARG:%.*]] : $_AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l<τ_0_0>): -// CHECK-SIL: [[BB3_PB_STRUCT:%.*]] = struct $_AD__enum_addr_notactive_bb3__PB__src_0_wrt_1_l<τ_0_0> ([[BB3_PRED_ARG]] : $_AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l<τ_0_0>) +// CHECK-SIL: [[BB3_PB_STRUCT:%.*]] = tuple $(predecessor: _AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l<τ_0_0>) ([[BB3_PRED_ARG]]) -// CHECK-SIL: [[PB_FNREF:%.*]] = function_ref @enum_addr_notactivelTJpUSpSr : $@convention(thin) <τ_0_0> (Float, @owned _AD__enum_addr_notactive_bb3__PB__src_0_wrt_1_l<τ_0_0>) -> Float -// CHECK-SIL: [[PB:%.*]] = partial_apply [callee_guaranteed] [[PB_FNREF]]<τ_0_0>([[BB3_PB_STRUCT]]) : $@convention(thin) <τ_0_0> (Float, @owned _AD__enum_addr_notactive_bb3__PB__src_0_wrt_1_l<τ_0_0>) -> Float +// CHECK-SIL: [[PB_FNREF:%.*]] = function_ref @enum_addr_notactivelTJpUSpSr : $@convention(thin) <τ_0_0> (Float, @owned (predecessor: _AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l<τ_0_0>)) -> Float +// CHECK-SIL: [[PB:%.*]] = partial_apply [callee_guaranteed] [[PB_FNREF]]<τ_0_0>([[BB3_PB_STRUCT]]) : $@convention(thin) <τ_0_0> (Float, @owned (predecessor: _AD__enum_addr_notactive_bb3__Pred__src_0_wrt_1_l<τ_0_0>)) -> Float // CHECK-SIL: [[VJP_RESULT:%.*]] = tuple ([[X_ARG]] : $Float, [[PB]] : $@callee_guaranteed (Float) -> Float) // CHECK-SIL: return [[VJP_RESULT]] : $(Float, @callee_guaranteed (Float) -> Float) @@ -241,36 +235,36 @@ func cond_tuple_var(_ x: Float) -> Float { return y.1 } -// CHECK-SIL-LABEL: sil private [ossa] @cond_tuple_varTJpSpSr : $@convention(thin) (Float, @owned _AD__cond_tuple_var_bb3__PB__src_0_wrt_0) -> Float { -// CHECK-SIL: bb0([[SEED:%.*]] : $Float, [[BB3_PB_STRUCT:%.*]] : $_AD__cond_tuple_var_bb3__PB__src_0_wrt_0): -// CHECK-SIL: [[BB3_PRED:%.*]] = destructure_struct [[BB3_PB_STRUCT]] : $_AD__cond_tuple_var_bb3__PB__src_0_wrt_0 +// CHECK-SIL-LABEL: sil private [ossa] @cond_tuple_varTJpSpSr : $@convention(thin) (Float, @owned (predecessor: _AD__cond_tuple_var_bb3__Pred__src_0_wrt_0)) -> Float { +// CHECK-SIL: bb0([[SEED:%.*]] : $Float, [[BB3_PB_STRUCT:%.*]] : $(predecessor: _AD__cond_tuple_var_bb3__Pred__src_0_wrt_0)): +// CHECK-SIL: [[BB3_PRED:%.*]] = destructure_tuple [[BB3_PB_STRUCT]] : $(predecessor: _AD__cond_tuple_var_bb3__Pred__src_0_wrt_0) // CHECK-SIL: copy_addr {{%.*}} to {{%.*}} : $*(Float, Float) // CHECK-SIL-NOT: copy_addr {{%.*}} to {{%.*}} : $*Float // CHECK-SIL: switch_enum [[BB3_PRED]] : $_AD__cond_tuple_var_bb3__Pred__src_0_wrt_0, case #_AD__cond_tuple_var_bb3__Pred__src_0_wrt_0.bb2!enumelt: bb1, case #_AD__cond_tuple_var_bb3__Pred__src_0_wrt_0.bb1!enumelt: bb3 -// CHECK-SIL: bb1([[BB3_PRED2_TRAMP_PB_STRUCT:%.*]] : $_AD__cond_tuple_var_bb2__PB__src_0_wrt_0): -// CHECK-SIL: br bb2({{%.*}} : $Float, {{%.*}} : $Float, [[BB3_PRED2_TRAMP_PB_STRUCT]] : $_AD__cond_tuple_var_bb2__PB__src_0_wrt_0) +// CHECK-SIL: bb1([[BB3_PRED2_TRAMP_PB_STRUCT:%.*]] : $(predecessor: _AD__cond_tuple_var_bb2__Pred__src_0_wrt_0)): +// CHECK-SIL: br bb2({{%.*}} : $Float, {{%.*}} : $Float, [[BB3_PRED2_TRAMP_PB_STRUCT]] : $(predecessor: _AD__cond_tuple_var_bb2__Pred__src_0_wrt_0)) -// CHECK-SIL: bb2({{%.*}} : $Float, {{%.*}} : $Float, [[BB2_PB_STRUCT:%.*]] : $_AD__cond_tuple_var_bb2__PB__src_0_wrt_0): -// CHECK-SIL: [[BB2_PRED:%.*]] = destructure_struct [[BB2_PB_STRUCT]] +// CHECK-SIL: bb2({{%.*}} : $Float, {{%.*}} : $Float, [[BB2_PB_STRUCT:%.*]] : $(predecessor: _AD__cond_tuple_var_bb2__Pred__src_0_wrt_0)): +// CHECK-SIL: [[BB2_PRED:%.*]] = destructure_tuple [[BB2_PB_STRUCT]] // CHECK-SIL: copy_addr {{%.*}} to {{%.*}} : $*(Float, Float) // CHECK-SIL-NOT: copy_addr {{%.*}} to {{%.*}} : $*Float // CHECK-SIL: switch_enum [[BB2_PRED]] : $_AD__cond_tuple_var_bb2__Pred__src_0_wrt_0, case #_AD__cond_tuple_var_bb2__Pred__src_0_wrt_0.bb0!enumelt: bb6 -// CHECK-SIL: bb3([[BB3_PRED1_TRAMP_PB_STRUCT:%.*]] : $_AD__cond_tuple_var_bb1__PB__src_0_wrt_0): -// CHECK-SIL: br bb4({{%.*}} : $Float, {{%.*}} : $Float, [[BB3_PRED1_TRAMP_PB_STRUCT]] : $_AD__cond_tuple_var_bb1__PB__src_0_wrt_0) +// CHECK-SIL: bb3([[BB3_PRED1_TRAMP_PB_STRUCT:%.*]] : $(predecessor: _AD__cond_tuple_var_bb1__Pred__src_0_wrt_0)): +// CHECK-SIL: br bb4({{%.*}} : $Float, {{%.*}} : $Float, [[BB3_PRED1_TRAMP_PB_STRUCT]] : $(predecessor: _AD__cond_tuple_var_bb1__Pred__src_0_wrt_0)) -// CHECK-SIL: bb4({{%.*}} : $Float, {{%.*}} : $Float, [[BB1_PB_STRUCT:%.*]] : $_AD__cond_tuple_var_bb1__PB__src_0_wrt_0): -// CHECK-SIL: [[BB1_PRED:%.*]] = destructure_struct [[BB1_PB_STRUCT]] +// CHECK-SIL: bb4({{%.*}} : $Float, {{%.*}} : $Float, [[BB1_PB_STRUCT:%.*]] : $(predecessor: _AD__cond_tuple_var_bb1__Pred__src_0_wrt_0)): +// CHECK-SIL: [[BB1_PRED:%.*]] = destructure_tuple [[BB1_PB_STRUCT]] // CHECK-SIL: copy_addr {{%.*}} to {{%.*}} : $*(Float, Float) // CHECK-SIL-NOT: copy_addr {{%.*}} to {{%.*}} : $*Float // CHECK-SIL: switch_enum [[BB1_PRED]] : $_AD__cond_tuple_var_bb1__Pred__src_0_wrt_0, case #_AD__cond_tuple_var_bb1__Pred__src_0_wrt_0.bb0!enumelt: bb5 -// CHECK-SIL: bb5([[BB1_PRED0_TRAMP_PB_STRUCT:%.*]] : $_AD__cond_tuple_var_bb0__PB__src_0_wrt_0): -// CHECK-SIL: br bb7({{%.*}} : $Float, [[BB1_PRED0_TRAMP_PB_STRUCT]] : $_AD__cond_tuple_var_bb0__PB__src_0_wrt_0) +// CHECK-SIL: bb5([[BB1_PRED0_TRAMP_PB_STRUCT:%.*]] : $()): +// CHECK-SIL: br bb7({{%.*}} : $Float, [[BB1_PRED0_TRAMP_PB_STRUCT]] : $()) -// CHECK-SIL: bb6([[BB2_PRED0_TRAMP_PB_STRUCT:%.*]] : $_AD__cond_tuple_var_bb0__PB__src_0_wrt_0): -// CHECK-SIL: br bb7({{%.*}} : $Float, [[BB2_PRED0_TRAMP_PB_STRUCT]] : $_AD__cond_tuple_var_bb0__PB__src_0_wrt_0) +// CHECK-SIL: bb6([[BB2_PRED0_TRAMP_PB_STRUCT:%.*]] : $()): +// CHECK-SIL: br bb7({{%.*}} : $Float, [[BB2_PRED0_TRAMP_PB_STRUCT]] : $()) -// CHECK-SIL: bb7({{%.*}} : $Float, [[BB0_PB_STRUCT:%.*]] : $_AD__cond_tuple_var_bb0__PB__src_0_wrt_0): +// CHECK-SIL: bb7({{%.*}} : $Float, [[BB0_PB_STRUCT:%.*]] : $()): // CHECK-SIL: return {{%.*}} : $Float diff --git a/test/AutoDiff/SILOptimizer/semantic_member_accessors_sil.swift b/test/AutoDiff/SILOptimizer/semantic_member_accessors_sil.swift index 610deab218cab..1d2ce56dc0505 100644 --- a/test/AutoDiff/SILOptimizer/semantic_member_accessors_sil.swift +++ b/test/AutoDiff/SILOptimizer/semantic_member_accessors_sil.swift @@ -77,7 +77,7 @@ func trigger(_ x: T.Type) { // CHECK: } // CHECK-LABEL: sil private [ossa] @$s4null6StructV1xSfvsTJpSSpSr -// CHECK: bb0([[ADJ_SELF:%.*]] : $*Struct.TangentVector, {{.*}} : $_AD__$s4null6StructV1xSfvs_bb0__PB__src_0_wrt_0_1): +// CHECK: bb0([[ADJ_SELF:%.*]] : $*Struct.TangentVector, {{.*}} : $()): // CHECK: [[ADJ_X_ADDR:%.*]] = struct_element_addr [[ADJ_SELF]] : $*Struct.TangentVector, #Struct.TangentVector.x // CHECK: [[ADJ_X:%.*]] = load [trivial] [[ADJ_X_ADDR]] : $*Float // CHECK: [[ZERO_FN:%.*]] = witness_method $Float, #AdditiveArithmetic.zero!getter @@ -86,7 +86,7 @@ func trigger(_ x: T.Type) { // CHECK: } // CHECK-LABEL: sil private [ossa] @$s4null6StructV1xSfvgTJpSpSr -// CHECK: bb0([[ADJ_X:%.*]] : $Float, {{.*}} : $_AD__$s4null6StructV1xSfvg_bb0__PB__src_0_wrt_0): +// CHECK: bb0([[ADJ_X:%.*]] : $Float, {{.*}} : $()): // CHECK: [[ADJ_Y_ADDR:%.*]] = alloc_stack $Float // CHECK: [[ZERO_FN:%.*]] = witness_method $Float, #AdditiveArithmetic.zero!getter // CHECK: apply [[ZERO_FN]]([[ADJ_Y_ADDR]], {{.*}}) diff --git a/test/AutoDiff/compiler_crashers_fixed/58660-conflicting-debug-info-inlining.swift b/test/AutoDiff/compiler_crashers_fixed/58660-conflicting-debug-info-inlining.swift index ae3ba68b3c4cd..e596edded8bfd 100644 --- a/test/AutoDiff/compiler_crashers_fixed/58660-conflicting-debug-info-inlining.swift +++ b/test/AutoDiff/compiler_crashers_fixed/58660-conflicting-debug-info-inlining.swift @@ -45,7 +45,7 @@ struct MyModel: Differentiable { mutating func member4() { // CHECK-LABEL: // pullback of MyModel.member4() // CHECK-NOT: debug_value %{{.*}} : $MyModel.TangentVector, var, name %{{.*}}, argno 1, implicit, scope -// CHECK: bb1(%{{.*}} : $_AD__$s4main7MyModelV7member4yyF_bb1__PB__src_0_wrt_0): +// CHECK: bb0(%{{.*}} : $(predecessor: _AD__$s4main7MyModelV7member4yyF_bb3__Pred__src_0_wrt_0)): // CHECK: debug_value %{{.*}} : $MyModel.TangentVector, var, name "derivative of 'self' in scope at {{.*}} (scope #1)", implicit, scope // Must be a differentiable type. var localVar: Float = 0 diff --git a/test/AutoDiff/validation-test/simple_math.swift b/test/AutoDiff/validation-test/simple_math.swift index 88b33e0ecfeaf..e855e9b9e50cf 100644 --- a/test/AutoDiff/validation-test/simple_math.swift +++ b/test/AutoDiff/validation-test/simple_math.swift @@ -438,7 +438,7 @@ SimpleMathTests.test("Adjoint value accumulation for aggregate lhs and concrete // CHECK-LABEL: sil private [ossa] @${{.*}}doubled{{.*}}TJp{{.*}} : $@convention(thin) (Float, @owned {{.*}}) -> SmallTestModel.TangentVector { // CHECK: bb0([[DX:%.*]] : $Float, [[PB_STRUCT:%.*]] : {{.*}}): -// CHECK: ([[PB0:%.*]], [[PB1:%.*]]) = destructure_struct [[PB_STRUCT]] +// CHECK: ([[PB0:%.*]], [[PB1:%.*]]) = destructure_tuple [[PB_STRUCT]] // CHECK: [[ADJ_TUPLE:%.*]] = apply [[PB1]]([[DX]]) : $@callee_guaranteed (Float) -> (Float, Float) // CHECK: ([[TMP0:%.*]], [[ADJ_CONCRETE:%.*]]) = destructure_tuple [[ADJ_TUPLE]] : $(Float, Float) // CHECK: [[TMP1:%.*]] = apply [[PB0]]([[TMP0]]) : $@callee_guaranteed (Float) -> SmallTestModel.TangentVector From d7852025a9dc3b5045b4376b9e6ecddba2041262 Mon Sep 17 00:00:00 2001 From: Arnold Schwaighofer Date: Wed, 8 Feb 2023 08:12:11 -0800 Subject: [PATCH 32/98] IRGen: Change to using frame-pointer=non-leaf for functions that request no framepointer Using frame-pointer=none inhibits usage of compact unwind info. rdar://98857255 --- lib/IRGen/IRGenModule.cpp | 2 +- test/IRGen/default_function_ir_attributes.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index a89c22e44c50c..3855a6f2c152a 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -1310,7 +1310,7 @@ bool swift::irgen::shouldRemoveTargetFeature(StringRef feature) { } void IRGenModule::setHasNoFramePointer(llvm::AttrBuilder &Attrs) { - Attrs.addAttribute("frame-pointer", "none"); + Attrs.addAttribute("frame-pointer", "non-leaf"); } void IRGenModule::setHasNoFramePointer(llvm::Function *F) { diff --git a/test/IRGen/default_function_ir_attributes.swift b/test/IRGen/default_function_ir_attributes.swift index cf10ccdb9ca9e..b0041410a1b08 100644 --- a/test/IRGen/default_function_ir_attributes.swift +++ b/test/IRGen/default_function_ir_attributes.swift @@ -171,6 +171,6 @@ func test_computed_key_path_generic_thunks(value: T) -> KeyPat // unlikely that handrolled code generation would think to add one. // CHECK: attributes [[ATTRS_SIMPLE]] = { [[CUSTOM_ATTRS:.*target-cpu.*]] }{{$}} // CHECK-DAG: attributes [[ATTRS_NOINLINE_NOUNWIND]] = { noinline nounwind {{.*target-cpu.*}} } -// CHECK-DAG: attributes [[ATTRS_NOINLINE_READNONE_NOUNWIND_NOFRAME]] = { noinline nounwind readnone {{.*}}"frame-pointer"="none"{{.*target-cpu.*}} } -// CHECK-DAG: attributes [[ATTRS_NOINLINE_READONLY_NOUNWIND_NOFRAME]] = { noinline nounwind readonly willreturn {{.*}}"frame-pointer"="none"{{.*target-cpu.*}} } +// CHECK-DAG: attributes [[ATTRS_NOINLINE_READNONE_NOUNWIND_NOFRAME]] = { noinline nounwind readnone {{.*}}"frame-pointer"="non-leaf"{{.*target-cpu.*}} } +// CHECK-DAG: attributes [[ATTRS_NOINLINE_READONLY_NOUNWIND_NOFRAME]] = { noinline nounwind readonly willreturn {{.*}}"frame-pointer"="non-leaf"{{.*target-cpu.*}} } // CHECK-DAG: attributes [[ATTRS_NOUNWIND]] = { nounwind [[CUSTOM_ATTRS]] }{{$}} From e025a49edca1eca0017e73292f574de5a4396a4a Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Wed, 8 Feb 2023 09:20:43 -0800 Subject: [PATCH 33/98] [interop] NFC, test, drop libcxx-in-sdk requirement CI is running with SDKs that have libc++ already --- test/Interop/Cxx/stdlib/print-libcxx-module-interface.swift | 1 - .../print-libcxx-symbolic-module-interface.swift | 1 - test/Interop/lit.local.cfg | 5 ----- 3 files changed, 7 deletions(-) diff --git a/test/Interop/Cxx/stdlib/print-libcxx-module-interface.swift b/test/Interop/Cxx/stdlib/print-libcxx-module-interface.swift index 402851cc45f88..5c4f28c892348 100644 --- a/test/Interop/Cxx/stdlib/print-libcxx-module-interface.swift +++ b/test/Interop/Cxx/stdlib/print-libcxx-module-interface.swift @@ -1,7 +1,6 @@ // RUN: %target-swift-ide-test -print-module -module-to-print=CxxStdlib -source-filename=x -enable-experimental-cxx-interop -enable-objc-interop -module-print-submodules | %FileCheck %s // REQUIRES: OS=macosx -// REQUIRES: libcxx-in-sdk // CHECK: enum std { // CHECK-NEXT: enum __1 { diff --git a/test/Interop/Cxx/symbolic-imports/print-libcxx-symbolic-module-interface.swift b/test/Interop/Cxx/symbolic-imports/print-libcxx-symbolic-module-interface.swift index 2727437510530..befb36bb46908 100644 --- a/test/Interop/Cxx/symbolic-imports/print-libcxx-symbolic-module-interface.swift +++ b/test/Interop/Cxx/symbolic-imports/print-libcxx-symbolic-module-interface.swift @@ -2,7 +2,6 @@ // REQUIRES: asserts // REQUIRES: OS=macosx -// REQUIRES: libcxx-in-sdk // CHECK: enum std { // CHECK-NEXT: enum __1 { diff --git a/test/Interop/lit.local.cfg b/test/Interop/lit.local.cfg index fe41b4457c47e..4b8502b1b2871 100644 --- a/test/Interop/lit.local.cfg +++ b/test/Interop/lit.local.cfg @@ -12,11 +12,6 @@ clang_opt = clang_compile_opt is_cf_options_interop_updated = True if config.variant_sdk and config.variant_sdk != "": - # Check if libc++ is present in the SDK or not. - if config.target_sdk_libcxx_path and os.path.exists(config.target_sdk_libcxx_path): - config.available_features.add('libcxx-in-sdk') - config.substitutions.insert(0, ('%libcxx-in-sdk-path', config.target_sdk_libcxx_path)) - # Check if CF_OPTIONS macro has been updated to be imported into Swift in C++ mode correctly. cf_avail_path = os.path.join(config.variant_sdk, 'System', 'Library', 'Frameworks', 'CoreFoundation.framework', 'Versions', 'A', 'Headers', 'CFAvailability.h') cf_avail_path_embedded = os.path.join(config.variant_sdk, 'System', 'Library', 'Frameworks', 'CoreFoundation.framework', 'Headers', 'CFAvailability.h') From 61ab4d539d65ec1cd9dda0b14355e5bd6d574d83 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 8 Feb 2023 09:27:07 -0800 Subject: [PATCH 34/98] [Frontend] Remove TypeWrappers flag and associated attributes --- include/swift/Basic/Features.def | 5 ----- utils/gyb_syntax_support/AttributeKinds.py | 10 ---------- 2 files changed, 15 deletions(-) diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 5b9aea690a31e..9ff4c470fe73e 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -132,11 +132,6 @@ EXPERIMENTAL_FEATURE(SendableCompletionHandlers, false) /// Enables opaque type erasure without also enabling implict dynamic EXPERIMENTAL_FEATURE(OpaqueTypeErasure, false) -/// Whether to enable experimental @typeWrapper feature which allows to -/// declare a type that controls access to all stored properties of the -/// wrapped type. -EXPERIMENTAL_FEATURE(TypeWrappers, false) - /// Whether to perform round-trip testing of the Swift Swift parser. EXPERIMENTAL_FEATURE(ParserRoundTrip, false) diff --git a/utils/gyb_syntax_support/AttributeKinds.py b/utils/gyb_syntax_support/AttributeKinds.py index c40d35e6ded8e..758955ff85bc8 100644 --- a/utils/gyb_syntax_support/AttributeKinds.py +++ b/utils/gyb_syntax_support/AttributeKinds.py @@ -692,11 +692,6 @@ def __init__(self, name, swift_name=None): ABIStableToAdd, ABIStableToRemove, APIStableToAdd, APIStableToRemove, code=133), - SimpleDeclAttribute('typeWrapper', 'TypeWrapper', - OnStruct, OnClass, OnEnum, - ABIStableToAdd, ABIStableToRemove, APIStableToAdd, APIStableToRemove, # noqa: E501 - code=134), - SimpleDeclAttribute('_spiOnly', 'SPIOnly', OnImport, UserInaccessible, ABIStableToAdd, ABIStableToRemove, APIStableToAdd, APIStableToRemove, # noqa: E501 @@ -706,11 +701,6 @@ def __init__(self, name, swift_name=None): APIBreakingToAdd, APIStableToRemove, ABIStableToAdd, ABIStableToRemove, # noqa: E501 code=136), - SimpleDeclAttribute('typeWrapperIgnored', 'TypeWrapperIgnored', - OnVar, - ABIStableToAdd, ABIStableToRemove, APIStableToAdd, APIStableToRemove, # noqa: E501 - code=137), - SimpleDeclAttribute('_noMetadata', 'NoMetadata', OnGenericTypeParam, UserInaccessible, From acb7f8efda6ea9402d89806446668b35f1f6b0b3 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 8 Feb 2023 10:10:58 -0800 Subject: [PATCH 35/98] Implement generic signature checking for PartialType.create(with:) (#63496) Check other constraints some more fixes --- include/swift/Runtime/Metadata.h | 5 + .../Sources/Reflection/PartialType.swift | 439 ++++++++++++++---- .../Reflection/Sources/Reflection/Type.swift | 6 +- .../ContextDescriptorValues.swift | 52 ++- .../ContextDescriptor/GenericSignature.swift | 209 ++++++++- .../Sources/_Runtime/Functions.swift | 4 + .../_Runtime/Metadata/ClassMetadata.swift | 2 +- .../Sources/_Runtime/Metadata/Metadata.swift | 17 + .../Metadata/MetadataAccessFunction.swift | 186 ++++++-- .../_Runtime/Utils/MangledTypeReference.swift | 7 + .../Sources/_Runtime/Utils/PointerStuff.swift | 28 +- .../RelativeIndirectablePointerIntPair.swift | 15 + stdlib/public/runtime/ProtocolConformance.cpp | 7 + test/stdlib/Reflection/PartialType.swift | 225 +++++++++ 14 files changed, 1052 insertions(+), 150 deletions(-) create mode 100644 test/stdlib/Reflection/PartialType.swift diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index 5292dab2ff412..bc390dbcd39db 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -879,6 +879,11 @@ SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL MetadataResponse getSuperclassMetadata(MetadataRequest request, const ClassMetadata *self); +SWIFT_CC(swift) +SWIFT_RUNTIME_STDLIB_SPI +bool _swift_class_isSubclass(const Metadata *subclass, + const Metadata *superclass); + #if !NDEBUG /// Verify that the given metadata pointer correctly roundtrips its /// mangled name through the demangler. diff --git a/stdlib/public/Reflection/Sources/Reflection/PartialType.swift b/stdlib/public/Reflection/Sources/Reflection/PartialType.swift index 7d4262b42bf2e..51eed79b4aba8 100644 --- a/stdlib/public/Reflection/Sources/Reflection/PartialType.swift +++ b/stdlib/public/Reflection/Sources/Reflection/PartialType.swift @@ -39,118 +39,379 @@ extension PartialType { @available(SwiftStdlib 5.9, *) extension PartialType { + /// Creates a fully realized and uniqued `Type` instance using this + /// `PartialType` as the base type. + /// + /// This variant takes no arguments which assumes the type being referenced by + /// this `PartialType` is completely non-generic. In the following example, + /// `Nested` is technically generic, but in reality there's only a single + /// spelling of this which is `Generic.Nested`. Considering this, + /// `PartialType` does not consider this type to be generic thus you can call + /// the no argument variant of `create(with:)` to create a type of `Nested`. + /// + /// struct Generic {} + /// + /// extension Generic where T == Int { + /// struct Nested {} + /// } + /// + /// let nestedTy = Type(Generic.Nested.self) + /// print(nestedTy.partial?.create()) // Optional(Generic.Nested) + /// + /// - Returns: The created `Type` instance or nil if the operation failed. + @available(SwiftStdlib 5.9, *) @inlinable public func create() -> Type? { - guard !descriptor.base.flags.isGeneric else { - return nil - } - - return Type(descriptor.accessor(.complete)) + _create(with: UnsafeBufferPointer(start: nil, count: 0)) } - + + /// Creates a fully realized and uniqued `Type` instance using this + /// `PartialType` as the base type. + /// + /// Takes a list of types to use when making the full `Type` instance. It + /// expects the exact same number of generic arguments that is needed for + /// creation. Note that all of the provided types must fulfill the + /// requirements of the type's generic signature. In the following example, + /// `Dictionary` requires that its `Key` argument must conform to `Hashable`. + /// If the provided `Key` argument in the list of types does not conform to + /// `Hashable`, this operation will fail and return a `nil`. + /// + /// struct Dictionary {} + /// + /// let dictTy = Type([String: String].self) + /// // Optional(Dictionary) + /// print(dictTy.partial?.create(with: Type(String.self), Type(Int.self))) + /// + /// The order in which the provided types are assigned start from the + /// outermost generic context and works its way inward. + /// + /// struct ABC { + /// struct XYZ {} + /// } + /// + /// Effectively `XYZ` has 4 generic arguments needed to fully realize it. + /// Given the following list of types: `[Int, String, Double, Array]`, + /// we start with the outermost type, `ABC`, and assign the following generic + /// arguments: `` and lookup the `Equatable` conformance + /// for `String`. Now for `XYZ`, we finish the generic assignment with the + /// following: `>` and lookup `Double`'s + /// `Hashable`. + /// + /// - Parameters: + /// - types: A variadic list of `Type` instances used when making this + /// `PartialType`'s realized `Type`. + /// - Returns: The created `Type` instance or nil if the operation failed. + @available(SwiftStdlib 5.9, *) @inlinable - public func create(with arg: Type) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == 1 else { - return nil - } - - return Type(descriptor.accessor(.complete, arg.metadata)) + public func create(with types: Type...) -> Type? { + create(with: types) } - + + /// Creates a fully realized and uniqued `Type` instance using this + /// `PartialType` as the base type. + /// + /// Takes a list of types to use when making the full `Type` instance. It + /// expects the exact same number of generic arguments that is needed for + /// creation. Note that all of the provided types must fulfill the + /// requirements of the type's generic signature. In the following example, + /// `Dictionary` requires that its `Key` argument must conform to `Hashable`. + /// If the provided `Key` argument in the list of types does not conform to + /// `Hashable`, this operation will fail and return a `nil`. + /// + /// struct Dictionary {} + /// + /// let dictTy = Type([String: String].self) + /// // Optional(Dictionary) + /// print(dictTy.partial?.create( + /// with: [Type(String.self), Type(Int.self)] + /// )) + /// + /// The order in which the provided types are assigned start from the + /// outermost generic context and works its way inward. + /// + /// struct ABC { + /// struct XYZ {} + /// } + /// + /// Effectively `XYZ` has 4 generic arguments needed to fully realize it. + /// Given the following list of types: `[Int, String, Double, Array]`, + /// we start with the outermost type, `ABC`, and assign the following generic + /// arguments: `` and lookup the `Equatable` conformance + /// for `String`. Now for `XYZ`, we finish the generic assignment with the + /// following: `>` and lookup `Double`'s + /// `Hashable`. + /// + /// - Parameters: + /// - types: An array of `Type` instances used when making this + /// `PartialType`'s realized `Type`. + /// - Returns: The created `Type` instance or nil if the operation failed. + @available(SwiftStdlib 5.9, *) @inlinable - public func create(with arg0: Type, _ arg1: Type) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == 2 else { - return nil + public func create(with args: [Type]) -> Type? { + args.withUnsafeBufferPointer { + let buffer = UnsafeBufferPointer( + start: UnsafePointer( + $0.baseAddress.unsafelyUnwrapped._rawValue + ), + count: $0.count + ) + + return _create(with: buffer) } - - return Type(descriptor.accessor(.complete, arg0.metadata, arg1.metadata)) } - + + /// Creates a fully realized and uniqued `Type` instance using this + /// `PartialType` as the base type. + /// + /// Takes a list of types to use when making the full `Type` instance. It + /// expects the exact same number of generic arguments that is needed for + /// creation. Note that all of the provided types must fulfill the + /// requirements of the type's generic signature. In the following example, + /// `Dictionary` requires that its `Key` argument must conform to `Hashable`. + /// If the provided `Key` argument in the list of types does not conform to + /// `Hashable`, this operation will fail and return a `nil`. + /// + /// struct Dictionary {} + /// + /// let dictTy = Type([String: String].self) + /// // Optional(Dictionary) + /// print(dictTy.partial?.create(with: String.self, Int.self)) + /// + /// The order in which the provided types are assigned start from the + /// outermost generic context and works its way inward. + /// + /// struct ABC { + /// struct XYZ {} + /// } + /// + /// Effectively `XYZ` has 4 generic arguments needed to fully realize it. + /// Given the following list of types: `[Int, String, Double, Array]`, + /// we start with the outermost type, `ABC`, and assign the following generic + /// arguments: `` and lookup the `Equatable` conformance + /// for `String`. Now for `XYZ`, we finish the generic assignment with the + /// following: `>` and lookup `Double`'s + /// `Hashable`. + /// + /// - Parameters: + /// - types: A variadic list of `Any.Type` instances used when making this + /// `PartialType`'s realized `Type`. + /// - Returns: The created `Type` instance or nil if the operation failed. + @available(SwiftStdlib 5.9, *) @inlinable - public func create(with arg0: Type, _ arg1: Type, _ arg2: Type) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == 3 else { - return nil - } - - return Type(descriptor.accessor( - .complete, - arg0.metadata, - arg1.metadata, - arg2.metadata - )) + public func create(with args: any Any.Type...) -> Type? { + create(with: args) } - + + /// Creates a fully realized and uniqued `Type` instance using this + /// `PartialType` as the base type. + /// + /// Takes a list of types to use when making the full `Type` instance. It + /// expects the exact same number of generic arguments that is needed for + /// creation. Note that all of the provided types must fulfill the + /// requirements of the type's generic signature. In the following example, + /// `Dictionary` requires that its `Key` argument must conform to `Hashable`. + /// If the provided `Key` argument in the list of types does not conform to + /// `Hashable`, this operation will fail and return a `nil`. + /// + /// struct Dictionary {} + /// + /// let dictTy = Type([String: String].self) + /// // Optional(Dictionary) + /// print(dictTy.partial?.create(with: [String.self, Int.self])) + /// + /// The order in which the provided types are assigned start from the + /// outermost generic context and works its way inward. + /// + /// struct ABC { + /// struct XYZ {} + /// } + /// + /// Effectively `XYZ` has 4 generic arguments needed to fully realize it. + /// Given the following list of types: `[Int, String, Double, Array]`, + /// we start with the outermost type, `ABC`, and assign the following generic + /// arguments: `` and lookup the `Equatable` conformance + /// for `String`. Now for `XYZ`, we finish the generic assignment with the + /// following: `>` and lookup `Double`'s + /// `Hashable`. + /// + /// - Parameters: + /// - types: An array of `Any.Type` instances used when making this + /// `PartialType`'s realized `Type`. + /// - Returns: The created `Type` instance or nil if the operation failed. + @available(SwiftStdlib 5.9, *) @inlinable - public func create(with args: Type...) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == args.count else { - return nil - } - - let metadataArgs = args.map { - $0.metadata + public func create(with args: [any Any.Type]) -> Type? { + args.withUnsafeBufferPointer { + let buffer = UnsafeBufferPointer( + start: UnsafePointer( + $0.baseAddress.unsafelyUnwrapped._rawValue + ), + count: $0.count + ) + + return _create(with: buffer) } - - return Type(descriptor.accessor(.complete, metadataArgs)) } -} -@available(SwiftStdlib 5.9, *) -extension PartialType { - @inlinable - public func create(with arg: Any.Type) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == 1 else { - return nil + @available(SwiftStdlib 5.9, *) + @usableFromInline + internal func _create( + with args: UnsafeBufferPointer + ) -> Type? { + // If a descriptor doesn't have a generic signature, it itself is not + // generic. Thus, we have the 0 argument case, so just call the accessor + // with no arguments if we were passed no arguments. + guard let genericSig = descriptor.genericSignature else { + guard args.count == 0 else { + return nil + } + + return Type(descriptor.accessor(.complete)) } - - return Type(descriptor.accessor(.complete, Metadata(arg))) - } - - @inlinable - public func create(with arg0: Any.Type, _ arg1: Any.Type) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == 2 else { + + // Otherwise, this type is generic. + + // Gather the number of key parameters defined by the actual parameters in a + // generic signature. + var numberOfParameterKeyArguments = 0 + + for parameter in genericSig.parameters { + if parameter.hasKeyArgument { + numberOfParameterKeyArguments += 1 + } + } + + // Make sure the number of arguments we were given is equal to the number of + // parameter key arguments in our generic signature. + guard numberOfParameterKeyArguments == args.count else { return nil } - - return Type(descriptor.accessor(.complete, Metadata(arg0), Metadata(arg1))) + + // If we don't have any parameter key arguments, then we're done and can + // call the accessor with nothing. + guard numberOfParameterKeyArguments > 0 else { + return Type(descriptor.accessor(.complete)) + } + + // If we don't have requirements to deal with, just do the simple thing and + // call the accessor with just our metadata arguments. Otherwise, we need to + // ensure our arguments conform to all their respective protocols, layouts, + // or same type requirements. + guard genericSig.requirements.count > 0 else { + return createNoRequirements(with: args) + } + + return createRequirements(with: args, genericSig) } - - @inlinable - public func create( - with arg0: Any.Type, - _ arg1: Any.Type, - _ arg2: Any.Type + + @available(SwiftStdlib 5.9, *) + internal func createNoRequirements( + with args: UnsafeBufferPointer ) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == 3 else { - return nil + switch args.count { + case 1: + return Type(descriptor.accessor(.complete, args[0])) + case 2: + return Type(descriptor.accessor(.complete, args[0], args[1])) + case 3: + return Type(descriptor.accessor(.complete, args[0], args[1], args[2])) + default: + return Type(descriptor.accessor(.complete, args)) } - - return Type(descriptor.accessor( - .complete, - Metadata(arg0), - Metadata(arg1), - Metadata(arg2) - )) } - - @inlinable - public func create(with args: Any.Type...) -> Type? { - guard let genericSig = descriptor.genericSignature, - genericSig.parameters.count == args.count else { - return nil + + @available(SwiftStdlib 5.9, *) + internal func createRequirements( + with args: UnsafeBufferPointer, + _ genericSig: GenericSignature + ) -> Type? { + var keyArguments = Array(args) + let argPtr = UnsafeRawPointer(args.baseAddress.unsafelyUnwrapped) + + for req in genericSig.requirements { + switch req.flags.kind { + // There are 3 kinds of same type requirements: + // + // 1. Concrete type like 'T == String' + // 2. Associated type same type constraints. E.g. 'T.Element == X' + // 3. Same type constraints to other generic parameters. E.g. 'T == U' + // + // The first case should have been handled before we got here because + // those are purely syntactical parameters at that point. However, we must + // still check the 2nd case. The 3rd case is also somewhat syntactical, + // but we still have a key argument in that case which is why it makes + // its way down here. + case .sameType: + if !req.checkSameType(in: descriptor.base, with: argPtr) { + return nil + } + + // Protocol conformance requirement like 'T: P'. + case .protocol: + // Ensure that the passed parameters conform to their respectful + // protocols and pass the witness table + guard let witnessTable = req.checkProtocolConformance( + in: descriptor.base, + with: argPtr + ) else { + return nil + } + + // If this requirement doesn't introduce a key argument, don't append + // the found witness table to our arguments. Not sure if this is + // possible, but let's be defensive here. + guard req.flags.hasKeyArgument else { + continue + } + + keyArguments.append(witnessTable.ptr) + + // Check that the appropriate argument is a subclass of the required base + // class. + case .baseClass: + if !req.checkBaseClass(in: descriptor.base, with: argPtr) { + return nil + } + + // The only currently legal layout constraint is 'AnyObject'. Ensure that + // whatever argument is supposed to bind to this constraint is in fact + // an object. + case .layout: + if !req.checkLayout(in: descriptor.base, with: argPtr) { + return nil + } + + // Same conformance requirements are currently not emitted, so it's safe + // to skip this kind. + case .sameConformance: + continue + + // If we have a requirement that we don't know about, just be defensive + // and return nil. + default: + return nil + } } - - let metadataArgs = args.map { - Metadata($0) + + switch keyArguments.count { + case 1: + return Type(descriptor.accessor(.complete, keyArguments[0])) + case 2: + return Type(descriptor.accessor( + .complete, + keyArguments[0], + keyArguments[1] + )) + case 3: + return Type(descriptor.accessor( + .complete, + keyArguments[0], + keyArguments[1], + keyArguments[2] + )) + default: + return Type(descriptor.accessor(.complete, keyArguments)) } - - return Type(descriptor.accessor(.complete, metadataArgs)) } } diff --git a/stdlib/public/Reflection/Sources/Reflection/Type.swift b/stdlib/public/Reflection/Sources/Reflection/Type.swift index e15e25088d3d7..489df2bf62f4f 100644 --- a/stdlib/public/Reflection/Sources/Reflection/Type.swift +++ b/stdlib/public/Reflection/Sources/Reflection/Type.swift @@ -65,8 +65,8 @@ extension Type { @available(SwiftStdlib 5.9, *) extension Type { @inlinable - public var swiftType: Any.Type { - unsafeBitCast(metadata) + public var swiftType: any Any.Type { + unsafeBitCast(metadata, to: Any.Type.self) } } @@ -117,7 +117,7 @@ extension Type { extension Type: CustomStringConvertible { @inlinable public var description: String { - _typeName(unsafeBitCast(metadata.ptr), qualified: false) + _typeName(swiftType, qualified: false) } } diff --git a/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/ContextDescriptorValues.swift b/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/ContextDescriptorValues.swift index a3903eb918766..c37dd8a05f109 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/ContextDescriptorValues.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/ContextDescriptorValues.swift @@ -441,6 +441,41 @@ extension FieldDescriptor.Element { } } +@available(SwiftStdlib 5.9, *) +extension GenericSignature { + @available(SwiftStdlib 5.9, *) + @frozen + public struct LayoutKind { + @usableFromInline + let value: UInt32 + + @available(SwiftStdlib 5.9, *) + @inlinable + init(_ value: UInt32) { + self.value = value + } + + @available(SwiftStdlib 5.9, *) + @inline(__always) + @inlinable + public static var `class`: LayoutKind { + LayoutKind(0x0) + } + } +} + +@available(SwiftStdlib 5.9, *) +extension GenericSignature.LayoutKind: Equatable { + @available(SwiftStdlib 5.9, *) + @inlinable + public static func ==( + lhs: GenericSignature.LayoutKind, + rhs: GenericSignature.LayoutKind + ) -> Bool { + lhs.value == rhs.value + } +} + @available(SwiftStdlib 5.9, *) extension GenericSignature { @frozen @@ -542,7 +577,22 @@ extension GenericSignature.RequirementDescriptor { Kind(value: 0x1F) } } - +} + +@available(SwiftStdlib 5.9, *) +extension GenericSignature.RequirementDescriptor.Kind: Equatable { + @available(SwiftStdlib 5.9, *) + @inlinable + public static func ==( + lhs: GenericSignature.RequirementDescriptor.Kind, + rhs: GenericSignature.RequirementDescriptor.Kind + ) -> Bool { + lhs.value == rhs.value + } +} + +@available(SwiftStdlib 5.9, *) +extension GenericSignature.RequirementDescriptor { @frozen public struct Flags { @usableFromInline diff --git a/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/GenericSignature.swift b/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/GenericSignature.swift index 60bfe29a52ed3..9626905195de4 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/GenericSignature.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/GenericSignature.swift @@ -17,7 +17,7 @@ public struct GenericSignature { public let header: Header public let parameters: BufferView public let requirements: IndirectBufferView - + @inlinable init( _ header: Header, @@ -41,15 +41,15 @@ extension GenericSignature { numberOfKeyArguments: UInt16, numberOfExtraArguments: UInt16 ) - + @usableFromInline let storage: Storage - + @inlinable public var numberOfParameters: Int { Int(truncatingIfNeeded: storage.numberOfParameters) } - + public var numberOfRequirements: Int { Int(truncatingIfNeeded: storage.numberOfRequirements) } @@ -66,14 +66,15 @@ extension GenericSignature { // This field is a union which represents the type of requirement // that this parameter is constrained to. It is represented by the following: // 1. Same type requirement (RelativeDirectPointer) - // 2. Protocol requirement (RelativeIndirectablePointerIntPair) - // 3. Conformance requirement (RelativeIndirectablePointer) - // 4. Layout requirement (LayoutKind) + // 2. Base class requirement (RelativeDirectPointer) + // 3. Protocol requirement (RelativeIndirectablePointerIntPair) + // 4. Conformance requirement (RelativeIndirectablePointer) + // 5. Layout requirement (LayoutKind) requirement: Int32 ) - + public let ptr: UnsafeRawPointer - + @inlinable public init(_ ptr: UnsafeRawPointer) { self.ptr = ptr @@ -81,25 +82,207 @@ extension GenericSignature { } } +@available(SwiftStdlib 5.9, *) +extension GenericSignature.RequirementDescriptor { + @available(SwiftStdlib 5.9, *) + @inlinable + public var flags: Flags { + layout.flags + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public var parameter: MangledTypeReference { + MangledTypeReference(address(for: \.parameter)) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public var `protocol`: ProtocolDescriptor { + let addr = address(for: \.requirement) + .relativeIndirectablePointerIntPairAddress( + as: ProtocolDescriptor.self, + and: UInt8.self + ) + + return ProtocolDescriptor(addr) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public var sameType: MangledTypeReference { + MangledTypeReference( + address(for: \.requirement).relativeDirectAddress(as: CChar.self) + ) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public var layoutKind: GenericSignature.LayoutKind { + address(for: \.requirement).loadUnaligned( + as: GenericSignature.LayoutKind.self + ) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public var baseClass: MangledTypeReference { + sameType + } +} + +@available(SwiftStdlib 5.9, *) +extension GenericSignature.RequirementDescriptor { + @available(SwiftStdlib 5.9, *) + internal func parameterType( + in context: ContextDescriptor, + with argPtr: UnsafeRawPointer + ) -> Any.Type? { + _getTypeByMangledNameInContext( + UnsafePointer(parameter.ptr._rawValue), + UInt(parameter.length), + genericContext: context.ptr, + genericArguments: argPtr + ) + } + + @available(SwiftStdlib 5.9, *) + public func checkProtocolConformance( + in context: ContextDescriptor, + with argPtr: UnsafeRawPointer + ) -> WitnessTable? { + guard flags.kind == .protocol else { + return nil + } + + guard let parameterTy = parameterType(in: context, with: argPtr) else { + return nil + } + + return swift_conformsToProtocol(Metadata(parameterTy), `protocol`) + } + + // This function handles 2 cases: + // + // 1. Associated type same type constraints. E.g. 'T.Element == X' + // 2. Same type constraints to other generic parameters. E.g. 'T == U' + // + // In the first case, we will have both a parameter type and same type type + // be resolved. Simply check that they are the same and move on. For the + // second case, either the parameter type or the same type type will not be + // resolved because this type of constraint makes one of the parameters + // non-key. For example, 'T == U' this constraint causes 'U' to be a non-key + // argument, but 'T' still is however. In the same vein, 'U == [T]' causes + // 'U' to be our key argument now. + // + // Same type constraints that look like 'T == String' cause 'T' to be a purely + // syntactical generic parameter, thus it is resolved ealier in the stack and + // not provided to our argument pointer. + @available(SwiftStdlib 5.9, *) + public func checkSameType( + in context: ContextDescriptor, + with argPtr: UnsafeRawPointer + ) -> Bool { + guard flags.kind == .sameType else { + return false + } + + guard let parameterTy = parameterType(in: context, with: argPtr) else { + // Because of the 2nd case, there might not be a resolved parameter type + // or same type type, so we have to return true to indicate that there is + // no constraint to solve. + return true + } + + let sameTypeTy = _getTypeByMangledNameInContext( + UnsafePointer(sameType.ptr._rawValue), + UInt(sameType.length), + genericContext: context.ptr, + genericArguments: argPtr + ) + + guard let sameTypeTy = sameTypeTy else { + // Because of the 2nd case, there might not be a resolved parameter type + // or same type type, so we have to return true to indicate that there is + // no constraint to solve. + return true + } + + return parameterTy == sameTypeTy + } + + @available(SwiftStdlib 5.9, *) + public func checkLayout( + in context: ContextDescriptor, + with argPtr: UnsafeRawPointer + ) -> Bool { + guard flags.kind == .layout else { + return false + } + + guard let parameterTy = parameterType(in: context, with: argPtr) else { + return false + } + + switch layoutKind { + case .class: + return Metadata(parameterTy).isAnyClass + + // There is currently only class layouts supported, but in case we somehow + // find something else return false. + default: + return false + } + } + + @available(SwiftStdlib 5.9, *) + public func checkBaseClass( + in context: ContextDescriptor, + with argPtr: UnsafeRawPointer + ) -> Bool { + guard flags.kind == .baseClass else { + return false + } + + guard let parameterTy = parameterType(in: context, with: argPtr), + Metadata(parameterTy).isAnyClass else { + return false + } + + let baseClassTy = _getTypeByMangledNameInContext( + UnsafePointer(baseClass.ptr._rawValue), + UInt(baseClass.length), + genericContext: context.ptr, + genericArguments: argPtr + ) + + guard let baseClassTy = baseClassTy else { + return false + } + + return _isSubclass(Metadata(parameterTy), Metadata(baseClassTy)) + } +} + @available(SwiftStdlib 5.9, *) @inlinable func getGenericSignature(at address: UnsafeRawPointer) -> GenericSignature { var address = address - + let header = address.loadUnaligned(as: GenericSignature.Header.self) address += MemoryLayout.size - + let parameters = BufferView( start: address, count: header.numberOfParameters ) // This accounts for padding address += (-header.numberOfParameters & 0x3) + header.numberOfParameters - + let requirements = IndirectBufferView( start: address, count: header.numberOfRequirements ) - + return GenericSignature(header, parameters, requirements) } diff --git a/stdlib/public/Reflection/Sources/_Runtime/Functions.swift b/stdlib/public/Reflection/Sources/_Runtime/Functions.swift index a7714a705ba39..ee360a6e2a63c 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Functions.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Functions.swift @@ -38,6 +38,10 @@ public struct BoxPair { @_silgen_name("swift_allocBox") public func swift_allocBox(_: Metadata) -> BoxPair +@available(SwiftStdlib 5.9, *) +@_silgen_name("_swift_class_isSubclass") +internal func _isSubclass(_: Metadata, _: Metadata) -> Bool + @available(SwiftStdlib 5.9, *) @inlinable public func swift_conformsToProtocol( diff --git a/stdlib/public/Reflection/Sources/_Runtime/Metadata/ClassMetadata.swift b/stdlib/public/Reflection/Sources/_Runtime/Metadata/ClassMetadata.swift index f85d024a2d2bd..e3fa66dfb2874 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Metadata/ClassMetadata.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Metadata/ClassMetadata.swift @@ -14,7 +14,7 @@ import Swift @available(SwiftStdlib 5.9, *) @frozen public struct ClassMetadata: PublicLayout { -#if canImport(ObjectiveC) +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) public typealias Layout = ( base: Metadata.Layout, superclass: Metadata?, diff --git a/stdlib/public/Reflection/Sources/_Runtime/Metadata/Metadata.swift b/stdlib/public/Reflection/Sources/_Runtime/Metadata/Metadata.swift index 966d27adac2cd..8e39780390bec 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Metadata/Metadata.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Metadata/Metadata.swift @@ -85,6 +85,23 @@ extension Metadata { } } +@available(SwiftStdlib 5.9, *) +extension Metadata { + @available(SwiftStdlib 5.9, *) + internal var isAnyClass: Bool { + switch kind { + case .class, + .objcClassWrapper, + .foreignClass, + .foreignReferenceType: + return true + + default: + return false + } + } +} + //===----------------------------------------------------------------------===// // Stdlib conformances //===----------------------------------------------------------------------===// diff --git a/stdlib/public/Reflection/Sources/_Runtime/Metadata/MetadataAccessFunction.swift b/stdlib/public/Reflection/Sources/_Runtime/Metadata/MetadataAccessFunction.swift index 8efc8881ce470..ea3dbd2d8c0f3 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Metadata/MetadataAccessFunction.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Metadata/MetadataAccessFunction.swift @@ -13,98 +13,193 @@ import Swift @available(SwiftStdlib 5.9, *) extension Metadata { + @available(SwiftStdlib 5.9, *) @frozen public struct AccessFunction { @usableFromInline let ptr: UnsafeRawPointer - + + @available(SwiftStdlib 5.9, *) @inlinable init(_ ptr: UnsafeRawPointer) { self.ptr = ptr } - - // MARK: Access Function 0 Args - + +//===----------------------------------------------------------------------===// +// 0 Arguments +//===----------------------------------------------------------------------===// + @usableFromInline typealias AccessFn0 = @convention(thin) ( Request ) -> Response - + + @available(SwiftStdlib 5.9, *) @inlinable public func callAsFunction(_ request: Request) -> Metadata { let fn = unsafeBitCast(ptr, to: AccessFn0.self) - + return fn(request).metadata } - - // MARK: Access Function 1 Arg - + +//===----------------------------------------------------------------------===// +// 1 Argument +//===----------------------------------------------------------------------===// + @usableFromInline typealias AccessFn1 = @convention(thin) ( Request, - Metadata + UnsafeRawPointer ) -> Response - + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ arg0: any Any.Type + ) -> Metadata { + self(request, Metadata(arg0)) + } + + @available(SwiftStdlib 5.9, *) @inlinable public func callAsFunction( _ request: Request, _ arg0: Metadata + ) -> Metadata { + self(request, arg0.ptr) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ arg0: UnsafeRawPointer ) -> Metadata { let fn = unsafeBitCast(ptr, to: AccessFn1.self) - + return fn(request, arg0).metadata } - - // MARK: Access Function 2 Args - + +//===----------------------------------------------------------------------===// +// 2 Arguments +//===----------------------------------------------------------------------===// + @usableFromInline typealias AccessFn2 = @convention(thin) ( Request, - Metadata, - Metadata + UnsafeRawPointer, + UnsafeRawPointer ) -> Response - + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ arg0: any Any.Type, + _ arg1: any Any.Type + ) -> Metadata { + self(request, Metadata(arg0), Metadata(arg1)) + } + + @available(SwiftStdlib 5.9, *) @inlinable public func callAsFunction( _ request: Request, _ arg0: Metadata, _ arg1: Metadata + ) -> Metadata { + self(request, arg0.ptr, arg1.ptr) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ arg0: UnsafeRawPointer, + _ arg1: UnsafeRawPointer ) -> Metadata { let fn = unsafeBitCast(ptr, to: AccessFn2.self) - + return fn(request, arg0, arg1).metadata } - - // MARK: Access Function 3 Args - + +//===----------------------------------------------------------------------===// +// 3 Arguments +//===----------------------------------------------------------------------===// + @usableFromInline typealias AccessFn3 = @convention(thin) ( Request, - Metadata, - Metadata, - Metadata + UnsafeRawPointer, + UnsafeRawPointer, + UnsafeRawPointer ) -> Response - + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ arg0: any Any.Type, + _ arg1: any Any.Type, + _ arg2: any Any.Type + ) -> Metadata { + self(request, Metadata(arg0), Metadata(arg1), Metadata(arg2)) + } + + @available(SwiftStdlib 5.9, *) @inlinable public func callAsFunction( _ request: Request, _ arg0: Metadata, _ arg1: Metadata, _ arg2: Metadata + ) -> Metadata { + self(request, arg0.ptr, arg1.ptr, arg2.ptr) + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ arg0: UnsafeRawPointer, + _ arg1: UnsafeRawPointer, + _ arg2: UnsafeRawPointer ) -> Metadata { let fn = unsafeBitCast(ptr, to: AccessFn3.self) - + return fn(request, arg0, arg1, arg2).metadata } - - // MARK: Access Function Many Args + +//===----------------------------------------------------------------------===// +// Many Arguments +//===----------------------------------------------------------------------===// @usableFromInline typealias AccessFnMany = @convention(thin) ( Request, - UnsafePointer + UnsafePointer ) -> Response - + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ args: [any Any.Type] + ) -> Metadata { + let fn = unsafeBitCast(ptr, to: AccessFnMany.self) + + return args.withUnsafeBufferPointer { + fn( + request, + UnsafePointer( + $0.baseAddress.unsafelyUnwrapped._rawValue + ) + ).metadata + } + } + + @available(SwiftStdlib 5.9, *) @inlinable public func callAsFunction( _ request: Request, @@ -113,8 +208,35 @@ extension Metadata { let fn = unsafeBitCast(ptr, to: AccessFnMany.self) return args.withUnsafeBufferPointer { - fn(request, $0.baseAddress.unsafelyUnwrapped).metadata + fn( + request, + UnsafePointer( + $0.baseAddress.unsafelyUnwrapped._rawValue + ) + ).metadata } } + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ args: [UnsafeRawPointer] + ) -> Metadata { + args.withUnsafeBufferPointer { + self(request, $0) + } + } + + @available(SwiftStdlib 5.9, *) + @inlinable + public func callAsFunction( + _ request: Request, + _ args: UnsafeBufferPointer + ) -> Metadata { + let fn = unsafeBitCast(ptr, to: AccessFnMany.self) + + return fn(request, args.baseAddress.unsafelyUnwrapped).metadata + } } } diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/MangledTypeReference.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/MangledTypeReference.swift index b515f41c420f7..2a9ef02330e52 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/MangledTypeReference.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/MangledTypeReference.swift @@ -25,6 +25,13 @@ public struct MangledTypeReference { @available(SwiftStdlib 5.9, *) extension MangledTypeReference { + @available(SwiftStdlib 5.9, *) + @inlinable + public var length: Int { + getSymbolicMangledNameLength(ptr) + } + + @available(SwiftStdlib 5.9, *) @inlinable var standardSubstitution: Any.Type? { let byte1 = ptr.loadUnaligned(fromByteOffset: 1, as: UInt8.self) diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift index de4b66dde8313..436fffe898b84 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift @@ -11,35 +11,41 @@ import Swift -@available(SwiftStdlib 5.9, *) -@inlinable -public func unsafeBitCast(_ x: T, to type: U.Type = U.self) -> U { +@_alwaysEmitIntoClient +func unsafeBitCast(_ x: T, to type: U.Type = U.self) -> U { Swift.unsafeBitCast(x, to: type) } extension UnsafePointer { - @inlinable + @_alwaysEmitIntoClient var raw: UnsafeRawPointer { UnsafeRawPointer(self) } } extension UnsafeMutablePointer { - @inlinable + @_alwaysEmitIntoClient var raw: UnsafeMutableRawPointer { UnsafeMutableRawPointer(self) } } +extension UnsafeBufferPointer { + @_alwaysEmitIntoClient + var raw: UnsafeRawBufferPointer { + UnsafeRawBufferPointer(self) + } +} + extension UnsafeRawPointer { - @inlinable + @_alwaysEmitIntoClient var bitPattern: UInt64 { UInt64(truncatingIfNeeded: UInt(bitPattern: self)) } } extension UInt64 { - @inlinable + @_alwaysEmitIntoClient var rawPointer: UnsafeRawPointer { let pointer = UnsafeRawPointer(bitPattern: UInt(truncatingIfNeeded: self)) @@ -48,15 +54,14 @@ extension UInt64 { } extension UnsafeRawPointer { - @inlinable + @_alwaysEmitIntoClient var mutable: UnsafeMutableRawPointer { UnsafeMutableRawPointer(mutating: self) } } extension UnsafeRawPointer { - @inlinable - @inline(__always) + @_alwaysEmitIntoClient var binaryString: String { // let length = strlen(UnsafePointer(_rawValue)) // @@ -71,12 +76,13 @@ extension UnsafeRawPointer { } extension UnsafeRawPointer { - @inlinable + @_alwaysEmitIntoClient func offset(of count: Int) -> UnsafeRawPointer { advanced(by: count * MemoryLayout.size) } } +@_alwaysEmitIntoClient func getSymbolicMangledNameLength(_ base: UnsafeRawPointer) -> Int { var end = base while let current = Optional(end.load(as: UInt8.self)), current != 0 { diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointerIntPair.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointerIntPair.swift index e08642a80e153..c6cebeecca055 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointerIntPair.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointerIntPair.swift @@ -55,3 +55,18 @@ public struct RelativeIndirectablePointerIntPair< } } } + +extension UnsafeRawPointer { + @available(SwiftStdlib 5.9, *) + @inlinable + public func relativeIndirectablePointerIntPairAddress( + as type: T.Type, + and type2: U.Type + ) -> UnsafeRawPointer { + let relativePointer = RelativeIndirectablePointerIntPair( + offset: loadUnaligned(as: Int32.self) + ) + + return relativePointer.address(from: self) + } +} diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index 4c15133cd53b6..6c13672e519bf 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -1273,6 +1273,13 @@ static bool isSubclass(const Metadata *subclass, const Metadata *superclass) { }); } +SWIFT_CC(swift) +SWIFT_RUNTIME_STDLIB_SPI +bool swift::_swift_class_isSubclass(const Metadata *subclass, + const Metadata *superclass) { + return isSubclass(subclass, superclass); +} + llvm::Optional swift::_checkGenericRequirements( llvm::ArrayRef requirements, llvm::SmallVectorImpl &extraArguments, diff --git a/test/stdlib/Reflection/PartialType.swift b/test/stdlib/Reflection/PartialType.swift new file mode 100644 index 0000000000000..1a99e915d818d --- /dev/null +++ b/test/stdlib/Reflection/PartialType.swift @@ -0,0 +1,225 @@ +// RUN: %target-run-simple-swift +// REQUIRES: executable_test + +import StdlibUnittest +import _Runtime +import Reflection + +let suite = TestSuite("PartialType") + +//===----------------------------------------------------------------------===// +// Basic +//===----------------------------------------------------------------------===// + +struct TypeWhoDoesNotConformToHashable {} + +if #available(SwiftStdlib 5.9, *) { + suite.test("basic creation") { + let intPTy = Type(Int.self).partial! + let int = intPTy.create() + expectNotNil(int) + expectEqual(int!.swiftType, Int.self) + + let arrPTy = Type([Void].self).partial! + let intArr = arrPTy.create(with: Int.self) + expectNotNil(intArr) + expectEqual(intArr!.swiftType, [Int].self) + + let dictPTy = Type([AnyHashable: Any].self).partial! + let goodDict = dictPTy.create(with: String.self, Int.self) + expectNotNil(goodDict) + expectEqual(goodDict!.swiftType, [String: Int].self) + + let badDict = dictPTy.create(with: TypeWhoDoesNotConformToHashable.self, Int.self) + expectNil(badDict) + } +} + +//===----------------------------------------------------------------------===// +// Protocol conformance constraints +//===----------------------------------------------------------------------===// + +protocol MyProto {} + +extension Int: MyProto {} +extension Bool: MyProto {} + +struct ProtoGeneric {} + +struct ProtoGeneric2 where T.Element: Hashable {} + +if #available(SwiftStdlib 5.9, *) { + suite.test("protocol conformance constraint creation") { + let protoGenericPTy = Type(ProtoGeneric.self).partial! + + let a = protoGenericPTy.create(with: Int.self) + expectNotNil(a) + expectEqual(a!.swiftType, ProtoGeneric.self) + + let b = protoGenericPTy.create(with: String.self) + expectNil(b) + + let protoGeneric2PTy = Type(ProtoGeneric2<[String]>.self).partial! + + let c = protoGeneric2PTy.create(with: [Int].self) + expectNotNil(c) + expectEqual(c!.swiftType, ProtoGeneric2<[Int]>.self) + + let d = protoGeneric2PTy.create(with: [TypeWhoDoesNotConformToHashable].self) + expectNil(d) + } +} + +//===----------------------------------------------------------------------===// +// Same type constraints +//===----------------------------------------------------------------------===// + +struct SameTypeGeneric1 {} + +extension SameTypeGeneric1 where T == String { + struct Nested {} +} + +struct SameTypeGeneric2 where T.Element == Int {} + +struct SameTypeGeneric3 {} + +extension SameTypeGeneric3 where T == U? { + struct Nested {} +} + +if #available(SwiftStdlib 5.9, *) { + suite.test("same type constraint creation") { + // 1. Concrete same types + + let nestedPTy = Type(SameTypeGeneric1.Nested.self).partial! + + let a = nestedPTy.create() + expectNotNil(a) + expectEqual(a!.swiftType, SameTypeGeneric1.Nested.self) + + let b = nestedPTy.create(with: Int.self) + expectNil(b) + + let c = nestedPTy.create(with: String.self) + expectNil(c) + + // 2. Associated type same types + + let stg2PTy = Type(SameTypeGeneric2>.self).partial! + + let d = stg2PTy.create(with: [Int].self) + expectNotNil(d) + expectEqual(d!.swiftType, SameTypeGeneric2<[Int]>.self) + + let e = stg2PTy.create(with: Bool.self) + expectNil(e) + + let f = stg2PTy.create(with: [String].self) + expectNil(f) + + // 3. Generic same types + + let nested2PTy = Type(SameTypeGeneric3.Nested.self).partial! + + let g = nested2PTy.create(with: Int.self) + expectNotNil(g) + expectEqual(g!.swiftType, SameTypeGeneric3.Nested.self) + + let h = nested2PTy.create() + expectNil(h) + + let i = nested2PTy.create(with: Int?.self) + expectNotNil(i) + expectEqual(i!.swiftType, SameTypeGeneric3.Nested.self) + } +} + +//===----------------------------------------------------------------------===// +// Layout constraints +//===----------------------------------------------------------------------===// + +class LayoutClass1 {} +class LayoutClass2 {} + +struct LayoutGeneric {} + +if #available(SwiftStdlib 5.9, *) { + suite.test("layout constraint creation") { + let layoutGenericPTy = Type(LayoutGeneric.self).partial! + + let a = layoutGenericPTy.create(with: Int.self) + expectNil(a) + + let b = layoutGenericPTy.create(with: [Int].self) + expectNil(b) + + let c = layoutGenericPTy.create() + expectNil(c) + + let d = layoutGenericPTy.create(with: LayoutClass2.self) + expectNotNil(d) + expectEqual(d!.swiftType, LayoutGeneric.self) + } +} + +//===----------------------------------------------------------------------===// +// Base class constraints +//===----------------------------------------------------------------------===// + +class UnrelatedClass {} + +class BaseClass1 {} +class SubClass1: BaseClass1 {} +struct BaseClassGeneric1 {} + +class BaseClass2 {} +class SubClass2: BaseClass2 {} +struct BaseClassGeneric2> {} +struct Weird {} + +extension Weird where T: Equatable { + class SubSubClass2: BaseClass2 {} +} + +if #available(SwiftStdlib 5.9, *) { + suite.test("base class constraint creation") { + let one = Type(BaseClassGeneric1.self).partial! + + let a = one.create(with: Int.self) + expectNil(a) + + let b = one.create(with: SubClass1.self) + expectNotNil(b) + expectEqual(b!.swiftType, BaseClassGeneric1.self) + + let c = one.create(with: BaseClass1.self) + expectNotNil(c) + expectEqual(c!.swiftType, BaseClassGeneric1.self) + + let two = Type(BaseClassGeneric2>.self).partial! + + let d = two.create(with: Int.self) + expectNil(d) + + let e = two.create(with: BaseClass2.self) + expectNil(e) + + let f = two.create(with: BaseClass2.self) + expectNotNil(f) + expectEqual(f!.swiftType, BaseClassGeneric2>.self) + + let g = two.create(with: SubClass2.self) + expectNotNil(g) + expectEqual(g!.swiftType, BaseClassGeneric2.self) + + let h = two.create(with: Weird.SubSubClass2.self) + expectNil(h) + + let i = two.create(with: Weird.SubSubClass2.self) + expectNotNil(i) + expectEqual(i!.swiftType, BaseClassGeneric2.SubSubClass2>.self) + } +} + +runAllTests() From e0bf2ff85401275a05ecd5ee9c09004e19436862 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 8 Feb 2023 10:14:29 -0800 Subject: [PATCH 36/98] [SIL/DI] NFC: Remove TypeWrappers feature functionality --- include/swift/SIL/SILBuilder.h | 31 +- include/swift/SIL/SILCloner.h | 2 +- include/swift/SIL/SILInstruction.h | 9 +- lib/SIL/IR/SILInstructions.cpp | 6 +- lib/SIL/IR/SILPrinter.cpp | 24 +- lib/SIL/Parser/ParseSIL.cpp | 55 +-- lib/SIL/Verifier/SILVerifier.cpp | 19 +- lib/SILGen/SILGen.cpp | 18 - lib/SILGen/SILGenDecl.cpp | 63 --- lib/SILGen/SILGenLValue.cpp | 126 +----- lib/SILGen/SILGenType.cpp | 8 - .../Mandatory/DIMemoryUseCollector.cpp | 56 +-- .../Mandatory/DIMemoryUseCollector.h | 14 - .../Mandatory/DefiniteInitialization.cpp | 362 ------------------ .../Mandatory/RawSILInstLowering.cpp | 63 +-- test/SILGen/objc_properties.swift | 4 +- test/SILGen/property_wrapper_local.swift | 4 +- test/SILGen/property_wrappers.swift | 2 +- test/SILGen/resilient_assign_by_wrapper.swift | 8 +- test/SILOptimizer/raw_sil_inst_lowering.sil | 10 +- tools/sil-opt/SILOpt.cpp | 8 - 21 files changed, 76 insertions(+), 816 deletions(-) diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 5f5d7be45ec90..bf0d1803122e2 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -888,30 +888,13 @@ class SILBuilder { Qualifier)); } - AssignByWrapperInst * - createAssignByPropertyWrapper(SILLocation Loc, SILValue Src, SILValue Dest, - SILValue Initializer, SILValue Setter, - AssignByWrapperInst::Mode mode) { - return createAssignByWrapper( - Loc, AssignByWrapperInst::Originator::PropertyWrapper, Src, Dest, - Initializer, Setter, mode); - } - - AssignByWrapperInst * - createAssignByTypeWrapper(SILLocation Loc, SILValue Src, SILValue Dest, - SILValue Setter, AssignByWrapperInst::Mode mode) { - return createAssignByWrapper( - Loc, AssignByWrapperInst::Originator::TypeWrapper, Src, Dest, - SILUndef::get(Dest->getType(), getModule()), Setter, mode); - } - - AssignByWrapperInst * - createAssignByWrapper(SILLocation Loc, AssignByWrapperInst::Originator origin, - SILValue Src, SILValue Dest, SILValue Initializer, - SILValue Setter, AssignByWrapperInst::Mode mode) { - return insert(new (getModule()) - AssignByWrapperInst(getSILDebugLocation(Loc), origin, Src, - Dest, Initializer, Setter, mode)); + AssignByWrapperInst *createAssignByWrapper(SILLocation Loc, SILValue Src, + SILValue Dest, + SILValue Initializer, + SILValue Setter, + AssignByWrapperInst::Mode mode) { + return insert(new (getModule()) AssignByWrapperInst( + getSILDebugLocation(Loc), Src, Dest, Initializer, Setter, mode)); } StoreBorrowInst *createStoreBorrow(SILLocation Loc, SILValue Src, diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index 86d9feca83a99..2aab615882572 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1272,7 +1272,7 @@ void SILCloner::visitAssignByWrapperInst(AssignByWrapperInst *Inst) { getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope())); recordClonedInstruction( Inst, getBuilder().createAssignByWrapper( - getOpLocation(Inst->getLoc()), Inst->getOriginator(), + getOpLocation(Inst->getLoc()), getOpValue(Inst->getSrc()), getOpValue(Inst->getDest()), getOpValue(Inst->getInitializer()), getOpValue(Inst->getSetter()), Inst->getMode())); diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 4c2b67cb64fcc..e1cff7a280f99 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -4782,9 +4782,6 @@ class AssignByWrapperInst USE_SHARED_UINT8; public: - /// The kind of a wrapper that is being applied. - enum class Originator : uint8_t { TypeWrapper, PropertyWrapper }; - enum Mode { /// The mode is not decided yet (by DefiniteInitialization). Unknown, @@ -4804,9 +4801,7 @@ class AssignByWrapperInst }; private: - Originator originator; - - AssignByWrapperInst(SILDebugLocation DebugLoc, Originator origin, + AssignByWrapperInst(SILDebugLocation DebugLoc, SILValue Src, SILValue Dest, SILValue Initializer, SILValue Setter, Mode mode); @@ -4814,8 +4809,6 @@ class AssignByWrapperInst SILValue getInitializer() { return Operands[2].get(); } SILValue getSetter() { return Operands[3].get(); } - Originator getOriginator() const { return originator; } - Mode getMode() const { return Mode(sharedUInt8().AssignByWrapperInst.mode); } diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index ccd21dd398349..68cec3576be5c 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -1234,13 +1234,11 @@ AssignInst::AssignInst(SILDebugLocation Loc, SILValue Src, SILValue Dest, } AssignByWrapperInst::AssignByWrapperInst(SILDebugLocation Loc, - AssignByWrapperInst::Originator origin, SILValue Src, SILValue Dest, SILValue Initializer, SILValue Setter, AssignByWrapperInst::Mode mode) - : AssignInstBase(Loc, Src, Dest, Initializer, Setter), originator(origin) { - assert(Initializer->getType().is() || - (isa(Initializer) && originator == Originator::TypeWrapper)); + : AssignInstBase(Loc, Src, Dest, Initializer, Setter) { + assert(Initializer->getType().is()); sharedUInt8().AssignByWrapperInst.mode = uint8_t(mode); } diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 7da70173ea009..4071b63c882ca 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -1714,20 +1714,6 @@ class SILPrinter : public SILInstructionVisitor { } void visitAssignByWrapperInst(AssignByWrapperInst *AI) { - { - *this << "origin "; - - switch (AI->getOriginator()) { - case AssignByWrapperInst::Originator::TypeWrapper: - *this << "type_wrapper"; - break; - case AssignByWrapperInst::Originator::PropertyWrapper: - *this << "property_wrapper"; - } - - *this << ", "; - } - *this << getIDAndType(AI->getSrc()) << " to "; switch (AI->getMode()) { case AssignByWrapperInst::Unknown: @@ -1742,13 +1728,9 @@ class SILPrinter : public SILInstructionVisitor { *this << "[assign_wrapped_value] "; break; } - - *this << getIDAndType(AI->getDest()); - - if (AI->getOriginator() == AssignByWrapperInst::Originator::PropertyWrapper) - *this << ", init " << getIDAndType(AI->getInitializer()); - - *this << ", set " << getIDAndType(AI->getSetter()); + *this << getIDAndType(AI->getDest()) + << ", init " << getIDAndType(AI->getInitializer()) + << ", set " << getIDAndType(AI->getSetter()); } void visitMarkUninitializedInst(MarkUninitializedInst *MU) { diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index b94367d5aee9b..c6558121b4504 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -2248,35 +2248,6 @@ bool SILParser::parseSILDebugLocation(SILLocation &L, SILBuilder &B) { return false; } -static bool -parseAssignByWrapperOriginator(AssignByWrapperInst::Originator &Result, - SILParser &P) { - if (P.parseVerbatim("origin")) - return true; - - SourceLoc loc; - Identifier origin; - - if (P.parseSILIdentifier(origin, loc, diag::expected_in_attribute_list)) - return true; - - // Then try to parse one of our other initialization kinds. We do not support - // parsing unknown here so we use that as our fail value. - auto Tmp = - llvm::StringSwitch>( - origin.str()) - .Case("type_wrapper", AssignByWrapperInst::Originator::TypeWrapper) - .Case("property_wrapper", - AssignByWrapperInst::Originator::PropertyWrapper) - .Default(None); - - if (!Tmp) - return true; - - Result = *Tmp; - return false; -} - static bool parseAssignByWrapperMode(AssignByWrapperInst::Mode &Result, SILParser &P) { StringRef Str; @@ -4406,30 +4377,14 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, case SILInstructionKind::AssignByWrapperInst: { SILValue Src, DestAddr, InitFn, SetFn; SourceLoc DestLoc; - AssignByWrapperInst::Originator originator; AssignByWrapperInst::Mode mode; - if (parseAssignByWrapperOriginator(originator, *this)) - return true; - - if (P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",")) - return true; - if (parseTypedValueRef(Src, B) || parseVerbatim("to") || parseAssignByWrapperMode(mode, *this) || - parseTypedValueRef(DestAddr, DestLoc, B)) - return true; - - if (originator == AssignByWrapperInst::Originator::PropertyWrapper) { - if (P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || - parseVerbatim("init") || parseTypedValueRef(InitFn, B)) - return true; - } else { - assert(originator == AssignByWrapperInst::Originator::TypeWrapper); - InitFn = SILUndef::get(DestAddr->getType(), B.getModule()); - } - - if (P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseTypedValueRef(DestAddr, DestLoc, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || + parseVerbatim("init") || parseTypedValueRef(InitFn, B) || + P.parseToken(tok::comma, diag::expected_tok_in_sil_instr, ",") || parseVerbatim("set") || parseTypedValueRef(SetFn, B) || parseSILDebugLocation(InstLoc, B)) return true; @@ -4440,7 +4395,7 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, return true; } - ResultVal = B.createAssignByWrapper(InstLoc, originator, Src, DestAddr, + ResultVal = B.createAssignByWrapper(InstLoc, Src, DestAddr, InitFn, SetFn, mode); break; } diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 5f7335ca3e1a7..15567142e006f 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -2571,13 +2571,11 @@ class SILVerifier : public SILVerifierBase { "assign instruction can only exist in raw SIL"); require(Dest->getType().isAddress(), "Must store to an address dest"); - if (AI->getOriginator() == - AssignByWrapperInst::Originator::PropertyWrapper) { - SILValue initFn = AI->getInitializer(); - CanSILFunctionType initTy = initFn->getType().castTo(); - SILFunctionConventions initConv(initTy, AI->getModule()); - checkAssignByWrapperArgs(Src->getType(), initConv); - switch (initConv.getNumIndirectSILResults()) { + SILValue initFn = AI->getInitializer(); + CanSILFunctionType initTy = initFn->getType().castTo(); + SILFunctionConventions initConv(initTy, AI->getModule()); + checkAssignByWrapperArgs(Src->getType(), initConv); + switch (initConv.getNumIndirectSILResults()) { case 0: require(initConv.getNumDirectSILResults() == 1, "wrong number of init function results"); @@ -2598,13 +2596,6 @@ class SILVerifier : public SILVerifierBase { break; default: require(false, "wrong number of indirect init function results"); - } - } else { - require(AI->getOriginator() == - AssignByWrapperInst::Originator::TypeWrapper, - "wrong originator"); - require(isa(AI->getInitializer()), - "assignment via type wrapper does not have initializer"); } SILValue setterFn = AI->getSetter(); diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index b887d65798a23..47a55d5dab114 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -1448,24 +1448,6 @@ void SILGenModule::emitConstructor(ConstructorDecl *decl) { SILDeclRef constant(decl); DeclContext *declCtx = decl->getDeclContext(); - // Make sure that default & memberwise initializers of $Storage - // in a type wrapped type are always emitted because they would - // later be used to initialize its `$storage` property. - if (auto *SD = declCtx->getSelfStructDecl()) { - auto &ctx = SD->getASTContext(); - if (SD->getName() == ctx.Id_TypeWrapperStorage && - (decl->isMemberwiseInitializer() || - decl == SD->getDefaultInitializer())) { -#ifndef NDEBUG - auto *wrapped = SD->getDeclContext()->getSelfNominalTypeDecl(); - assert(wrapped->hasTypeWrapper()); -#endif - - emitFunctionDefinition(constant, getFunction(constant, ForDefinition)); - return; - } - } - if (declCtx->getSelfClassDecl()) { // Designated initializers for classes, as well as @objc convenience // initializers, have separate entry points for allocation and diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index da2ca9f1b6e8c..9b9a008f5ed5f 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -1385,69 +1385,6 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD, bool isLocalVar = singleVar && singleVar->getDeclContext()->isLocalContext(); emitInitializer(Init, singleVar, isLocalVar, initialization); - } else if (singleVar && - singleVar->isTypeWrapperLocalStorageForInitializer()) { - // If any of the type wrapper managed properties had default initializers - // we need to emit them as assignments to `_storage` elements as part - // of its initialization. - - auto storageVarType = singleVar->getType()->castTo(); - auto *wrappedDecl = cast( - singleVar->getDeclContext()->getInnermostTypeContext()); - - SmallVector, 2> fieldsToInitialize; - fieldsToInitialize.resize_for_overwrite(storageVarType->getNumElements()); - - unsigned numInitializable = 0; - for (auto member : wrappedDecl->getMembers()) { - auto *PBD = dyn_cast(member); - // Check every member that is managed by the type wrapper. - if (!(PBD && PBD->getSingleVar() && - PBD->getSingleVar()->isAccessedViaTypeWrapper())) - continue; - - auto *field = PBD->getSingleVar(); - auto fieldNo = storageVarType->getNamedElementId(field->getName()); - - if (auto *initExpr = PBD->getInit(/*index=*/0)) { - fieldsToInitialize[fieldNo] = {PBD->getSingleVar(), initExpr}; - ++numInitializable; - } - } - - if (numInitializable == 0) { - initialization->finishUninitialized(*this); - return; - } - - // If there are any initializable fields, let's split _storage into - // element initializers and emit initializations for individual fields. - - assert(initialization->canSplitIntoTupleElements()); - - SmallVector scratch; - auto fieldInits = initialization->splitIntoTupleElements( - *this, PBD, storageVarType->getCanonicalType(), scratch); - - for (unsigned i : range(fieldInits.size())) { - VarDecl *field; - Expr *initExpr; - - std::tie(field, initExpr) = fieldsToInitialize[i]; - - auto &fieldInit = fieldInits[i]; - if (initExpr) { - // If there is wrapped value expression, we have to emit a - // backing property initializer call, otherwise let's use - // default expression (which is just `.init()` call). - emitInitializer(initExpr, field, bool(getWrappedValueExpr(field)), - fieldInit); - } else { - fieldInit->finishUninitialized(*this); - } - } - - initialization->finishInitialization(*this); } else { // Otherwise, mark it uninitialized for DI to resolve. initialization->finishUninitialized(*this); diff --git a/lib/SILGen/SILGenLValue.cpp b/lib/SILGen/SILGenLValue.cpp index c787792f56f4c..25a4da7275b94 100644 --- a/lib/SILGen/SILGenLValue.cpp +++ b/lib/SILGen/SILGenLValue.cpp @@ -1427,21 +1427,6 @@ namespace { return false; } - bool canRewriteSetAsTypeWrapperInit(SILGenFunction &SGF) const { - auto *VD = dyn_cast(Storage); - if (!(VD && VD->isAccessedViaTypeWrapper())) - return false; - - auto *fnDecl = SGF.FunctionDC->getAsDecl(); - // Type wrapper transform applies only to user-defined - // designated initializers. - if (auto *ctor = dyn_cast_or_null(fnDecl)) { - return !ctor->isImplicit() && ctor->isDesignatedInit(); - } - - return false; - } - void emitAssignWithSetter(SILGenFunction &SGF, SILLocation loc, LValue &&dest, ArgumentSource &&value) { assert(getAccessorDecl()->isSetter()); @@ -1608,59 +1593,6 @@ namespace { return Mval; }; - auto getTupleFieldIndex = - [&](VarDecl *var, Identifier fieldName) -> Optional { - auto *tupleType = var->getInterfaceType()->castTo(); - int fieldIdx = tupleType->getNamedElementId(fieldName); - if (fieldIdx < 0) - return None; - return fieldIdx; - }; - - if (canRewriteSetAsTypeWrapperInit(SGF)) { - auto *ctor = cast(SGF.FunctionDC->getAsDecl()); - auto *field = cast(Storage); - auto FieldType = field->getValueInterfaceType(); - if (!Substitutions.empty()) { - FieldType = FieldType.subst(Substitutions); - } - - auto *localVar = ctor->getLocalTypeWrapperStorageVar(); - - // First, we need to find index of the current field in `_storage. - auto fieldIdx = getTupleFieldIndex(localVar, field->getName()); - assert(fieldIdx.has_value()); - - // Load `_storage.` - auto localVarRef = - SGF.maybeEmitValueOfLocalVarDecl(localVar, AccessKind::Write); - - auto typeData = getLogicalStorageTypeData( - SGF.getTypeExpansionContext(), SGF.SGM, getTypeData().AccessKind, - FieldType->getCanonicalType()); - - TupleElementComponent TEC(*fieldIdx, typeData); - auto storage = std::move(TEC).project(SGF, loc, localVarRef); - - // Partially apply the setter so it could be used by assign_by_wrapper - - auto setterFRef = getSetterFRef(); - auto setterTy = getSetterType(setterFRef); - SILFunctionConventions setterConv(setterTy, SGF.SGM.M); - auto setterFn = emitPartialSetterApply(setterFRef, setterConv); - - // Create the assign_by_wrapper with the initializer and setter. - - auto Mval = emitValue(field, FieldType, setterTy, setterConv); - - // Inject assign_by_wrapper instruction - - SGF.B.createAssignByTypeWrapper( - loc, Mval.forward(SGF), storage.forward(SGF), setterFn.getValue(), - AssignByWrapperInst::Unknown); - return; - } - if (canRewriteSetAsPropertyWrapperInit(SGF) && !Storage->isStatic() && isBackingVarVisible(cast(Storage), @@ -1688,44 +1620,23 @@ namespace { // Stores the address of the storage property. ManagedValue proj; - // If this is a type wrapper managed property, we emit everything - // through local `_storage` variable because wrapper property - // in this case is backed by `$storage` which has to get initialized - // first. - if (backingVar->isAccessedViaTypeWrapper()) { - auto *ctor = cast(SGF.FunctionDC->getAsDecl()); - auto *localVar = ctor->getLocalTypeWrapperStorageVar(); - - // First, we need to find index of the backing storage field in - // `_storage`. - auto fieldIdx = getTupleFieldIndex(localVar, backingVar->getName()); - assert(fieldIdx.has_value()); - - // Load `_storage.` - auto localVarRef = - SGF.maybeEmitValueOfLocalVarDecl(localVar, AccessKind::Write); - - TupleElementComponent TEC(*fieldIdx, typeData); - proj = std::move(TEC).project(SGF, loc, localVarRef); + // TODO: revist minimal + SILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType( + TypeExpansionContext::minimal(), backingVar, + ValType->getCanonicalType()); + + if (!BaseFormalType) { + proj = + SGF.maybeEmitValueOfLocalVarDecl(backingVar, AccessKind::Write); + } else if (BaseFormalType->mayHaveSuperclass()) { + RefElementComponent REC(backingVar, LValueOptions(), varStorageType, + typeData, /*actorIsolation=*/None); + proj = std::move(REC).project(SGF, loc, base); } else { - // TODO: revist minimal - SILType varStorageType = SGF.SGM.Types.getSubstitutedStorageType( - TypeExpansionContext::minimal(), backingVar, - ValType->getCanonicalType()); - - if (!BaseFormalType) { - proj = - SGF.maybeEmitValueOfLocalVarDecl(backingVar, AccessKind::Write); - } else if (BaseFormalType->mayHaveSuperclass()) { - RefElementComponent REC(backingVar, LValueOptions(), varStorageType, - typeData, /*actorIsolation=*/None); - proj = std::move(REC).project(SGF, loc, base); - } else { - assert(BaseFormalType->getStructOrBoundGenericStruct()); - StructElementComponent SEC(backingVar, varStorageType, typeData, - /*actorIsolation=*/None); - proj = std::move(SEC).project(SGF, loc, base); - } + assert(BaseFormalType->getStructOrBoundGenericStruct()); + StructElementComponent SEC(backingVar, varStorageType, typeData, + /*actorIsolation=*/None); + proj = std::move(SEC).project(SGF, loc, base); } // The property wrapper backing initializer forms an instance of @@ -1750,7 +1661,7 @@ namespace { auto Mval = emitValue(field, FieldType, setterTy, setterConv); - SGF.B.createAssignByPropertyWrapper( + SGF.B.createAssignByWrapper( loc, Mval.forward(SGF), proj.forward(SGF), initFn.getValue(), setterFn.getValue(), AssignByWrapperInst::Unknown); @@ -4884,9 +4795,6 @@ static bool trySetterPeephole(SILGenFunction &SGF, SILLocation loc, if (setterComponent.canRewriteSetAsPropertyWrapperInit(SGF)) return false; - if (setterComponent.canRewriteSetAsTypeWrapperInit(SGF)) - return false; - setterComponent.emitAssignWithSetter(SGF, loc, std::move(dest), std::move(src)); return true;; diff --git a/lib/SILGen/SILGenType.cpp b/lib/SILGen/SILGenType.cpp index f07594e955669..0e5dd16f34c00 100644 --- a/lib/SILGen/SILGenType.cpp +++ b/lib/SILGen/SILGenType.cpp @@ -1115,14 +1115,6 @@ class SILGenType : public TypeMemberVisitor { if (auto *normal = dyn_cast(conformance)) SGM.getWitnessTable(normal); } - - // Emit `init(for:storage)` initializer as it would be used - // by DI and IRGen later on. - if (auto typeWrapperInfo = theType->getTypeWrapper()) { - auto *ctor = typeWrapperInfo->Wrapper->getTypeWrapperInitializer(); - assert(ctor); - (void)SGM.getFunction(SILDeclRef(ctor), NotForDefinition); - } } //===--------------------------------------------------------------------===// diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp index f424f0fc66590..4c8e388b8f1ed 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp @@ -413,7 +413,7 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, std::string &Result) const { auto &Module = MemoryInst->getModule(); - if (isAnyInitSelf() || getAsTypeWrapperLocalStorageVar()) + if (isAnyInitSelf()) Result = "self"; else if (ValueDecl *VD = dyn_cast_or_null(getLoc().getAsASTNode())) @@ -468,24 +468,6 @@ DIMemoryObjectInfo::getPathStringToElement(unsigned Element, /// If the specified value is a 'let' property in an initializer, return true. bool DIMemoryObjectInfo::isElementLetProperty(unsigned Element) const { - // If this is an element of a `_storage` tuple, we need to - // check the `$Storage` to determine whether underlying storage - // backing element is immutable. - if (auto *storageVar = getAsTypeWrapperLocalStorageVar()) { - auto *wrappedType = cast( - storageVar->getDeclContext()->getInnermostTypeContext()); - assert(wrappedType && "_storage reference without type wrapper"); - - auto storageVarType = storageVar->getInterfaceType()->getAs(); - assert(Element < storageVarType->getNumElements()); - auto propertyName = storageVarType->getElement(Element).getName(); - - auto *storageDecl = - cast(wrappedType->getTypeWrapperStorageDecl()); - auto *property = storageDecl->lookupDirect(propertyName).front(); - return cast(property)->isLet(); - } - // If we aren't representing 'self' in a non-delegating initializer, then we // can't have 'let' properties. if (!isNonDelegatingInit()) @@ -557,13 +539,6 @@ ConstructorDecl *DIMemoryObjectInfo::getActorInitSelf() const { return nullptr; } -VarDecl *DIMemoryObjectInfo::getAsTypeWrapperLocalStorageVar() const { - if (isTypeWrapperLocalStorageVar(getFunction(), MemoryInst)) - return getLoc().getAsASTNode(); - - return nullptr; -} - //===----------------------------------------------------------------------===// // DIMemoryUse Implementation //===----------------------------------------------------------------------===// @@ -2019,32 +1994,3 @@ void swift::ownership::collectDIElementUsesFrom( collector.collectFrom(MemoryInfo.getUninitializedValue(), /*collectDestroysOfContainer*/ true); } - -bool swift::ownership::canHaveTypeWrapperLocalStorageVar(SILFunction &F) { - auto *DC = F.getDeclContext(); - if (!DC) - return false; - - auto *ctor = dyn_cast_or_null(DC->getAsDecl()); - if (!ctor || ctor->isImplicit() || !ctor->isDesignatedInit()) - return false; - - auto *parentType = ctor->getDeclContext()->getSelfNominalTypeDecl(); - return parentType && parentType->hasTypeWrapper(); -} - -bool swift::ownership::isTypeWrapperLocalStorageVar( - SILFunction &F, MarkUninitializedInst *Inst) { - if (!Inst->isVar()) - return false; - - if (!canHaveTypeWrapperLocalStorageVar(F)) - return false; - - if (auto *var = Inst->getLoc().getAsASTNode()) { - auto &ctx = var->getASTContext(); - return var->isImplicit() && var->getName() == ctx.Id_localStorageVar; - } - - return false; -} diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h index 2e12ab1975fe9..99b6e9c00cf37 100644 --- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h +++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollector.h @@ -164,11 +164,6 @@ class DIMemoryObjectInfo { /// actor's initializer. Otherwise, returns nullptr. ConstructorDecl *getActorInitSelf() const; - /// If \c TheMemory is a temporary variable that is responsible for - /// member-by-member initialization of `$Storage` in a user-defined - /// initializer of a type wrapped type, return its declaration. - VarDecl *getAsTypeWrapperLocalStorageVar() const; - /// True if this memory object is the 'self' of a derived class initializer. bool isDerivedClassSelf() const { return MemoryInst->isDerivedClassSelf(); } @@ -357,15 +352,6 @@ struct DIElementUseInfo { void collectDIElementUsesFrom(const DIMemoryObjectInfo &MemoryInfo, DIElementUseInfo &UseInfo); -/// Check whether given function is a user-defined initializer of a -/// type wrapped type. -bool canHaveTypeWrapperLocalStorageVar(SILFunction &F); - -/// Check whether this instruction represents `_storage` variable -/// injected by type-checker into user-defined designated initializer -/// of a type wrapped type. -bool isTypeWrapperLocalStorageVar(SILFunction &F, MarkUninitializedInst *Inst); - } // end namespace ownership } // end namespace swift diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp index 82efde83f271a..4e0e1c04da904 100644 --- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp +++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp @@ -459,13 +459,6 @@ namespace { /// corresponds to `self`. void injectActorHops(); - /// Injects `self.$storage = .init(storage: $Storage(...))` - /// assignment instructions into the function after each point - /// where `_storage` becomes fully initialized via `assign_by_wrapper`. - /// This is only necessary only for user-defined initializers of a - /// type wrapped types. - void injectTypeWrapperStorageInitalization(); - void emitSelfConsumedDiagnostic(SILInstruction *Inst); LiveOutBlockState &getBlockInfo(SILBasicBlock *BB) { @@ -1077,310 +1070,6 @@ void LifetimeChecker::injectActorHops() { injectExecutorHopAfter(point); } -/// TODO: Move this and \c injectActorHops into a separate file -/// i.e. `DICodeSynthesis.cpp`. -void LifetimeChecker::injectTypeWrapperStorageInitalization() { - auto *storageVar = TheMemory.getAsTypeWrapperLocalStorageVar(); - if (!storageVar) - return; - - SmallVector points; - findFullInitializationPoints(points); - - // `_storage` has not been initialized at all. - if (points.empty()) - return; - - auto *ctor = cast(storageVar->getDeclContext()->getAsDecl()); - auto *parentType = ctor->getDeclContext()->getSelfNominalTypeDecl(); - auto *storageDecl = - cast(parentType->getTypeWrapperStorageDecl()); - - for (auto *point : points) { - SILBuilderWithScope::insertAfter(point, [&](SILBuilder &b) { - SILLocation loc = SILLocation::invalid().asAutoGenerated(); - - SmallVector allocations; - - auto allocStack = [&](SILType type) -> SILValue { - auto value = b.createAllocStack(loc, type); - allocations.push_back(value); - return value; - }; - - auto createInitRef = [&](ConstructorDecl *ctor) -> SILValue { - auto *init = F.getModule().lookUpFunction(SILDeclRef(ctor)); - assert(init); - return b.createFunctionRef(loc, init); - }; - - // The instance type of $Storage declaration. - auto storageType = F.getLoweredType( - ctor->mapTypeIntoContext(storageDecl->getDeclaredInterfaceType())); - - // Argument value to use in call to .init(storage:) - SILValue storageObj = allocStack(storageType); - - // let storageObj = $Storage() - { - SILValue localStorageVar = TheMemory.getUninitializedValue(); - - SmallVector storageInitArgs; - - SILValue storageInitRef; - if (auto *memberwiseInit = storageDecl->getMemberwiseInitializer()) { - storageInitRef = createInitRef(memberwiseInit); - } else { - assert(storageDecl->hasDefaultInitializer()); - storageInitRef = createInitRef(storageDecl->getDefaultInitializer()); - } - - CanSILFunctionType storageInitTy = - storageInitRef->getType().castTo(); - SILFunctionConventions convention(storageInitTy, F.getModule()); - - // If `.init` produces result indirectly, let's load it - // into the prepared buffer. - if (convention.hasIndirectSILResults()) { - assert(convention.getNumIndirectSILResults() == 1); - storageInitArgs.push_back(storageObj); - } - - // Prepare arguments for $Storage.init(...) call. `_storage` - // tuple needs to be flattened and its elements have to be - // either copied or loaded based on a particular argument convention. - { - auto localStorageRef = - b.createBeginAccess(loc, localStorageVar, SILAccessKind::Read, - SILAccessEnforcement::Unsafe, - /*noNestedConflict=*/false, - /*fromBuiltin=*/false); - - // There could be only one indirect result her - $Storage, - // which is also verified above, actual arguments start - // at 1 in such case. - unsigned argIdx = convention.getNumIndirectSILResults(); - - std::function &)> - prepareArguments = - [&](SILValue val, SmallVectorImpl &results) { - // Destructure the tuple into individual elements recursively. - if (auto tupleType = val->getType().getAs()) { - for (unsigned i = 0, n = tupleType->getNumElements(); - i != n; ++i) { - SILValue elt = b.createTupleElementAddr(loc, val, i); - prepareArguments(elt, results); - } - return; - } - - switch (convention.getSILArgumentConvention(argIdx)) { - case SILArgumentConvention::Indirect_In: { - // The only way to load opaque type is to allocate a temporary - // variable on the stack for it and initialize from the element - // address. - SILValue arg = allocStack(val->getType()); - b.createCopyAddr(loc, val, arg, IsNotTake, IsInitialization); - results.push_back(arg); - break; - } - - case SILArgumentConvention::Indirect_In_Guaranteed: - // The argument is +0, so we can use the address of the - // element as an argument. - results.push_back(val); - break; - - case SILArgumentConvention::Direct_Owned: { - // Copy the value out at +1. - results.push_back(b.createTrivialLoadOr( - loc, val, LoadOwnershipQualifier::Copy)); - break; - } - - case SILArgumentConvention::Direct_Unowned: - case SILArgumentConvention::Direct_Guaranteed: { - results.push_back(b.createTrivialLoadOr( - loc, val, LoadOwnershipQualifier::Take)); - break; - } - - case SILArgumentConvention::Indirect_Out: - llvm_unreachable("indirect result is handled separately"); - - case SILArgumentConvention::Indirect_Inout: - case SILArgumentConvention::Indirect_InoutAliasable: - llvm_unreachable("inout arguments are not supported by $Storage."); - - case SILArgumentConvention::Pack_Out: - case SILArgumentConvention::Pack_Owned: - case SILArgumentConvention::Pack_Guaranteed: - case SILArgumentConvention::Pack_Inout: - llvm_unreachable("pack arguments are not supported by $Storage."); - } - - ++argIdx; - }; - - prepareArguments(localStorageRef, storageInitArgs); - - b.createEndAccess(loc, localStorageRef, /*abort=*/false); - } - - // append $Storage.Type as the last argument to $Storage.init() - { - auto storageMetatypeType = F.getLoweredType( - ctor->mapTypeIntoContext(storageDecl->getInterfaceType())); - storageInitArgs.push_back(b.createMetatype(loc, storageMetatypeType)); - } - - SubstitutionMap storageInitSubs; - // Generic signature of $Storage is appropriate because - // the call is to either memberwise or default initializer - // which cannot introduce any additional generic parameters. - if (auto genericSig = storageDecl->getGenericSignature()) { - storageInitSubs = SubstitutionMap::get( - genericSig, - [&](SubstitutableType *type) { - return ctor->mapTypeIntoContext(type); - }, - LookUpConformanceInModule( - ctor->getDeclContext()->getParentModule())); - } - - // = $Storage.init() - auto storageInitResult = b.createApply( - loc, storageInitRef, storageInitSubs, storageInitArgs); - - // If result was indirect it would already be loaded into - // \c storageObj by the call itself. - if (!convention.hasIndirectSILResults()) - b.createTrivialStoreOr(loc, storageInitResult, storageObj, - StoreOwnershipQualifier::Init); - } - - // self.$storage = (storage: storageObj)) - { - bool isClass = isa(parentType); - - auto typeWrapperInfo = parentType->getTypeWrapper(); - auto *typeWrapperInit = - typeWrapperInfo->Wrapper->getTypeWrapperInitializer(); - SILValue typeWrapperInitRef = createInitRef(typeWrapperInit); - - auto *self = TheMemory.findUninitializedSelfValue(); - - SILValue selfRef; - if (isClass) { - selfRef = b.emitBeginBorrowOperation(loc, self); - } else { - selfRef = b.createBeginAccess(loc, self, SILAccessKind::Modify, - SILAccessEnforcement::Static, - /*noNestedConflict=*/false, - /*fromBuiltin=*/false); - } - - CanSILFunctionType wrapperInitTy = - typeWrapperInitRef->getType().castTo(); - SILFunctionConventions convention(wrapperInitTy, F.getModule()); - - // Argument is always passed indirectly because it's the - // generic parameter `S`. - - // Reference to self.$storage - SILValue storagePropRef; - if (isClass) { - storagePropRef = b.createRefElementAddr( - loc, selfRef, parentType->getTypeWrapperProperty()); - } else { - assert(isa(parentType)); - storagePropRef = b.createStructElementAddr( - loc, selfRef, parentType->getTypeWrapperProperty()); - } - - auto wrappedType = MetatypeType::get(self->getType().getASTType(), - MetatypeRepresentation::Thick); - auto wrappedMetatype = - b.createMetatype(loc, F.getLoweredType(wrappedType)); - - auto typeWrapperType = - b.createMetatype(loc, F.getLoweredType(MetatypeType::get( - storagePropRef->getType().getASTType()))); - - SmallVector wrapperInitArgs; - - Optional localWrapperObj; - - if (convention.hasIndirectSILResults()) { - assert(convention.getNumIndirectSILResults() == 1); - localWrapperObj = allocStack(storagePropRef->getType()); - wrapperInitArgs.push_back(*localWrapperObj); - } - - wrapperInitArgs.push_back(wrappedMetatype); - wrapperInitArgs.push_back(storageObj); - wrapperInitArgs.push_back(typeWrapperType); - - TypeSubstitutionMap wrapperInitSubs; - { - auto sig = - typeWrapperInit->getGenericSignature().getCanonicalSignature(); - wrapperInitSubs[sig.getGenericParams()[0]] = - wrappedType->getMetatypeInstanceType(); - wrapperInitSubs[sig.getGenericParams()[1]] = storageType.getASTType(); - } - - // = .init(for: , storage: tmpStorage) - auto wrapperInitResult = - b.createApply(loc, typeWrapperInitRef, - SubstitutionMap::get( - typeWrapperInit->getGenericSignature(), - QueryTypeSubstitutionMap{wrapperInitSubs}, - LookUpConformanceInModule( - ctor->getDeclContext()->getParentModule())), - wrapperInitArgs); - - // self.$storage is a property access so it has to has to be wrapped - // in begin/end access instructions. - { - auto storagePropAccess = - b.createBeginAccess(loc, storagePropRef, SILAccessKind::Modify, - SILAccessEnforcement::Dynamic, - /*noNestedConflict=*/false, - /*fromBuiltin=*/false); - - // self.$storage = - { - // If the result of `init` is indirect, let's just - // initialize the property with it. - if (localWrapperObj) { - b.createCopyAddr(loc, *localWrapperObj, storagePropAccess, IsTake, - IsInitialization); - } else { - b.createAssign(loc, wrapperInitResult, storagePropAccess, - AssignOwnershipQualifier::Init); - } - } - - b.createEndAccess(loc, storagePropAccess, /*abort=*/false); - } - - if (isClass) { - b.emitEndBorrowOperation(loc, selfRef); - } else { - b.createEndAccess(loc, selfRef, /*aborted=*/false); - } - - // Dealloc all stack allocated data. - for (auto alloca = allocations.rbegin(); alloca != allocations.rend(); - ++alloca) { - b.createDeallocStack(loc, *alloca); - } - } - }); - } -} - void LifetimeChecker::doIt() { // With any escapes tallied up, we can work through all the uses, checking // for definitive initialization, promoting loads, rewriting assigns, and @@ -1471,10 +1160,6 @@ void LifetimeChecker::doIt() { // Insert hop_to_executor instructions for actor initializers, if needed. injectActorHops(); - // Insert `self.$storage` initialization for user-defined initializers - // declared in a type wrapped type. - injectTypeWrapperStorageInitalization(); - // If the memory object had any non-trivial stores that are init or assign // based on the control flow path reaching them, then insert dynamic control // logic and CFG diamonds to handle this. @@ -3849,29 +3534,6 @@ static void processMemoryObject(MarkUninitializedInst *I, LifetimeChecker(MemInfo, UseInfo, blockStates).doIt(); } -static MarkUninitializedInst *findLocalTypeWrapperStorageVar(SILFunction &F) { - if (!canHaveTypeWrapperLocalStorageVar(F)) - return nullptr; - - auto BB = F.getEntryBlock(); - if (!BB) - return nullptr; - - // The variable in question - `_storage` is injected during Sema - // as a first declaration statement in the body of a user-defined - // designated initializer of a type wrapped type. - // - // See \c TypeCheckFunctionBodyRequest for more details. - for (auto &I : *BB) { - SILInstruction *Inst = &I; - auto *MUI = dyn_cast(Inst); - if (MUI && isTypeWrapperLocalStorageVar(F, MUI)) - return MUI; - } - - return nullptr; -} - /// Check that all memory objects that require initialization before use are /// properly set and transform the code as required for flow-sensitive /// properties. @@ -3882,30 +3544,6 @@ static bool checkDefiniteInitialization(SILFunction &Fn) { BlockStates blockStates(&Fn); - // Special handling of _storage variable introduced by type wrapper. - // It has to be checked first because it injects initialization of - // `self.$storage`. - if (auto *storageVar = findLocalTypeWrapperStorageVar(Fn)) { - { - auto &M = Fn.getModule(); - - DiagnosticTransaction T(M.getASTContext().Diags); - - // process `_storage` object. - processMemoryObject(storageVar, blockStates); - - // Stop if `_storage` initialization checking produced - // errors, otherwise DI is going to emit confusing - // "return without fully initialized self - self.$storage" error. - if (T.hasErrors()) - return true; - } - - storageVar->replaceAllUsesWith(storageVar->getOperand()); - storageVar->eraseFromParent(); - Changed = true; - } - for (auto &BB : Fn) { for (auto I = BB.begin(), E = BB.end(); I != E;) { SILInstruction *Inst = &*I; diff --git a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp index a6984c5e540c0..d06d94ad30e6a 100644 --- a/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp +++ b/lib/SILOptimizer/Mandatory/RawSILInstLowering.cpp @@ -190,51 +190,28 @@ lowerAssignByWrapperInstruction(SILBuilderWithScope &b, LLVM_FALLTHROUGH; case AssignByWrapperInst::Initialization: case AssignByWrapperInst::Assign: { - switch (inst->getOriginator()) { - case AssignByWrapperInst::Originator::TypeWrapper: { - bool initialization = - inst->getMode() == AssignByWrapperInst::Initialization; - - if (inst->getDest()->getType().isAddressOnly(*inst->getFunction())) { - b.createCopyAddr(loc, src, dest, IsTake, - initialization ? IsInitialization - : IsNotInitialization); - } else { - b.createTrivialStoreOr(loc, src, dest, - initialization - ? StoreOwnershipQualifier::Init - : StoreOwnershipQualifier::Assign); - } - break; - } - - case AssignByWrapperInst::Originator::PropertyWrapper: { - SILValue initFn = inst->getInitializer(); - CanSILFunctionType fTy = initFn->getType().castTo(); - SILFunctionConventions convention(fTy, inst->getModule()); - SmallVector args; - if (convention.hasIndirectSILResults()) { - if (inst->getMode() == AssignByWrapperInst::Assign) - b.createDestroyAddr(loc, dest); - - args.push_back(dest); - getAssignByWrapperArgs(args, src, convention, b, forCleanup); - b.createApply(loc, initFn, SubstitutionMap(), args); + SILValue initFn = inst->getInitializer(); + CanSILFunctionType fTy = initFn->getType().castTo(); + SILFunctionConventions convention(fTy, inst->getModule()); + SmallVector args; + if (convention.hasIndirectSILResults()) { + if (inst->getMode() == AssignByWrapperInst::Assign) + b.createDestroyAddr(loc, dest); + + args.push_back(dest); + getAssignByWrapperArgs(args, src, convention, b, forCleanup); + b.createApply(loc, initFn, SubstitutionMap(), args); + } else { + getAssignByWrapperArgs(args, src, convention, b, forCleanup); + SILValue wrappedSrc = + b.createApply(loc, initFn, SubstitutionMap(), args); + if (inst->getMode() == AssignByWrapperInst::Initialization || + inst->getDest()->getType().isTrivial(*inst->getFunction())) { + b.createTrivialStoreOr(loc, wrappedSrc, dest, + StoreOwnershipQualifier::Init); } else { - getAssignByWrapperArgs(args, src, convention, b, forCleanup); - SILValue wrappedSrc = - b.createApply(loc, initFn, SubstitutionMap(), args); - if (inst->getMode() == AssignByWrapperInst::Initialization || - inst->getDest()->getType().isTrivial(*inst->getFunction())) { - b.createTrivialStoreOr(loc, wrappedSrc, dest, - StoreOwnershipQualifier::Init); - } else { - b.createStore(loc, wrappedSrc, dest, - StoreOwnershipQualifier::Assign); - } + b.createStore(loc, wrappedSrc, dest, StoreOwnershipQualifier::Assign); } - break; - } } // The unused partial_apply violates memory lifetime rules in case "self" diff --git a/test/SILGen/objc_properties.swift b/test/SILGen/objc_properties.swift index 58dcaa58fc9b6..76af42addd0a7 100644 --- a/test/SILGen/objc_properties.swift +++ b/test/SILGen/objc_properties.swift @@ -290,10 +290,10 @@ class SomeWrapperTests { // CHECK-LABEL: sil hidden [ossa] @$s15objc_properties16SomeWrapperTestsCyACSScfc : $@convention(method) (@owned String, @owned SomeWrapperTests) -> @owned SomeWrapperTests { // CHECK: [[M:%.*]] = function_ref @$s15objc_properties16SomeWrapperTestsC04someD0SivsTD // CHECK: [[C:%.*]] = partial_apply [callee_guaranteed] [[M]]({{.*}}) -// CHECK: assign_by_wrapper origin property_wrapper, {{%.*}}: $Int to {{%.*}} : $*SomeWrapper, init {{.*}} : $@callee_guaranteed (Int) -> SomeWrapper, set [[C]] : $@callee_guaranteed (Int) -> () +// CHECK: assign_by_wrapper {{%.*}}: $Int to {{%.*}} : $*SomeWrapper, init {{.*}} : $@callee_guaranteed (Int) -> SomeWrapper, set [[C]] : $@callee_guaranteed (Int) -> () // CHECK: [[M:%.*]] = function_ref @$s15objc_properties16SomeWrapperTestsC1sSSSgvsTD // CHECK: [[C:%.*]] = partial_apply [callee_guaranteed] [[M]]( -// CHECK: assign_by_wrapper origin property_wrapper, {{.*}} : $Optional to {{.*}} : $*W>, init {{.*}} : $@callee_guaranteed (@owned Optional) -> @owned W>, set [[C]] : $@callee_guaranteed (@owned Optional) -> () +// CHECK: assign_by_wrapper {{.*}} : $Optional to {{.*}} : $*W>, init {{.*}} : $@callee_guaranteed (@owned Optional) -> @owned W>, set [[C]] : $@callee_guaranteed (@owned Optional) -> () init(_ s: String) { someWrapper = 1000 self.s = s diff --git a/test/SILGen/property_wrapper_local.swift b/test/SILGen/property_wrapper_local.swift index 69767208646c2..21dc1a7d1b5c1 100644 --- a/test/SILGen/property_wrapper_local.swift +++ b/test/SILGen/property_wrapper_local.swift @@ -26,7 +26,7 @@ func testLocalWrapper() { // CHECK-NEXT: [[C:%.*]] = copy_value [[W]] : ${ var Wrapper } // CHECK-NOT: mark_function_escape // CHECK-NEXT: [[SPA:%.*]] = partial_apply [callee_guaranteed] [[S]]([[C]]) : $@convention(thin) (Int, @guaranteed { var Wrapper }) -> () - // CHECK-NEXT: assign_by_wrapper origin property_wrapper, {{%.*}} : $Int to [[P]] : $*Wrapper, init [[IPA]] : $@callee_guaranteed (Int) -> Wrapper, set [[SPA]] : $@callee_guaranteed (Int) -> () + // CHECK-NEXT: assign_by_wrapper {{%.*}} : $Int to [[P]] : $*Wrapper, init [[IPA]] : $@callee_guaranteed (Int) -> Wrapper, set [[SPA]] : $@callee_guaranteed (Int) -> () _ = value // CHECK: mark_function_escape [[P]] : $*Wrapper @@ -45,7 +45,7 @@ func testLocalWrapper() { // CHECK: [[OP:%.*]] = function_ref @$sSi2peoiyySiz_SitFZ : $@convention(method) (@inout Int, Int, @thin Int.Type) -> () // CHECK: apply [[OP]]({{%.*}}) : $@convention(method) (@inout Int, Int, @thin Int.Type) -> () // CHECK: [[RESULT:%.*]] = load [trivial] [[TMP]] : $*Int - // CHECK: assign_by_wrapper origin property_wrapper, [[RESULT]] : $Int to [[P]] + // CHECK: assign_by_wrapper [[RESULT]] : $Int to [[P]] // Check local property wrapper backing initializer and accessors diff --git a/test/SILGen/property_wrappers.swift b/test/SILGen/property_wrappers.swift index eded6351a6931..df2dbc42ce91b 100644 --- a/test/SILGen/property_wrappers.swift +++ b/test/SILGen/property_wrappers.swift @@ -904,7 +904,7 @@ struct NonMutatingWrapperTestStruct { // CHECK-LABEL: sil hidden [ossa] @$s17property_wrappers28NonMutatingWrapperTestStructV3valACSi_tcfC : $@convention(method) (Int, @thin NonMutatingWrapperTestStruct.Type) -> NonMutatingWrapperTestStruct { // CHECK: %[[LOAD:[0-9]+]] = load [trivial] %[[SRC:[0-9]+]] : $*NonMutatingWrapperTestStruct // CHECK-NEXT: %[[SET_PA:[0-9]+]] = partial_apply [callee_guaranteed] %[[PW_SETTER:[0-9]+]](%[[LOAD]]) : $@convention(method) (Int, NonMutatingWrapperTestStruct) -> () - // CHECK-NEXT: assign_by_wrapper origin property_wrapper, %[[SETVAL:[0-9]+]] : $Int to %[[ADDR:[0-9]+]] : $*NonMutatingSetterWrapper, init %[[INIT_PA:[0-9]+]] : $@callee_guaranteed (Int) -> NonMutatingSetterWrapper, set %[[SET_PA]] : $@callee_guaranteed (Int) -> () + // CHECK-NEXT: assign_by_wrapper %[[SETVAL:[0-9]+]] : $Int to %[[ADDR:[0-9]+]] : $*NonMutatingSetterWrapper, init %[[INIT_PA:[0-9]+]] : $@callee_guaranteed (Int) -> NonMutatingSetterWrapper, set %[[SET_PA]] : $@callee_guaranteed (Int) -> () @NonMutatingSetterWrapper var SomeProp: Int init(val: Int) { SomeProp = val diff --git a/test/SILGen/resilient_assign_by_wrapper.swift b/test/SILGen/resilient_assign_by_wrapper.swift index de926e9fd3a04..22f7ead149fba 100644 --- a/test/SILGen/resilient_assign_by_wrapper.swift +++ b/test/SILGen/resilient_assign_by_wrapper.swift @@ -34,7 +34,7 @@ public class AddressOnlySetter { // CHECK: [[E2:%.*]] = alloc_stack $AddressOnlyEnum // CHECK-NEXT: inject_enum_addr [[E2]] : $*AddressOnlyEnum, #AddressOnlyEnum.some!enumelt // CHECK: [[S:%.*]] = partial_apply [callee_guaranteed] {{%.*}}({{%.*}}) : $@convention(method) (@in AddressOnlyEnum, @guaranteed AddressOnlySetter) -> () - // CHECK: assign_by_wrapper origin property_wrapper, [[E2]] : $*AddressOnlyEnum + // CHECK: assign_by_wrapper [[E2]] : $*AddressOnlyEnum // CHECK-SAME: set [[S]] : $@callee_guaranteed (@in AddressOnlyEnum) -> () self.value = .some } @@ -58,7 +58,7 @@ extension SubstitutedSetter where T == Bool { // CHECK-LABEL: hidden [ossa] @$s27resilient_assign_by_wrapper17SubstitutedSetterVAASbRszlEACySbGycfC // CHECK: [[W:%.*]] = struct_element_addr {{%.*}} : $*SubstitutedSetter, #SubstitutedSetter._value // CHECK: [[B:%.*]] = alloc_stack $Bool - // CHECK: assign_by_wrapper origin property_wrapper, [[B]] : $*Bool to [[W]] : $*WrapGod + // CHECK: assign_by_wrapper [[B]] : $*Bool to [[W]] : $*WrapGod // CHECK-SAME: init {{%.*}} : $@callee_guaranteed (@in Bool) -> @out WrapGod // CHECK-SAME: set {{%.*}} : $@callee_guaranteed (@in Bool) -> () self.value = true @@ -77,7 +77,7 @@ extension ReabstractedSetter where T == Int { // CHECK: [[THUNK_REF:%.*]] = function_ref @$sSiIegy_SiIegn_TR : $@convention(thin) (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> ()) -> () // CHECK: [[CF:%.*]] = partial_apply [callee_guaranteed] [[THUNK_REF]]([[TH_F]]) : $@convention(thin) (@in_guaranteed Int, @guaranteed @callee_guaranteed (Int) -> ()) -> () // CHECK: [[CF2:%.*]] = convert_function [[CF]] - // CHECK: assign_by_wrapper origin property_wrapper, [[CF2]] + // CHECK: assign_by_wrapper [[CF2]] // CHECK-SAME: to {{%.*}} : $*WrapGod<(Int) -> ()> self.value = { x in } } @@ -94,7 +94,7 @@ extension ObjectifiedSetter where T == SomeObject { // CHECK-LABEL: sil hidden [ossa] @$s27resilient_assign_by_wrapper17ObjectifiedSetterV5valuexvs : $@convention(method) (@owned T, @inout ObjectifiedSetter) -> () { // CHECK: [[OBJ:%.*]] = apply {{%.*}}({{%.*}}) : $@convention(method) (@thick SomeObject.Type) -> @owned SomeObject // CHECK: [[STORAGE:%.*]] = struct_element_addr {{%.*}} : $*ObjectifiedSetter, #ObjectifiedSetter._value - // CHECK: assign_by_wrapper origin property_wrapper, [[OBJ]] : $SomeObject to [[STORAGE]] : $*WrapGod + // CHECK: assign_by_wrapper [[OBJ]] : $SomeObject to [[STORAGE]] : $*WrapGod // CHECK-SAME: init {{%.*}} : $@callee_guaranteed (@owned SomeObject) -> @out WrapGod // CHECK-SAME: set {{%.*}} : $@callee_guaranteed (@owned SomeObject) -> () self.value = SomeObject() diff --git a/test/SILOptimizer/raw_sil_inst_lowering.sil b/test/SILOptimizer/raw_sil_inst_lowering.sil index 5fa5a885cdd47..c5842317e8e76 100644 --- a/test/SILOptimizer/raw_sil_inst_lowering.sil +++ b/test/SILOptimizer/raw_sil_inst_lowering.sil @@ -108,7 +108,7 @@ bb0(%0 : @owned $SomeClass): %9 = partial_apply [callee_guaranteed] %8() : $@convention(thin) (@owned SomeClass) -> @owned Wrapper %10 = function_ref @set_closure : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () %11 = partial_apply [callee_guaranteed] %10(%1) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - assign_by_wrapper origin property_wrapper, %0 : $SomeClass to [init] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () + assign_by_wrapper %0 : $SomeClass to [init] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () destroy_value %11 : $@callee_guaranteed (@owned SomeClass) -> () destroy_value %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper %16 = load [take] %1 : $*RefStruct @@ -132,7 +132,7 @@ bb0(%0 : @owned $SomeClass): %9 = partial_apply [callee_guaranteed] %8() : $@convention(thin) (@owned SomeClass) -> @out Wrapper %10 = function_ref @set_closure : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () %11 = partial_apply [callee_guaranteed] %10(%1) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - assign_by_wrapper origin property_wrapper, %0 : $SomeClass to [init] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () + assign_by_wrapper %0 : $SomeClass to [init] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () destroy_value %11 : $@callee_guaranteed (@owned SomeClass) -> () destroy_value %9 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper %16 = load [take] %1 : $*RefStruct @@ -157,7 +157,7 @@ bb0(%0 : @owned $SomeClass, %1 : @owned $Wrapper): %9 = partial_apply [callee_guaranteed] %8() : $@convention(thin) (@owned SomeClass) -> @owned Wrapper %10 = function_ref @set_closure : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () %11 = partial_apply [callee_guaranteed] %10(%2) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - assign_by_wrapper origin property_wrapper, %0 : $SomeClass to [assign] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () + assign_by_wrapper %0 : $SomeClass to [assign] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () destroy_value %11 : $@callee_guaranteed (@owned SomeClass) -> () destroy_value %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper %16 = load [take] %2 : $*RefStruct @@ -182,7 +182,7 @@ bb0(%0 : @owned $SomeClass, %1 : @owned $Wrapper): %9 = partial_apply [callee_guaranteed] %8() : $@convention(thin) (@owned SomeClass) -> @out Wrapper %10 = function_ref @set_closure : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () %11 = partial_apply [callee_guaranteed] %10(%2) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - assign_by_wrapper origin property_wrapper, %0 : $SomeClass to [assign] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () + assign_by_wrapper %0 : $SomeClass to [assign] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () destroy_value %11 : $@callee_guaranteed (@owned SomeClass) -> () destroy_value %9 : $@callee_guaranteed (@owned SomeClass) -> @out Wrapper %16 = load [take] %2 : $*RefStruct @@ -207,7 +207,7 @@ bb0(%0 : @owned $SomeClass, %1 : @owned $RefStruct): %9 = partial_apply [callee_guaranteed] %8() : $@convention(thin) (@owned SomeClass) -> @owned Wrapper %10 = function_ref @set_closure : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () %11 = partial_apply [callee_guaranteed] %10(%2) : $@convention(method) (@owned SomeClass, @inout RefStruct) -> () - assign_by_wrapper origin property_wrapper, %0 : $SomeClass to [assign_wrapped_value] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () + assign_by_wrapper %0 : $SomeClass to [assign_wrapped_value] %7 : $*Wrapper, init %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper, set %11 : $@callee_guaranteed (@owned SomeClass) -> () destroy_value %11 : $@callee_guaranteed (@owned SomeClass) -> () destroy_value %9 : $@callee_guaranteed (@owned SomeClass) -> @owned Wrapper %16 = load [take] %2 : $*RefStruct diff --git a/tools/sil-opt/SILOpt.cpp b/tools/sil-opt/SILOpt.cpp index 875349ef165cb..1142346c22600 100644 --- a/tools/sil-opt/SILOpt.cpp +++ b/tools/sil-opt/SILOpt.cpp @@ -141,10 +141,6 @@ static llvm::cl::opt EnableExperimentalDistributed("enable-experimental-distributed", llvm::cl::desc("Enable experimental distributed actors.")); -static llvm::cl::opt EnableExperimentalTypeWrappers( - "enable-experimental-type-wrappers", - llvm::cl::desc("Enable experimental type wrappers.")); - static llvm::cl::opt VerifyExclusivity("enable-verify-exclusivity", llvm::cl::desc("Verify the access markers used to enforce exclusivity.")); @@ -604,10 +600,6 @@ int main(int argc, char **argv) { Feature::DifferentiableProgramming); } - if (EnableExperimentalTypeWrappers) { - Invocation.getLangOptions().Features.insert(Feature::TypeWrappers); - } - Invocation.getLangOptions().EnableCXXInterop = EnableCxxInterop; Invocation.getDiagnosticOptions().VerifyMode = From 5dded3da736e226a605ad2e36bf476a68ba15467 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 8 Feb 2023 09:54:12 -0800 Subject: [PATCH 37/98] [Tests] NFC: Remove test-cases associated with TypeWrappers feature --- .../Inputs/type_wrapper_defs.swift | 453 ----------- .../type_wrapper_with_actors.swift | 51 -- test/Interpreter/type_wrappers.swift | 726 ------------------ test/ModuleInterface/type_wrappers.swift | 124 --- ...pe_wrapper_definite_init_diagnostics.swift | 108 --- ..._wrapper_in_user_defined_init_lowering.sil | 176 ----- .../type_wrapper_init_transform.swift | 456 ----------- test/Serialization/type-wrapper-cycle.swift | 45 -- .../type_wrapper_in_swiftinterface.swift | 95 --- test/type/type_wrapper.swift | 689 ----------------- 10 files changed, 2923 deletions(-) delete mode 100644 test/Interpreter/Inputs/type_wrapper_defs.swift delete mode 100644 test/Interpreter/type_wrapper_with_actors.swift delete mode 100644 test/Interpreter/type_wrappers.swift delete mode 100644 test/ModuleInterface/type_wrappers.swift delete mode 100644 test/SILOptimizer/type_wrapper_definite_init_diagnostics.swift delete mode 100644 test/SILOptimizer/type_wrapper_in_user_defined_init_lowering.sil delete mode 100644 test/SILOptimizer/type_wrapper_init_transform.swift delete mode 100644 test/Serialization/type-wrapper-cycle.swift delete mode 100644 test/Serialization/type_wrapper_in_swiftinterface.swift delete mode 100644 test/type/type_wrapper.swift diff --git a/test/Interpreter/Inputs/type_wrapper_defs.swift b/test/Interpreter/Inputs/type_wrapper_defs.swift deleted file mode 100644 index 744a795ccf7a9..0000000000000 --- a/test/Interpreter/Inputs/type_wrapper_defs.swift +++ /dev/null @@ -1,453 +0,0 @@ -@typeWrapper -public struct Wrapper { - var underlying: S - - public init(for wrappedType: W.Type, storage: S) { - print("Wrapper.init(for: \(wrappedType), storage: \(storage))") - self.underlying = storage - } - - public subscript(propertyKeyPath propertyPath: KeyPath, - storageKeyPath storagePath: KeyPath) -> V { - get { - print("in read-only getter storage: \(storagePath)") - return underlying[keyPath: storagePath] - } - } - - public subscript(propertyKeyPath propertyPath: KeyPath, - storageKeyPath storagePath: WritableKeyPath) -> V { - get { - print("in getter storage: \(storagePath)") - return underlying[keyPath: storagePath] - } - set { - print("in setter => \(newValue)") - underlying[keyPath: storagePath] = newValue - } - } - - public subscript(wrappedSelf w: W, - propertyKeyPath propertyPath: KeyPath, - storageKeyPath storagePath: KeyPath) -> V { - get { - print("in (reference type) let getter storage: \(storagePath)") - return underlying[keyPath: storagePath] - } - } - - public subscript(wrappedSelf w: W, - propertyKeyPath propertyPath: KeyPath, - storageKeyPath storagePath: WritableKeyPath) -> V { - get { - print("in (reference type) getter storage: \(storagePath)") - return underlying[keyPath: storagePath] - } - set { - print("in (reference type) setter => \(newValue)") - underlying[keyPath: storagePath] = newValue - } - } -} - -@propertyWrapper -public struct PropWrapper { - public var value: Value - - public init(wrappedValue: Value) { - self.value = wrappedValue - } - - public var projectedValue: Self { return self } - - public var wrappedValue: Value { - get { - self.value - } - set { - self.value = newValue - } - } -} - -@propertyWrapper -public struct PropWrapperWithoutInit { - public var value: Value - - public init(value: Value) { - self.value = value - } - - public var projectedValue: Self { return self } - - public var wrappedValue: Value { - get { - self.value - } - set { - self.value = newValue - } - } -} - -@propertyWrapper -public struct PropWrapperWithoutProjection { - public var value: Value - - public init(wrappedValue: Value) { - self.value = wrappedValue - } - - public var wrappedValue: Value { - get { - self.value - } - set { - self.value = newValue - } - } -} - -@Wrapper -public class Person { - public var name: String - public var projects: [T] -} - -@Wrapper -public struct PersonWithDefaults { - public var name: String = "" - public var age: Int = 99 -} - -@Wrapper -public struct PropWrapperTest { - @PropWrapper public var test: Int -} - -@Wrapper -public struct DefaultedPropWrapperTest { - @PropWrapper public var test: Int = 0 -} - -@Wrapper -public struct DefaultedPropWrapperWithArgTest { - @PropWrapper(wrappedValue: 3) public var test: Int -} - -@Wrapper -public struct PropWrapperNoInitTest { - @PropWrapperWithoutInit public var a: Int - @PropWrapperWithoutInit(value: "b") public var b: String -} - -@Wrapper -public struct PropWrapperNoProjectionTest { - @PropWrapperWithoutProjection public var a: Int = 0 - @PropWrapperWithoutProjection(wrappedValue: PropWrapper(wrappedValue: "b")) @PropWrapper public var b: String -} - -@Wrapper -public struct ComplexPropWrapperTest { - @PropWrapper public var a: [String] = ["a"] - @PropWrapperWithoutInit(value: PropWrapper(wrappedValue: [1, 2, 3])) @PropWrapper public var b: [Int] -} - -@Wrapper -public struct PersonWithUnmanagedTest { - public let name: String - - public lazy var age: Int = { - 30 - }() - - public var placeOfBirth: String { - get { "Earth" } - } - - @PropWrapper public var favoredColor: String = "red" -} - -@Wrapper -public class ClassWithDesignatedInit { - public var a: Int - @PropWrapperWithoutInit public var b: [Int] - - public init(a: Int, b: [Int] = [1, 2, 3]) { - self.a = a - self._b = PropWrapperWithoutInit(value: b) - } -} - -@Wrapper -public class PersonWithIgnoredAge { - public var name: String - @typeWrapperIgnored public var age: Int = 0 - @typeWrapperIgnored @PropWrapper public var manufacturer: String? -} - -/// Empty class to make sure that $storage is initialized -/// even without any type wrapper managed properties. -@Wrapper -public class EmptyUserDefinedInitClassTest { - public init() {} -} - -/// Empty struct to make sure that $storage is initialized -/// even without any type wrapper managed properties. -@Wrapper -public class EmptyUserDefinedInitStructTest { - public init() {} -} - -@Wrapper -public class TrivialUserDefinedInitClassTest { - public var a: Int - - public init(a: Int) { - self.a = a - } - - public init(withReassign: Int) { - self.a = 0 - print(self.a) - self.a = withReassign - print(self.a) - } -} - -@Wrapper -public struct TrivialUserDefinedInitStructTest { - public var a: Int - - public init(a: Int) { - self.a = a - } - - public init(withReassign: Int) { - self.a = 0 - print(self.a) - self.a = withReassign - print(self.a) - } -} - -@Wrapper -public class ContextUserDefinedInitClassTest { - public var a: Int - @PropWrapper public var b: (String, (Int, [V])) - public var c: [K: V] - - public init( - a: Int = 0, - b: (String, (Int, [V])) = ("", (0, [1, 2, 3])), - c: [K: V] = [:], - placeholder: (K, V)? = nil - ) { - self.a = a - self.b = b - self.c = c - - print(self.c) - - if let placeholder { - self.c[placeholder.0] = placeholder.1 - } - - print(self.c) - } -} - -@Wrapper -public class ContextUserDefinedInitStructTest { - public var a: Int - @PropWrapper public var b: (String, (Int, [V])) - public var c: [K: V] - - public init( - a: Int = 0, - b: (String, (Int, [V])) = ("", (0, [1.0, 2.0, 3.0])), - c: [K: V] = [:], - placeholder: (K, V)? = nil - ) { - self.a = a - self.b = b - self.c = c - - print(self.c) - - if let placeholder { - self.c[placeholder.0] = placeholder.1 - } - - print(self.c) - } -} - -@Wrapper -public class UserDefinedInitWithConditionalTest { - var val: T? - - public init(cond: Bool = true, initialValue: T? = nil) { - if (cond) { - if let initialValue { - self.val = initialValue - } else { - self.val = nil - } - } else { - self.val = nil - } - - print(self.val) - } -} - -@Wrapper -public class ClassWithConvenienceInit { - public var a: T? - public var b: String = "" - - public init(aWithoutB: T?) { - self.a = aWithoutB - } - - init(a: T?, b: String) { - // Just to test that conditionals work properly - if let a { - self.a = a - } else { - self.a = nil - } - - self.b = b - } - - public convenience init() { - self.init(val: nil) - print(self.a) - print(self.b) - } - - public convenience init(val: T?) { - self.init(a: val, b: "") - print(self.a) - print(self.b) - - self.b = "" - print(self.b) - } -} - -@Wrapper -public struct TypeWithLetProperties { - let a: T - let b: Int - - public init(a: T, b: Int? = nil, onSet: (() -> Void)? = nil) { - self.a = a - if let b { - self.b = b - } else { - self.b = 0 - } - - print("--Before onSet--") - - print(self.a) - print(self.b) - - if let onSet { - onSet() - - print("--After onSet--") - print(self.a) - print(self.b) - } - } -} - -@Wrapper -public class TypeWithDefaultedLetProperties { - let a: T? = nil - let b: Int = 0 - - public init() { - print(self.a) - print(self.b) - } -} - -@Wrapper -public class TypeWithSomeDefaultedLetProperties { - let a: T - let b: Int? = 0 - @PropWrapper var c: String = "" - @PropWrapperWithoutInit(value: [1, ""]) var d: [Any] - - public init(a: T) { - self.a = a - self.c = "a" - - print(self.a) - print(self.b) - print(self.c) - } -} - -@Wrapper -public protocol WrappedProtocol { - associatedtype T : RangeReplaceableCollection - - var v: T { get set } -} - -@propertyWrapper -public struct PropWrapperWithDefaultInit { - typealias Wrapped = T - - public var wrappedValue: T { - get { value! } - set { value = newValue } - } - - var value: T? - - public init() {} - - public init(wrappedValue: T) { - self.value = wrappedValue - } -} - -@typeWrapper -public struct WrapperWithConformance { - var underlying: S - - public init(for wrappedType: W.Type, storage: S) { - print("WrapperWithConformance.init(for: \(wrappedType), storage: \(storage))") - self.underlying = storage - } - - public subscript(propertyKeyPath propertyPath: KeyPath, - storageKeyPath storagePath: KeyPath) -> V { - get { - print("in read-only getter storage: \(storagePath)") - return underlying[keyPath: storagePath] - } - } - - public subscript(propertyKeyPath propertyPath: KeyPath, - storageKeyPath storagePath: WritableKeyPath) -> V { - get { - print("in getter storage: \(storagePath)") - return underlying[keyPath: storagePath] - } - set { - print("in setter => \(newValue)") - underlying[keyPath: storagePath] = newValue - } - } -} - -public protocol WrappedTypesOnly {} diff --git a/test/Interpreter/type_wrapper_with_actors.swift b/test/Interpreter/type_wrapper_with_actors.swift deleted file mode 100644 index 700480491613c..0000000000000 --- a/test/Interpreter/type_wrapper_with_actors.swift +++ /dev/null @@ -1,51 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: %target-build-swift -target %target-cpu-apple-macosx10.15 -enable-experimental-feature TypeWrappers -parse-as-library -emit-library -emit-module-path %t/type_wrapper_defs.swiftmodule -module-name type_wrapper_defs %S/Inputs/type_wrapper_defs.swift -o %t/%target-library-name(type_wrapper_defs) -// RUN: %target-build-swift -target %target-cpu-apple-macosx10.15 -ltype_wrapper_defs -module-name main -I %t -L %t %s -o %t/main %target-rpath(%t) -// RUN: %target-codesign %t/main -// RUN: %target-run %t/main %t/%target-library-name(type_wrapper_defs) | %FileCheck %s - -// REQUIRES: executable_test -// REQUIRES: asserts -// REQUIRES: concurrency - -// rdar://76038845 -// REQUIRES: concurrency_runtime -// UNSUPPORTED: back_deployment_runtime - -// REQUIRES: OS=macosx - -// This requires executable tests to be run on the same machine as the compiler, -// as it links with a dylib that it doesn't arrange to get uploaded to remote executors. -// (rdar://99051588) -// UNSUPPORTED: remote_run || device_run - -// FIXME: There is no way to form a key path to an actor isolated property (rdar://84445219) -// REQUIRES: rdar84445219 - -import type_wrapper_defs - -@Wrapper -public actor Actor { - public var name: String - @PropWrapper public var age: Int? = nil - - public func setAge(_ newAge: Int) async { - age = newAge - } -} - -let a = Actor(name: "Arhtur Dent") -await print(a.name) -// CHECK: in getter -// CHECK-NEXT: Arhtur Dent -await print(a.age) -// CHECK: in getter -// CHECK-NEXT: nil - -await a.setAge(30) -// CHECK: in getter -// CHECK-NEXT: in setter => PropWrapper>(value: Optional(30)) - -await print(a.age) -// CHECK: in getter -// CHECK-NEXT: 30 diff --git a/test/Interpreter/type_wrappers.swift b/test/Interpreter/type_wrappers.swift deleted file mode 100644 index 2029381153e1d..0000000000000 --- a/test/Interpreter/type_wrappers.swift +++ /dev/null @@ -1,726 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: %target-build-swift -enable-experimental-feature TypeWrappers -parse-as-library -emit-library -emit-module-path %t/type_wrapper_defs.swiftmodule -module-name type_wrapper_defs %S/Inputs/type_wrapper_defs.swift -o %t/%target-library-name(type_wrapper_defs) -// RUN: %target-build-swift -ltype_wrapper_defs -module-name main -I %t -L %t %s -o %t/main %target-rpath(%t) -// RUN: %target-codesign %t/main -// RUN: %target-run %t/main %t/%target-library-name(type_wrapper_defs) | %FileCheck %s - -// REQUIRES: executable_test -// REQUIRES: asserts - -// This requires executable tests to be run on the same machine as the compiler, -// as it links with a dylib that it doesn't arrange to get uploaded to remote executors. -// (rdar://99051588) -// UNSUPPORTED: remote_run || device_run - -import type_wrapper_defs - -var p: Person = .init(name: "P", projects: ["A", "B"]) -// CHECK: Wrapper.init(for: Person, storage: $Storage(name: "P", projects: ["A", "B"])) - -print(p.name) -// CHECK: in (reference type) getter storage: \$Storage.name -// CHECK-NEXT: P -print(p.projects) -// CHECK: in (reference type) getter storage: \$Storage.projects -// CHECK-NEXT: ["A", "B"] - -p.name = "OtherP" -// CHECK: in (reference type) setter => OtherP -p.projects.append("C") -// CHECK: in (reference type) getter storage: \$Storage.projects -// CHECK-NEXT: in (reference type) setter => ["A", "B", "C"] - - -func addProjects(p: inout Person, _ newProjects: [T]) { - p.projects.append(contentsOf: newProjects) -} - -addProjects(p: &p, ["D"]) -// CHECK: in (reference type) getter storage: \$Storage.projects -// CHECK: in (reference type) setter => ["A", "B", "C", "D"] - -print(p.name) -// CHECK: in (reference type) getter storage: \$Storage.name -// CHECK-NEXT: OtherP - -print(p.projects) -// CHECK: in (reference type) getter storage: \$Storage.projects -// CHECK-NEXT: ["A", "B", "C", "D"] - -var pDefaults = PersonWithDefaults() -// CHECK: Wrapper.init(for: PersonWithDefaults, storage: $Storage(name: "", age: 99)) - -print(pDefaults.name) -// CHECK: in getter storage: \$Storage.name -// CHECK: - -print(pDefaults.age) -// CHECK: in getter storage: \$Storage.age -// CHECK: 99 - -pDefaults.name = "Actual Name" -// CHECK-NEXT: in setter => Actual Name - -pDefaults.age = 0 -// CHECK-NEXT: in setter => 0 - -print(pDefaults.name) -// CHECK: in getter storage: \$Storage.name -// CHECK: Actual Name - -print(pDefaults.age) -// CHECK: in getter storage: \$Storage.age -// CHECK: 0 - -let pDefaultsAge = PersonWithDefaults(name: "Actual Name") - -print(pDefaultsAge.name) -// CHECK: in getter storage: \$Storage.name -// CHECK: Actual Name - -print(pDefaultsAge.age) -// CHECK: in getter storage: \$Storage.age -// CHECK: 99 - -let pDefaultsName = PersonWithDefaults(age: 31337) - -print(pDefaultsName.name) -// CHECK: in getter storage: \$Storage.name -// CHECK: - -print(pDefaultsName.age) -// CHECK: in getter storage: \$Storage.age -// CHECK: 31337 - -func testPropertyWrappers() { - var wrapped1 = PropWrapperTest(test: 42) - // CHECK: Wrapper.init(for: PropWrapperTest, storage: $Storage(_test: type_wrapper_defs.PropWrapper(value: 42))) - do { - print(wrapped1.test) - // CHECK: in getter storage: \$Storage._test - // CHECK-NEXT: 42 - - wrapped1.test = 0 - // CHECK: in getter storage: \$Storage._test - // CHECK-NEXT: in setter => PropWrapper(value: 0) - - print(wrapped1.test) - // CHECK: in getter storage: \$Storage._test - // CHECK-NEXT: 0 - } - - var wrapped2 = DefaultedPropWrapperTest() - // CHECK: Wrapper.init(for: DefaultedPropWrapperTest, storage: $Storage(_test: type_wrapper_defs.PropWrapper(value: 0))) - do { - print(wrapped2.test) - // CHECK: in getter storage: \$Storage._test - // CHECK-NEXT: 0 - - wrapped2.test = 42 - // CHECK: in getter storage: \$Storage._test - // CHECK-NEXT: in setter => PropWrapper(value: 42) - - print(wrapped2.test) - // CHECK: in getter storage: \$Storage._test - // CHECK-NEXT: 42 - } - - var wrapped3 = DefaultedPropWrapperTest(test: 1) - // CHECK: Wrapper.init(for: DefaultedPropWrapperTest, storage: $Storage(_test: type_wrapper_defs.PropWrapper(value: 1))) - do { - print(wrapped3.test) - // CHECK: in getter storage: \$Storage._test - // CHECK-NEXT: 1 - - wrapped3.test = 31337 - // CHECK: in getter storage: \$Storage._test - // CHECK-NEXT: in setter => PropWrapper(value: 31337) - - print(wrapped3.test) - // CHECK: in getter - // CHECK-NEXT: 31337 - } - - var wrapped4 = DefaultedPropWrapperWithArgTest() - // CHECK: Wrapper.init(for: DefaultedPropWrapperWithArgTest, storage: $Storage(_test: type_wrapper_defs.PropWrapper(value: 3))) - do { - print(wrapped4.test) - // CHECK: in getter storage: \$Storage._test - // CHECK-NEXT: 3 - - wrapped4.test = 0 - // CHECK: in getter storage: \$Storage._test - // CHECK-NEXT: in setter => PropWrapper(value: 0) - - print(wrapped4.test) - // CHECK: in getter storage: \$Storage._test - // CHECK-NEXT: 0 - } - - var wrapped5 = PropWrapperNoInitTest(a: PropWrapperWithoutInit(value: 1)) - // CHECK: Wrapper.init(for: PropWrapperNoInitTest, storage: $Storage(_a: type_wrapper_defs.PropWrapperWithoutInit(value: 1), _b: type_wrapper_defs.PropWrapperWithoutInit(value: "b"))) - do { - print(wrapped5.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: 1 - - print(wrapped5.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: b - - wrapped5.a = 42 - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: in setter => PropWrapperWithoutInit(value: 42) - - wrapped5.b = "not b" - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: in setter => PropWrapperWithoutInit(value: "not b") - - print(wrapped5.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: 42 - - print(wrapped5.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: not b - } - - var wrapped6 = PropWrapperNoInitTest(a: PropWrapperWithoutInit(value: 1), b: PropWrapperWithoutInit(value: "hello")) - // CHECK: Wrapper.init(for: PropWrapperNoInitTest, storage: $Storage(_a: type_wrapper_defs.PropWrapperWithoutInit(value: 1), _b: type_wrapper_defs.PropWrapperWithoutInit(value: "hello"))) - do { - print(wrapped6.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: 1 - - print(wrapped6.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: hello - - wrapped6.a = 42 - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: in setter => PropWrapperWithoutInit(value: 42) - - wrapped6.b = "b" - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: in setter => PropWrapperWithoutInit(value: "b") - - print(wrapped6.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: 42 - - print(wrapped6.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: b - } - - var wrapped7 = ComplexPropWrapperTest() - // CHECK: Wrapper.init(for: ComplexPropWrapperTest, storage: $Storage(_a: type_wrapper_defs.PropWrapper>(value: ["a"]), _b: type_wrapper_defs.PropWrapperWithoutInit>>(value: type_wrapper_defs.PropWrapper>(value: [1, 2, 3])))) - do { - print(wrapped7.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: ["a"] - - print(wrapped7.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: [1, 2, 3] - - wrapped7.a = ["a", "b", "c"] - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: in setter => PropWrapper>(value: ["a", "b", "c"]) - - print(wrapped7.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: ["a", "b", "c"] - - wrapped7.b = [0] - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: in setter => PropWrapperWithoutInit>>(value: type_wrapper_defs.PropWrapper>(value: [0])) - - print(wrapped7.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: [0] - } - - var wrapped8 = ComplexPropWrapperTest(a: ["a", "b"]) - // CHECK: Wrapper.init(for: ComplexPropWrapperTest, storage: $Storage(_a: type_wrapper_defs.PropWrapper>(value: ["a", "b"]), _b: type_wrapper_defs.PropWrapperWithoutInit>>(value: type_wrapper_defs.PropWrapper>(value: [1, 2, 3])))) - do { - print(wrapped8.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: ["a", "b"] - - print(wrapped8.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: [1, 2, 3] - - wrapped8.a = ["a", "b", "c"] - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: in setter => PropWrapper>(value: ["a", "b", "c"]) - - print(wrapped8.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: ["a", "b", "c"] - - wrapped8.b = [0] - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: in setter => PropWrapperWithoutInit>>(value: type_wrapper_defs.PropWrapper>(value: [0])) - - print(wrapped8.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: [0] - } - - var wrapped9 = ComplexPropWrapperTest(b: PropWrapperWithoutInit(value: PropWrapper(wrappedValue: [0]))) - // CHECK: Wrapper.init(for: ComplexPropWrapperTest, storage: $Storage(_a: type_wrapper_defs.PropWrapper>(value: ["a"]), _b: type_wrapper_defs.PropWrapperWithoutInit>>(value: type_wrapper_defs.PropWrapper>(value: [0])))) - do { - print(wrapped9.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: ["a"] - - print(wrapped9.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: [0] - - wrapped9.a = ["a", "b", "c"] - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: in setter => PropWrapper>(value: ["a", "b", "c"]) - - print(wrapped9.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: ["a", "b", "c"] - - wrapped9.b = [1, 2, 3] - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: in setter => PropWrapperWithoutInit>>(value: type_wrapper_defs.PropWrapper>(value: [1, 2, 3])) - - print(wrapped9.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: [1, 2, 3] - } - - var wrapped10 = ComplexPropWrapperTest(a: [], b: PropWrapperWithoutInit(value: PropWrapper(wrappedValue: [0]))) - // CHECK: Wrapper.init(for: ComplexPropWrapperTest, storage: $Storage(_a: type_wrapper_defs.PropWrapper>(value: []), _b: type_wrapper_defs.PropWrapperWithoutInit>>(value: type_wrapper_defs.PropWrapper>(value: [0])))) - do { - print(wrapped10.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: [] - - print(wrapped10.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: [0] - - wrapped10.a = ["a", "b", "c"] - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: in setter => PropWrapper>(value: ["a", "b", "c"]) - - print(wrapped10.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: ["a", "b", "c"] - - wrapped10.b = [1, 2, 3] - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: in setter => PropWrapperWithoutInit>>(value: type_wrapper_defs.PropWrapper>(value: [1, 2, 3])) - - print(wrapped10.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: [1, 2, 3] - } - - var wrapped11 = PropWrapperNoProjectionTest() - // CHECK: Wrapper.init(for: PropWrapperNoProjectionTest, storage: $Storage(_a: type_wrapper_defs.PropWrapperWithoutProjection(value: 0), _b: type_wrapper_defs.PropWrapperWithoutProjection>(value: type_wrapper_defs.PropWrapper(value: "b")))) - do { - print(wrapped11.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: 0 - - print(wrapped11.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: b - - wrapped11.a = 42 - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: in setter => PropWrapperWithoutProjection(value: 42) - - wrapped11.b = "not b" - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: in setter => PropWrapperWithoutProjection>(value: type_wrapper_defs.PropWrapper(value: "not b")) - - print(wrapped11.a) - // CHECK: in getter storage: \$Storage._a - // CHECK-NEXT: 42 - - print(wrapped11.b) - // CHECK: in getter storage: \$Storage._b - // CHECK-NEXT: not b - } -} - -testPropertyWrappers() - -do { - var person = PersonWithUnmanagedTest(name: "Arthur Dent") - // CHECK: Wrapper.init(for: PersonWithUnmanagedTest, storage: $Storage(name: "Arthur Dent", _favoredColor: type_wrapper_defs.PropWrapper(value: "red"))) - - print(person.name) - // CHECK: in read-only getter storage: \$Storage.name - // CHECK-NEXT: Arthur Dent - - print(person.age) - // CHECK: 30 - - print(person.placeOfBirth) - // CHECK: Earth - - print(person.favoredColor) - // CHECK: in getter storage: \$Storage._favoredColor - // CHECK-NEXT: red - - person.favoredColor = "yellow" - // CHECK: in getter storage: \$Storage._favoredColor - // CHECK-NEXT: in setter => PropWrapper(value: "yellow") - - print(person.favoredColor) - // CHECK: in getter storage: \$Storage._favoredColor - // CHECK-NEXT: yellow -} - -do { - var test = ClassWithDesignatedInit(a: 42) - - print(test.a) - // CHECK: in (reference type) getter storage: \$Storage.a - // CHECK-NEXT: 42 - - print(test.b) - // CHECK: in (reference type) getter storage: \$Storage._b - // CHECK-NEXT: [1, 2, 3] - - test.a = 0 - // CHECK: in (reference type) setter => 0 - - test.b = [42] - // CHECK: in (reference type) getter storage: \$Storage._b - // CHECK-NEXT: in (reference type) setter => PropWrapperWithoutInit>(value: [42]) - - print(test.a) - // CHECK: in (reference type) getter storage: \$Storage.a - // CHECK-NEXT: 0 - - print(test.b) - // CHECK: in (reference type) getter storage: \$Storage._b - // CHECK-NEXT: [42] -} - -do { - var arthur = PersonWithIgnoredAge(name: "Arthur Dent", age: 30) - // CHECK: Wrapper.init(for: PersonWithIgnoredAge, storage: $Storage(name: "Arthur Dent")) - - print(arthur.name) - // CHECK: in (reference type) getter storage: \$Storage.name - // CHECK-NEXT: Arthur Dent - - print(arthur.age) - // CHECK-NOT: in getter - // CHECK-NEXT: 30 - - print(arthur.manufacturer) - // CHECK-NOT: in getter - // CHECK-NEXT: nil - - arthur.age = 32 - // CHECK-NOT: in setter - - var marvin = PersonWithIgnoredAge(name: "Marvin The Paranoid Android", manufacturer: "Sirius Cybernetics Corporation") - // CHECK: Wrapper.init(for: PersonWithIgnoredAge, storage: $Storage(name: "Marvin The Paranoid Android")) - - print(marvin.name) - // CHECK: in (reference type) getter storage: \$Storage.name - // CHECK-NEXT: Marvin The Paranoid Android - - print(marvin.age) - // CHECK-NOT: in getter - // CHECK-NEXT: 0 - - print(marvin.manufacturer) - // CHECK-NOT: in getter - // CHECK-NEXT: Sirius Cybernetics Corporation - - marvin.age = 1000 - // CHECK-NOT: in setter - - marvin.manufacturer = nil - // CHECK-NOT: in setter -} - -// user-defined init tests -do { - _ = EmptyUserDefinedInitClassTest() - // CHECK: Wrapper.init(for: EmptyUserDefinedInitClassTest, storage: $Storage()) - _ = EmptyUserDefinedInitStructTest() - // CHECK: Wrapper.init(for: EmptyUserDefinedInitStructTest, storage: $Storage()) - - _ = TrivialUserDefinedInitClassTest(a: 42) - // CHECK: Wrapper.init(for: TrivialUserDefinedInitClassTest, storage: $Storage(a: 42)) - - _ = TrivialUserDefinedInitClassTest(withReassign: 42) - // CHECK: Wrapper.init(for: TrivialUserDefinedInitClassTest, storage: $Storage(a: 0)) - // CHECK-NEXT: in (reference type) getter storage: \$Storage.a - // CHECK-NEXT: 0 - // CHECK-NEXT: in (reference type) setter => 42 - // CHECK-NEXT: in (reference type) getter storage: \$Storage.a - // CHECK-NEXT: 42 - - _ = TrivialUserDefinedInitStructTest(withReassign: 42) - // CHECK: Wrapper.init(for: TrivialUserDefinedInitStructTest, storage: $Storage(a: 0)) - // CHECK-NEXT: in getter storage: \$Storage.a - // CHECK-NEXT: 0 - // CHECK-NEXT: in setter => 42 - // CHECK-NEXT: in getter storage: \$Storage.a - // CHECK-NEXT: 42 - - let complex1 = ContextUserDefinedInitClassTest(c: ["hello": 42], placeholder: ("", -1)) - // CHECK: Wrapper.init(for: ContextUserDefinedInitClassTest, storage: $Storage(a: 0, _b: type_wrapper_defs.PropWrapper<(Swift.String, (Swift.Int, Swift.Array))>(value: ("", (0, [1, 2, 3]))), c: ["hello": 42])) - // CHECK-NEXT: in (reference type) getter storage: \$Storage.c - // CHECK-NEXT: ["hello": 42] - // CHECK-NEXT: in (reference type) getter storage: \$Storage.c - // CHECK-NEXT: in (reference type) setter => [{{.*}}, {{.*}}] - // CHECK-NEXT: in (reference type) getter storage: \$Storage.c - // CHECK-NEXT: [{{.*}}, {{.*}}] - print(complex1.a) - // CHECK-NEXT: in (reference type) getter storage: \$Storage.a - // CHECK-NEXT: 0 - print(complex1.b) - // CHECK-NEXT: in (reference type) getter storage: \$Storage._b - // CHECK-NEXT: ("", (0, [1, 2, 3])) - - if complex1.c == ["hello": 42] { // use of Hashable, Equatable conformances - // CHECK-NEXT: in (reference type) getter storage: \$Storage.c - fatalError("== failed between complex1 dictionaries") - } - - if complex1.c != ["hello": 42, "": -1] { - // CHECK-NEXT: in (reference type) getter storage: \$Storage.c - fatalError("!= failed between complex1 dictionaries") - } - - let complex2 = ContextUserDefinedInitStructTest(b: ("", (0, [1])), c: ["hello": 42], placeholder: ("", -1)) - // CHECK: Wrapper.init(for: ContextUserDefinedInitStructTest, storage: $Storage(a: 0, _b: type_wrapper_defs.PropWrapper<(Swift.String, (Swift.Int, Swift.Array))>(value: ("", (0, [1]))), c: ["hello": 42])) - // CHECK-NEXT: in (reference type) getter storage: \$Storage.c - // CHECK-NEXT: ["hello": 42] - // CHECK-NEXT: in (reference type) getter storage: \$Storage.c - // CHECK-NEXT: in (reference type) setter => [{{.*}}, {{.*}}] - // CHECK-NEXT: in (reference type) getter storage: \$Storage.c - // CHECK-NEXT: [{{.*}}, {{.*}}] - print(complex2.a) - // CHECK-NEXT: in (reference type) getter storage: \$Storage.a - // CHECK-NEXT: 0 - print(complex2.b) - // CHECK-NEXT: in (reference type) getter storage: \$Storage._b - // CHECK-NEXT: ("", (0, [1])) - if complex2.c == ["hello": 42] { // use of Hashable, Equatable conformances - // CHECK-NEXT: in (reference type) getter storage: \$Storage.c - fatalError("== failed between complex2 dictionaries") - } - - if complex2.c != ["hello": 42, "": -1] { - // CHECK-NEXT: in (reference type) getter storage: \$Storage.c - fatalError("!= failed between complex2 dictionaries") - } - - // cond: true, initialValue: nil - _ = UserDefinedInitWithConditionalTest() - // CHECK: Wrapper.init(for: UserDefinedInitWithConditionalTest, storage: $Storage(val: nil)) - // CHECK-NEXT: in (reference type) getter storage: \$Storage.val - // CHECK-NEXT nil - - // initalValue: nil - _ = UserDefinedInitWithConditionalTest<[String: any BinaryInteger]>(cond: true) - // CHECK: Wrapper.init(for: UserDefinedInitWithConditionalTest>, storage: $Storage(val: nil)) - // CHECK-NEXT: in (reference type) getter storage: \$Storage.val - // CHECK-NEXT: nil - - do { - let initialValue = (("a", 42), ("b", 0)) - - _ = UserDefinedInitWithConditionalTest(cond: true, initialValue: initialValue) - // CHECK: Wrapper.init(for: UserDefinedInitWithConditionalTest<((String, Int), (String, Int))>, storage: $Storage(val: Optional((("a", 42), ("b", 0))))) - // CHECK-NEXT: in (reference type) getter storage: \$Storage.val - // CHECK-NEXT: Optional((("a", 42), ("b", 0))) - - _ = UserDefinedInitWithConditionalTest(cond: false, initialValue: initialValue) - // CHECK: Wrapper.init(for: UserDefinedInitWithConditionalTest<((String, Int), (String, Int))>, storage: $Storage(val: nil)) - // CHECK-NEXT: in (reference type) getter storage: \$Storage.val - // CHECK-NEXT: nil - } -} - -do { - let test1 = ClassWithConvenienceInit(val: [1, 2, 3]) - // CHECK: Wrapper.init(for: ClassWithConvenienceInit>, storage: $Storage(a: Optional([1, 2, 3]), b: "")) - // CHECK: in (reference type) getter storage: \$Storage.a - // CHECK-NEXT: [1, 2, 3] - // CHECK-NEXT: in (reference type) getter storage: \$Storage.b - // CHECK-NEXT: - // CHECK-NEXT: in (reference type) setter => - // CHECK-NEXT: in (reference type) getter storage: \$Storage.b - // CHECK-NEXT: - - let test2 = ClassWithConvenienceInit(aWithoutB: [1, ""]) - // CHECK: Wrapper.init(for: ClassWithConvenienceInit>, storage: $Storage(a: Optional([1, ""]), b: "")) - - func test(_ v: T) { - let test1 = ClassWithConvenienceInit<(Int, String, T)>() - test1.a = (-1, "ultimate question", v) - print(test1.a) - } - - test((a: 1, b: 2.0, c: 3)) - // CHECK: Wrapper.init(for: ClassWithConvenienceInit<(Int, String, (a: Int, b: Double, c: Int))>, storage: $Storage(a: nil, b: "")) - // -> from init(a: T?) - // CHECK: in (reference type) getter storage: \$Storage.a - // CHECK-NEXT: nil - // CHECK-NEXT: in (reference type) getter storage: \$Storage.b - // CHECK-NEXT: - // CHECK-NEXT: in (reference type) setter => - // CHECK-NEXT: in (reference type) getter storage: \$Storage.b - // CHECK-NEXT: - // -> from init() - // CHECK-NEXT: in (reference type) getter storage: \$Storage.a - // CHECK-NEXT: nil - // CHECK-NEXT: in (reference type) getter storage: \$Storage.b - // CHECK-NEXT: - // -> from test(_: T) - // CHECK-NEXT: in (reference type) setter => Optional((-1, "ultimate question", (a: 1, b: 2.0, c: 3))) - // CHECK-NEXT: in (reference type) getter storage: \$Storage.a - // CHECK-NEXT: Optional((-1, "ultimate question", (a: 1, b: 2.0, c: 3))) -} - -do { - class X : CustomStringConvertible { - var x: [Int] = [] - - var description: String { - "X(x: \(x))" - } - } - - var arg = X() - - let test1 = TypeWithLetProperties(a: arg, b: 42) { - arg.x.append(1) - } - // CHECK: Wrapper.init(for: TypeWithLetProperties, storage: $Storage(a: X(x: []), b: 42)) - // CHECK-NEXT: --Before onSet-- - // CHECK-NEXT: in read-only getter storage: \$Storage.a - // CHECK-NEXT: X(x: []) - // CHECK-NEXT: in read-only getter storage: \$Storage.b - // CHECK-NEXT: 42 - // CHECK-NEXT: --After onSet-- - // CHECK-NEXT: in read-only getter storage: \$Storage.a - // CHECK-NEXT: X(x: [1]) - // CHECK-NEXT: in read-only getter storage: \$Storage.b - // CHECK-nEXT: 42 - - let test2 = TypeWithLetProperties(a: Optional.some([1, 2, 3])) - // CHECK: Wrapper.init(for: TypeWithLetProperties>>, storage: $Storage(a: Optional([1, 2, 3]), b: 0)) - // CHECK-NEXT: --Before onSet-- - // CHECK-NEXT: in read-only getter storage: \$Storage.a - // CHECK-NEXT: Optional([1, 2, 3]) - // CHECK-NEXT: in read-only getter storage: \$Storage.b - // CHECK-NEXT: 0 - - let test3 = TypeWithDefaultedLetProperties<[String]>() - // CHECK: Wrapper.init(for: TypeWithDefaultedLetProperties>, storage: $Storage(a: nil, b: 0)) - // CHECK-NEXT: in (reference type) let getter storage: \$Storage.a - // CHECK-NEXT: nil - // CHECK-NEXT: in (reference type) let getter storage: \$Storage.b - // CHECK-NEXT: 0 - - let test4 = TypeWithSomeDefaultedLetProperties(a: ["", 1.0]) - // CHECK: Wrapper.init(for: TypeWithSomeDefaultedLetProperties>, storage: $Storage(a: ["", 1.0], b: Optional(0), _c: type_wrapper_defs.PropWrapper(value: ""), _d: type_wrapper_defs.PropWrapperWithoutInit>(value: [1, ""]))) - // CHECK-NEXT: in (reference type) getter storage: \$Storage._c - // CHECK-NEXT: in (reference type) setter => PropWrapper(value: "a") - // CHECK-NEXT: in (reference type) let getter storage: \$Storage.a - // CHECK-NEXT: ["", 1.0] - // CHECK-NEXT: in (reference type) let getter storage: \$Storage.b - // CHECK-NEXT: Optional(0) - // CHECK-NEXT: in (reference type) getter storage: \$Storage._c - // CHECK-NEXT: a -} - -do { - @Wrapper - struct Test { - var a: T - var b: Int - } - - let wrapper = Wrapper(for: Test<[Float?]>.self, storage: Test<[Float?]>.$Storage(a: [1.0], b: 42)) - // CHECK: Wrapper.init(for: Test>>, storage: $Storage(a: [Optional(1.0)], b: 42)) - - var test = Test(storageWrapper: wrapper) - - print(test.a) - print(test.b) - // CHECK-NEXT: in getter storage: \$Storage.a - // CHECK-NEXT: [Optional(1.0)] - // CHECK-NEXT: in getter storage: \$Storage.b - // CHECK-NEXT: 42 -} - -// Type wrapper inference from protocols -do { - struct Test : WrappedProtocol { - var v: [T?] - - init(_ value: T? = nil) { - v = [value] - } - } - - func run_test(_ test: T) { - print(test.v) - print(test.v.count) - } - - run_test(Test(Optional("Hello"))) - // CHECK: Wrapper.init(for: Test, storage: $Storage(v: [Optional("Hello")])) - // CHECK-NEXT: in getter storage: \$Storage.v - // CHECK-NEXT: [Optional("Hello")] - // CHECK-NEXT: in getter storage: \$Storage.v - // CHECK-NEXT: 1 -} - -do { - @Wrapper - class Test { - @PropWrapperWithDefaultInit var x: Int - - required init() {} - - required init(x: Int) { - self.x = x - } - } - - var test = Test(x: 42) - print(test.x) - // CHECK: Wrapper.init(for: Test, storage: $Storage(_x: type_wrapper_defs.PropWrapperWithDefaultInit(value: nil))) - // CHECK-NEXT: in (reference type) getter storage: \$Storage._x - // CHECK-NEXT: in (reference type) setter => PropWrapperWithDefaultInit(value: Optional(42)) - // CHECK-NEXT: in (reference type) getter storage: \$Storage._x - // CHECK-NEXT: 42 -} - -do { - @WrapperWithConformance - struct Test : WrappedTypesOnly { - var a: Int = 42 - var b: String = "" - @PropWrapperWithDefaultInit var c: [Int] - - init() {} - } - - let test = Test() - // CHECK: WrapperWithConformance.init(for: Test, storage: $Storage(a: 42, b: "", _c: type_wrapper_defs.PropWrapperWithDefaultInit>(value: nil))) -} diff --git a/test/ModuleInterface/type_wrappers.swift b/test/ModuleInterface/type_wrappers.swift deleted file mode 100644 index 1af1dfe260105..0000000000000 --- a/test/ModuleInterface/type_wrappers.swift +++ /dev/null @@ -1,124 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: %target-swift-emit-module-interface(%t/TypeWrappers.swiftinterface) %s -module-name TypeWrappers -enable-experimental-feature TypeWrappers -// RUN: %target-swift-typecheck-module-from-interface(%t/TypeWrappers.swiftinterface) -module-name TypeWrappers -// RUN: %FileCheck %s < %t/TypeWrappers.swiftinterface - -// REQUIRES: asserts - -// CHECK: @typeWrapper public struct Wrapper { -// CHECK-NEXT: public init(for: W.Type, storage: S) -// CHECK-NEXT: public subscript(propertyKeyPath _: Swift.KeyPath, storageKeyPath path: Swift.KeyPath) -> V { -// CHECK-NEXT: get -// CHECK-NEXT: } -// CHECK-NEXT: public subscript(propertyKeyPath _: Swift.KeyPath, storageKeyPath path: Swift.WritableKeyPath) -> V { -// CHECK-NEXT: get -// CHECK-NEXT: set -// CHECK-NEXT: } -// CHECK-NEXT: } - -@typeWrapper -public struct Wrapper { - public init(for: W.Type, storage: S) {} - - public subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { - get { fatalError() } - } - - public subscript(propertyKeyPath _: KeyPath, storageKeyPath path: WritableKeyPath) -> V { - get { fatalError() } - set { } - } -} - -// CHECK: @TypeWrappers.Wrapper public class Test where T : Swift.StringProtocol { -// CHECK: public init(a: Swift.Int, b: [T]) -// CHECK: } - -@Wrapper -public class Test { - var a: Int - let b: [T] -} - -@Wrapper -public protocol Wrapped { - init() -} - -public protocol OuterWrapped : Wrapped {} - -// CHECK: @TypeWrappers.Wrapper public struct WithProtocol : TypeWrappers.Wrapped { -// CHECK: public var a: Swift.Int { -// CHECK: get -// CHECK: set -// CHECK: } -// CHECK: public var b: Swift.String { -// CHECK: get -// CHECK: set -// CHECK: } -// CHECK: public init() -// CHECK: public var $storage: TypeWrappers.Wrapper -// CHECK: public struct $Storage { -// CHECK: } -// CHECK: public init(storageWrapper: TypeWrappers.Wrapper) -// CHECK: } - -public struct WithProtocol : Wrapped { - public var a: Int - public var b: String - - public init() { - } -} - -// CHECK: @TypeWrappers.Wrapper final public class ClassWithProtoocol : TypeWrappers.Wrapped { -// CHECK: required public init() -// CHECK: final public var $storage: TypeWrappers.Wrapper -// CHECK: public struct $Storage { -// CHECK: } -// CHECK: public init(storageWrapper: TypeWrappers.Wrapper) -// CHECK: } - -@Wrapper -public final class ClassWithProtoocol : Wrapped { - var test: String = "" - - public required init() {} -} - -// CHECK: @TypeWrappers.Wrapper public struct WithOuterWrapper : TypeWrappers.OuterWrapped { -// CHECK: public init() -// CHECK: public init(_ v: T) -// CHECK: public var $storage: TypeWrappers.Wrapper, TypeWrappers.WithOuterWrapper.$Storage> -// CHECK: public struct $Storage { -// CHECK: } -// CHECK: public init(storageWrapper: TypeWrappers.Wrapper, TypeWrappers.WithOuterWrapper.$Storage>) -// CHECK: } - -@Wrapper -public struct WithOuterWrapper : OuterWrapped { - var test: T? = nil - - public init() {} - - public init(_ v: T) { - self.test = v - } -} - -protocol UnrelatedProtocol { -} - -// CHECK: @TypeWrappers.Wrapper public struct WithComposition { -// CHECK: public init() -// CHECK: public var $storage: TypeWrappers.Wrapper -// CHECK: public struct $Storage { -// CHECK: } -// CHECK: public init(storageWrapper: TypeWrappers.Wrapper) -// CHECK: } -// CHECK: extension TypeWrappers.WithComposition : TypeWrappers.OuterWrapped {} - -@Wrapper -public struct WithComposition : OuterWrapped & UnrelatedProtocol { - public init() {} -} diff --git a/test/SILOptimizer/type_wrapper_definite_init_diagnostics.swift b/test/SILOptimizer/type_wrapper_definite_init_diagnostics.swift deleted file mode 100644 index 9b67add471c61..0000000000000 --- a/test/SILOptimizer/type_wrapper_definite_init_diagnostics.swift +++ /dev/null @@ -1,108 +0,0 @@ -// RUN: %target-swift-frontend -enable-experimental-feature TypeWrappers -enable-copy-propagation=requested-passes-only -emit-sil -primary-file %s -o /dev/null -verify - -// REQUIRES: asserts - -@typeWrapper -struct Wrapper { - var underlying: S - - init(for: W.Type, storage: S) { self.underlying = storage } - - subscript(propertyKeyPath _: KeyPath, - storageKeyPath path: KeyPath) -> V { - get { underlying[keyPath: path] } - } - - subscript(propertyKeyPath _: KeyPath, - storageKeyPath path: WritableKeyPath) -> V { - get { underlying[keyPath: path] } - set { underlying[keyPath: path] = newValue } - } -} - -do { - @Wrapper - struct ImmutableTest1 { - let x: Int - - init(x: Int) { - self.x = x - self.x = 0 // expected-error {{immutable value 'self.x' may only be initialized once}} - } - } - - @Wrapper - class ImmutableTest2 { - let a: Int - let b: String - - init(a: Int, b: String) { - self.a = 0 - self.a = a // expected-error {{immutable value 'self.a' may only be initialized once}} - - self.b = b - self.b = "" // expected-error {{immutable value 'self.b' may only be initialized once}} - } - } - - // FIXME: Diagnostics should mention `self.b` or `self.a` and not `self.$storage` - - @Wrapper - struct ImmutableTest3 { - let a: Int - let b: String - - init() { - self.b = "" // Ok - self.a = 42 // Ok - } - - init(a: Int = 42) { - self.a = a - print(self.a) // expected-error {{'self' used before all stored properties are initialized}} expected-note {{'self.$storage' not initialized}} - self.b = "" - } - - /* FIXME: This test is crashing in findFullInitializationPoints - init(b: String) { - self.b = b - print(self.a) - } - */ - - init(otherB: String?) { - self.a = 0 - - if let b = otherB { - self.b = b - } - } // expected-error {{return from initializer without initializing all stored properties}} expected-note {{'self.$storage' not initialized}} - - init(optB: String? = nil) { - if let optB { - self.b = optB - print(self.b) // expected-error {{'self' used before all stored properties are initialized}} expected-note {{'self.$storage' not initialized}} - } else { - self.b = "" - print(self.b) // expected-error {{'self' used before all stored properties are initialized}} expected-note {{'self.$storage' not initialized}} - } - - self.a = 0 - } - } - - @Wrapper - struct ImmutableReassignmentTest { - let x: Int = 42 - - init(x: Int) { - self.x = x // expected-error {{immutable value 'self.x' may only be initialized once}} - } - - init(optX: Int?) { - if let x = optX { - self.x = x // expected-error {{immutable value 'self.x' may only be initialized once}} - } - } - } -} diff --git a/test/SILOptimizer/type_wrapper_in_user_defined_init_lowering.sil b/test/SILOptimizer/type_wrapper_in_user_defined_init_lowering.sil deleted file mode 100644 index 84ca09acf7b39..0000000000000 --- a/test/SILOptimizer/type_wrapper_in_user_defined_init_lowering.sil +++ /dev/null @@ -1,176 +0,0 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -raw-sil-inst-lowering -enable-experimental-type-wrappers | %FileCheck %s - -sil_stage raw - -import Builtin -import Swift -import SwiftShims - -@typeWrapper class MyWrapper { - @_hasStorage var underlying: S { get set } - init(for: W.Type, storage s: S) - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { get } - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: WritableKeyPath) -> V { get set } - deinit -} - -@MyWrapper struct Test1 { - var a: Int - var b: T - init(a: Int, b: T) - init(a: Int, b: T, otherB: T) -} - -// Test1.a.setter -sil hidden [ossa] @$s4main5Test1V1aSivs : $@convention(method) (Int, @inout Test1) -> () { -bb0(%0 : $Int, %1 : $*Test1): - %2 = tuple () - return %2 : $() -} - -// Test1.b.setter -sil hidden [ossa] @$s4main5Test1V1bxvs : $@convention(method) (@in T, @inout Test1) -> () { -bb0(%0 : $*T, %1 : $*Test1): - destroy_addr %0 : $*T - %2 = tuple () - return %2 : $() -} - -// Test1.init(a:b:) -// -// CHECK-LABEL: sil hidden [ossa] @$s4main5Test1V1a1bACyxGSi_xtcfC -// CHECK: [[LOCAL_STORAGE:%.*]] = project_box %7 : $<τ_0_0> { var (a: Int, b: τ_0_0) } , 0 -// CHECK: [[A_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Int, b: T), 0 -// CHECK-NOT: {{.*}} = assign_by_wrapper {{.*}} -// CHECK: store [[ARG_0:%.*]] to [trivial] [[A_REF]] : $*Int -// -// CHECK: [[T:%.*]] = alloc_stack $T -// CHECK: copy_addr [[ARG_1:%.*]] to [init] [[T]] : $*T -// CHECK: [[B_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Int, b: T), 1 -// CHECK-NOT: {{.*}} = assign_by_wrapper {{.*}} -// CHECK: copy_addr [take] [[T]] to [init] [[B_REF]] : $*T -sil hidden [ossa] @$s4main5Test1V1a1bACyxGSi_xtcfC : $@convention(method) (Int, @in T, @thin Test1.Type) -> @owned Test1 { -bb0(%0 : $Int, %1 : $*T, %2 : $@thin Test1.Type): - %3 = alloc_box $<τ_0_0> { var Test1<τ_0_0> } , var, name "self" - %4 = mark_uninitialized [rootself] %3 : $<τ_0_0> { var Test1<τ_0_0> } - %5 = begin_borrow [lexical] %4 : $<τ_0_0> { var Test1<τ_0_0> } - %6 = project_box %5 : $<τ_0_0> { var Test1<τ_0_0> } , 0 - %9 = alloc_box $<τ_0_0> { var (a: Int, b: τ_0_0) } , var, name "_storage" - %10 = mark_uninitialized [var] %9 : $<τ_0_0> { var (a: Int, b: τ_0_0) } - %11 = begin_borrow [lexical] %10 : $<τ_0_0> { var (a: Int, b: τ_0_0) } - %12 = project_box %11 : $<τ_0_0> { var (a: Int, b: τ_0_0) } , 0 - %13 = begin_access [modify] [unknown] %6 : $*Test1 - %14 = tuple_element_addr %12 : $*(a: Int, b: T), 0 - // function_ref Test1.a.setter - %15 = function_ref @$s4main5Test1V1aSivs : $@convention(method) <τ_0_0> (Int, @inout Test1<τ_0_0>) -> () - %16 = partial_apply [callee_guaranteed] %15(%13) : $@convention(method) <τ_0_0> (Int, @inout Test1<τ_0_0>) -> () - assign_by_wrapper origin type_wrapper, %0 : $Int to [init] %14 : $*Int, set %16 : $@callee_guaranteed (Int) -> () - end_access %13 : $*Test1 - destroy_value %16 : $@callee_guaranteed (Int) -> () - %20 = alloc_stack $T - copy_addr %1 to [init] %20 : $*T - %22 = begin_access [modify] [unknown] %6 : $*Test1 - %23 = tuple_element_addr %12 : $*(a: Int, b: T), 1 - // function_ref Test1.b.setter - %24 = function_ref @$s4main5Test1V1bxvs : $@convention(method) <τ_0_0> (@in τ_0_0, @inout Test1<τ_0_0>) -> () - %25 = partial_apply [callee_guaranteed] %24(%22) : $@convention(method) <τ_0_0> (@in τ_0_0, @inout Test1<τ_0_0>) -> () - assign_by_wrapper origin type_wrapper, %20 : $*T to [init] %23 : $*T, set %25 : $@callee_guaranteed (@in T) -> () - end_access %22 : $*Test1 - destroy_value %25 : $@callee_guaranteed (@in T) -> () - dealloc_stack %20 : $*T - end_borrow %11 : $<τ_0_0> { var (a: Int, b: τ_0_0) } - destroy_value %10 : $<τ_0_0> { var (a: Int, b: τ_0_0) } - %32 = load [copy] %6 : $*Test1 - destroy_addr %1 : $*T - end_borrow %5 : $<τ_0_0> { var Test1<τ_0_0> } - destroy_value %4 : $<τ_0_0> { var Test1<τ_0_0> } - return %32 : $Test1 -} - -// Test1.init(a:b:otherB:) -// -// CHECK-LABEL: sil hidden [ossa] @$s4main5Test1V1a1b6otherBACyxGSi_xxtcfC -// CHECK: [[LOCAL_STORAGE:%.*]] = project_box %8 : $<τ_0_0> { var (a: Int, b: τ_0_0) } , 0 -// -// CHECK: [[A_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Int, b: T), 0 -// CHECK-NOT: {{.*}} = assign_by_wrapper {{.*}} -// CHECK: store [[ARG_0:%.*]] to [trivial] [[A_REF]] : $*Int -// -// CHECK: [[T:%.*]] = alloc_stack $T -// CHECK-NEXT: copy_addr [[ARG_1:%.*]] to [init] [[T]] : $*T -// CHECK: [[B_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Int, b: T), 1 -// CHECK-NOT: {{.*}} = assign_by_wrapper {{.*}} -// CHECK: copy_addr [take] [[T]] to [init] [[B_REF]] : $*T -// -// CHECK: [[OTHER_T:%.*]] = alloc_stack $T -// CHECK-NEXT: copy_addr [[ARG_2:%.*]] to [init] %19 : $*T -// CHECK: [[B_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Int, b: T), 1 -// CHECK-NOT: {{.*}} = assign_by_wrapper {{.*}} -// CHECK: copy_addr [take] [[OTHER_T]] to [[B_REF]] : $*T -// -// CHECK: [[T:%.*]] = alloc_stack $T -// copy_addr [[ARG_1]] to [init] [[T]] : $*T -// CHECK: [[B_SETTER:%.*]] = function_ref @$s4main5Test1V1bxvs : $@convention(method) <τ_0_0> (@in τ_0_0, @inout Test1<τ_0_0>) -> () -// CHECK-NEXT: [[PARTIALY_APPLIED_SETTER:%.*]] = partial_apply [callee_guaranteed] [[B_SETTER]]({{.*}}) : $@convention(method) <τ_0_0> (@in τ_0_0, @inout Test1<τ_0_0>) -> () -// CHECK-NOT: {{.*}} = assign_by_wrapper {{.*}} -// CHECK-NEXT: apply [[PARTIALY_APPLIED_SETTER]]([[T]]) : $@callee_guaranteed (@in T) -> () -sil hidden [ossa] @$s4main5Test1V1a1b6otherBACyxGSi_xxtcfC : $@convention(method) (Int, @in T, @in T, @thin Test1.Type) -> @owned Test1 { -bb0(%0 : $Int, %1 : $*T, %2 : $*T, %3 : $@thin Test1.Type): - %4 = alloc_box $<τ_0_0> { var Test1<τ_0_0> } , var, name "self" - %5 = mark_uninitialized [rootself] %4 : $<τ_0_0> { var Test1<τ_0_0> } - %6 = begin_borrow [lexical] %5 : $<τ_0_0> { var Test1<τ_0_0> } - %7 = project_box %6 : $<τ_0_0> { var Test1<τ_0_0> } , 0 - %11 = alloc_box $<τ_0_0> { var (a: Int, b: τ_0_0) } , var, name "_storage" - %12 = mark_uninitialized [var] %11 : $<τ_0_0> { var (a: Int, b: τ_0_0) } - %13 = begin_borrow [lexical] %12 : $<τ_0_0> { var (a: Int, b: τ_0_0) } - %14 = project_box %13 : $<τ_0_0> { var (a: Int, b: τ_0_0) } , 0 - %15 = begin_access [modify] [unknown] %7 : $*Test1 - %16 = tuple_element_addr %14 : $*(a: Int, b: T), 0 - // function_ref Test1.a.setter - %17 = function_ref @$s4main5Test1V1aSivs : $@convention(method) <τ_0_0> (Int, @inout Test1<τ_0_0>) -> () - %18 = partial_apply [callee_guaranteed] %17(%15) : $@convention(method) <τ_0_0> (Int, @inout Test1<τ_0_0>) -> () - assign_by_wrapper origin type_wrapper, %0 : $Int to [init] %16 : $*Int, set %18 : $@callee_guaranteed (Int) -> () - end_access %15 : $*Test1 - destroy_value %18 : $@callee_guaranteed (Int) -> () - %22 = alloc_stack $T - copy_addr %1 to [init] %22 : $*T - %24 = begin_access [modify] [unknown] %7 : $*Test1 - %25 = tuple_element_addr %14 : $*(a: Int, b: T), 1 - // function_ref Test1.b.setter - %26 = function_ref @$s4main5Test1V1bxvs : $@convention(method) <τ_0_0> (@in τ_0_0, @inout Test1<τ_0_0>) -> () - %27 = partial_apply [callee_guaranteed] %26(%24) : $@convention(method) <τ_0_0> (@in τ_0_0, @inout Test1<τ_0_0>) -> () - assign_by_wrapper origin type_wrapper, %22 : $*T to [init] %25 : $*T, set %27 : $@callee_guaranteed (@in T) -> () - end_access %24 : $*Test1 - destroy_value %27 : $@callee_guaranteed (@in T) -> () - dealloc_stack %22 : $*T - %32 = alloc_stack $T - copy_addr %2 to [init] %32 : $*T - %34 = begin_access [modify] [unknown] %7 : $*Test1 - %35 = tuple_element_addr %14 : $*(a: Int, b: T), 1 - // function_ref Test1.b.setter - %36 = function_ref @$s4main5Test1V1bxvs : $@convention(method) <τ_0_0> (@in τ_0_0, @inout Test1<τ_0_0>) -> () - %37 = partial_apply [callee_guaranteed] %36(%34) : $@convention(method) <τ_0_0> (@in τ_0_0, @inout Test1<τ_0_0>) -> () - assign_by_wrapper origin type_wrapper, %32 : $*T to [assign] %35 : $*T, set %37 : $@callee_guaranteed (@in T) -> () - end_access %34 : $*Test1 - destroy_value %37 : $@callee_guaranteed (@in T) -> () - dealloc_stack %32 : $*T - %42 = alloc_stack $T - copy_addr %1 to [init] %42 : $*T - %44 = begin_access [modify] [unknown] %7 : $*Test1 - %45 = tuple_element_addr %14 : $*(a: Int, b: T), 1 - // function_ref Test1.b.setter - %46 = function_ref @$s4main5Test1V1bxvs : $@convention(method) <τ_0_0> (@in τ_0_0, @inout Test1<τ_0_0>) -> () - %47 = partial_apply [callee_guaranteed] %46(%44) : $@convention(method) <τ_0_0> (@in τ_0_0, @inout Test1<τ_0_0>) -> () - assign_by_wrapper origin type_wrapper, %42 : $*T to [assign_wrapped_value] %45 : $*T, set %47 : $@callee_guaranteed (@in T) -> () - end_access %44 : $*Test1 - destroy_value %47 : $@callee_guaranteed (@in T) -> () - dealloc_stack %42 : $*T - end_borrow %13 : $<τ_0_0> { var (a: Int, b: τ_0_0) } - destroy_value %12 : $<τ_0_0> { var (a: Int, b: τ_0_0) } - %54 = load [copy] %7 : $*Test1 - destroy_addr %2 : $*T - destroy_addr %1 : $*T - end_borrow %6 : $<τ_0_0> { var Test1<τ_0_0> } - destroy_value %5 : $<τ_0_0> { var Test1<τ_0_0> } - return %54 : $Test1 -} \ No newline at end of file diff --git a/test/SILOptimizer/type_wrapper_init_transform.swift b/test/SILOptimizer/type_wrapper_init_transform.swift deleted file mode 100644 index 81dd12c6b0cf5..0000000000000 --- a/test/SILOptimizer/type_wrapper_init_transform.swift +++ /dev/null @@ -1,456 +0,0 @@ -// RUN: %target-swift-frontend -enable-experimental-feature TypeWrappers -module-name test -disable-availability-checking -Xllvm -sil-print-after=definite-init -emit-sil %s -o /dev/null 2>&1 | %FileCheck %s - -// REQUIRES: asserts - -@typeWrapper -public struct Wrapper { - var underlying: S - - public init(for: W.Type, storage: S) { - self.underlying = storage - } - - public subscript(propertyKeyPath _: KeyPath, - storageKeyPath path: KeyPath) -> V { - get { underlying[keyPath: path] } - } - - public subscript(propertyKeyPath _: KeyPath, - storageKeyPath path: WritableKeyPath) -> V { - get { underlying[keyPath: path] } - set { underlying[keyPath: path] = newValue } - } -} - -@Wrapper -struct TrivialStruct { - // CHECK-LABEL: sil hidden [ossa] @$s4test13TrivialStructVACycfC - // CHECK: [[SELF:%.*]] = alloc_stack $TrivialStruct, var, name "self", implicit - // CHECK: [[LOCAL_STORAGE:%.*]] = alloc_stack $(), var, name "_storage", implicit - // CHECK: [[STORAGE_INST:%.*]] = alloc_stack $TrivialStruct.$Storage - // CHECK: [[STORAGE_INIT_REF:%.*]] = function_ref @$s4test13TrivialStructV8$StorageVAEycfC : $@convention(method) (@thin TrivialStruct.$Storage.Type) -> TrivialStruct.$Storage - // CHECK: [[LOCAL_STORAGE_REF:%.*]] = begin_access [read] [unsafe] [[LOCAL_STORAGE]] : $*() - // CHECK: end_access [[LOCAL_STORAGE_REF]] : $*() - // CHECK: [[STORAGE_METATYPE:%.*]] = metatype $@thin TrivialStruct.$Storage.Type - // CHECK: [[STORAGE_INIT_RES:%.*]] = apply [[STORAGE_INIT_REF]]([[STORAGE_METATYPE]]) : $@convention(method) (@thin TrivialStruct.$Storage.Type) -> TrivialStruct.$Storage - // CHECK: store [[STORAGE_INIT_RES]] to [trivial] [[STORAGE_INST]] : $*TrivialStruct.$Storage - // CHECK: [[WRAPPER_INIT_REF:%.*]] = function_ref @$s4test7WrapperV3for7storageACyxq_Gxm_q_tcfC - // CHECK: [[SELF_ACCESS:%.*]] = begin_access [modify] [static] %1 : $*TrivialStruct - // CHECK: [[STORAGE_VAR_REF:%.*]] = struct_element_addr [[SELF_ACCESS]] : $*TrivialStruct, #TrivialStruct.$storage - // CHECK: [[WRAPPED_METATYPE:%.*]] = metatype $@thick TrivialStruct.Type - // CHECK: [[WRAPPER_METATYPE:%.*]] = metatype $@thin Wrapper.Type - // CHECK: [[WRAPPER_INST:%.*]] = alloc_stack $Wrapper - // CHECK: {{.*}} = apply [[WRAPPER_INIT_REF]]([[WRAPPER_INST]], [[WRAPPED_METATYPE]], [[STORAGE_INST]], [[WRAPPER_METATYPE]]) - // CHECK: [[STORAGE_VAR_ACCESS:%.*]] = begin_access [modify] [dynamic] [[STORAGE_VAR_REF]] : $*Wrapper - // CHECK: copy_addr [take] [[WRAPPER_INST]] to [init] [[STORAGE_VAR_ACCESS]] : $*Wrapper - // CHECK: end_access [[STORAGE_VAR_ACCESS]] : $*Wrapper - // CHECK: end_access [[SELF_ACCESS]] : $*TrivialStruct - // CHECK: dealloc_stack [[WRAPPER_INST]] : $*Wrapper - // CHECK: dealloc_stack [[STORAGE_INST]] : $*TrivialStruct.$Storage - // CHECK: dealloc_stack [[LOCAL_STORAGE]] : $*() - init() {} -} - -@Wrapper -struct GenericStruct { - var test: T - - // CHECK-LABEL: sil hidden [ossa] @$s4test13GenericStructV1vACyxGx_tcfC : $@convention(method) (@in T, @thin GenericStruct.Type) -> @out GenericStruct - // CHECK: [[SELF:%.*]] = alloc_stack [lexical] $GenericStruct, var, name "self", implicit - // CHECK: [[LOCAL_STORAGE:%.*]] = alloc_stack [lexical] $(test: T), var, name "_storage", implicit - // CHECK: [[TEST_PROP:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(test: T), 0 - // CHECK: assign_by_wrapper origin type_wrapper, [[FIELD_VALUE:%.*]] : $*T to [init] [[TEST_PROP]] : $*T, set {{.*}} - // CHECK-NEXT: [[STORAGE_INST:%.*]] = alloc_stack $GenericStruct.$Storage - // CHECK: [[STORAGE_INIT_REF:%.*]] = function_ref @$s4test13GenericStructV8$StorageVAaEyx_Gx_tcfC : $@convention(method) <τ_0_0 where τ_0_0 : Collection> (@in τ_0_0, @thin GenericStruct<τ_0_0>.$Storage.Type) -> @out GenericStruct<τ_0_0>.$Storage - // CHECK: [[LOCAL_STORAGE_ACCESS:%.*]] = begin_access [read] [unsafe] [[LOCAL_STORAGE]] : $*(test: T) - // CHECK: [[T_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(test: T), 0 - // CHECK: [[T_VAL:%.*]] = alloc_stack $T - // CHECK: copy_addr [[T_REF]] to [init] [[T_VAL]] : $*T - // CHECK: end_access [[LOCAL_STORAGE_ACCESS]] : $*(test: T) - // CHECK: [[STORAGE_METATYPE:%.*]] = metatype $@thin GenericStruct.$Storage.Type - // CHECK: {{.*}} = apply [[STORAGE_INIT_REF]]([[STORAGE_INST]], [[T_VAL]], [[STORAGE_METATYPE]]) : $@convention(method) <τ_0_0 where τ_0_0 : Collection> (@in τ_0_0, @thin GenericStruct<τ_0_0>.$Storage.Type) -> @out GenericStruct<τ_0_0>.$Storage - // CHECK: [[WRAPPER_INIT_REF:%.*]] = function_ref @$s4test7WrapperV3for7storageACyxq_Gxm_q_tcfC - // CHECK: [[SELF_ACCESS:%.*]] = begin_access [modify] [static] [[SELF]] : $*GenericStruct - // CHECK: [[STORAGE_VAR:%.*]] = struct_element_addr [[SELF_ACCESS]] : $*GenericStruct, #GenericStruct.$storage - // CHECK: [[WRAPPED_METATYPE:%.*]] = metatype $@thick GenericStruct.Type - // CHECK: [[WRAPPER_METATYPE:%.*]] = metatype $@thin Wrapper, GenericStruct.$Storage>.Type - // CHECK: [[WRAPPER_INST:%.*]] = alloc_stack $Wrapper, GenericStruct.$Storage> - // CHECK: {{.*}} = apply [[WRAPPER_INIT_REF]], GenericStruct.$Storage>([[WRAPPER_INST]], [[WRAPPED_METATYPE]], [[STORAGE_INST]], [[WRAPPER_METATYPE]]) - // CHECK: [[STORAGE_VAR_ACCESS:%.*]] = begin_access [modify] [dynamic] [[STORAGE_VAR]] : $*Wrapper, GenericStruct.$Storage> - // CHECK: copy_addr [take] [[WRAPPER_INST]] to [init] [[STORAGE_VAR_ACCESS]] : $*Wrapper, GenericStruct.$Storage> - // CHECK: end_access [[STORAGE_VAR_ACCESS]] : $*Wrapper, GenericStruct.$Storage> - init(v: T) { - self.test = v - } -} - -// The only difference between wrapped class and struct is how `self.$storage` is referenced. -@Wrapper -class GenericClass { - var test: T - - // CHECK-LABEL: sil hidden [ossa] @$s4test12GenericClassC1vACyxGx_tcfc : $@convention(method) (@in T, @owned GenericClass) -> @owned GenericClass - // CHECK: [[LOCAL_STORAGE:%.*]] = alloc_stack [lexical] $(test: T), var, name "_storage", implicit - // CHECK: [[TEST_PROP:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(test: T), 0 - // CHECK: assign_by_wrapper origin type_wrapper, [[TEST_VAL:%.*]] : $*T to [init] [[TEST_PROP]] : $*T, set {{.*}} - // CHECK: [[SELF_ACCESS:%.*]] = begin_borrow [[SELF:%.*]] : $GenericClass - // CHECK-NEXT: [[STORAGE_VAR_REF:%.*]] = ref_element_addr [[SELF_ACCESS]] : $GenericClass, #GenericClass.$storage - // CHECK: [[WRAPPED_METATYPE:%.*]] = metatype $@thick GenericClass.Type - // CHECK: {{.*}} = apply [[WRAPPER_INIT_REF:%.*]], GenericClass.$Storage>([[WRAPPER_INST:%.*]], [[WRAPPED_METATYPE]], [[STORAGE_INST:%.*]], {{.*}}) - // CHECK: [[STORAGE_VAR_ACCESS:%.*]] = begin_access [modify] [dynamic] [[STORAGE_VAR_REF]] : $*Wrapper, GenericClass.$Storage> - // CHECK-NEXT: copy_addr [take] [[WRAPPER_INST]] to [init] [[STORAGE_VAR_ACCESS]] : $*Wrapper, GenericClass.$Storage> - // CHECK-NEXT: end_access [[STORAGE_VAR_ACCESS]] - // CHECK-NEXT: end_borrow [[SELF_ACCESS]] - init(v: T) { - self.test = v - } -} - -@Wrapper -struct TupleFlatteningTest { - var a: Int - var b: (String, (Int, K)) - var c: [K: V] - var d: V? - - // Make sure that `_storage` is correctly flattened before its elements - // are passed as arguments to $Storage.init(a:b:c:d:) - - // CHECK-LABEL: sil hidden [ossa] @$s4test19TupleFlatteningTestV1a1b1c1dACyxq_GSi_SS_Si_xttSDyxq_Gq_SgtcfC - // CHECK: [[LOCAL_STORAGE:%.*]] = alloc_stack [lexical] $(a: Int, b: (String, (Int, K)), c: Dictionary, d: Optional), var, name "_storage", implicit - // CHECK: [[A_PROP:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Int, b: (String, (Int, K)), c: Dictionary, d: Optional), 0 - // CHECK: assign_by_wrapper origin type_wrapper, {{.*}} : $Int to [init] [[A_PROP]] : $*Int, set {{.*}} : $@callee_guaranteed (Int) -> () - // CHECK: [[B_PROP:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Int, b: (String, (Int, K)), c: Dictionary, d: Optional), 1 - // CHECK: assign_by_wrapper origin type_wrapper, {{.*}} : $*(String, (Int, K)) to [init] [[B_PROP]] : $*(String, (Int, K)), set {{.*}} : $@callee_guaranteed (@owned String, Int, @in K) -> () - // CHECK: [[C_PROP:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Int, b: (String, (Int, K)), c: Dictionary, d: Optional), 2 - // CHECK: assign_by_wrapper origin type_wrapper, {{.*}} : $Dictionary to [init] [[C_PROP]] : $*Dictionary, set {{.*}} : $@callee_guaranteed (@owned Dictionary) -> () - // CHECK: [[D_PROP:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Int, b: (String, (Int, K)), c: Dictionary, d: Optional), 3 - // CHECK: assign_by_wrapper origin type_wrapper, {{.*}} : $*Optional to [init] [[D_PROP]] : $*Optional, set {{.*}} : $@callee_guaranteed (@in Optional) -> () - // CHECK: [[STORAGE_VAR:%.*]] = alloc_stack $TupleFlatteningTest.$Storage - // CHECK: [[STORAGE_INIT_REF:%.*]] = function_ref @$s4test19TupleFlatteningTestV8$StorageV1a1b1c1dAEyxq__GSi_SS_Si_xttSDyxq_Gq_SgtcfC - // CHECK: [[LOCAL_STORAGE_ACCESS:%.*]] = begin_access [read] [unsafe] [[LOCAL_STORAGE:%.*]] : $*(a: Int, b: (String, (Int, K)), c: Dictionary, d: Optional) - // CHECK: [[A_REF:%.*]] = tuple_element_addr {{%[^,]+}} : $*(a: Int, b: (String, (Int, K)), c: Dictionary, d: Optional), 0 - // CHECK: [[A:%.*]] = load [trivial] [[A_REF]] : $*Int - // CHECK: [[B_TUPLE:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(a: Int, b: (String, (Int, K)), c: Dictionary, d: Optional), 1 - // CHECK: [[B_ELT_0_REF:%.*]] = tuple_element_addr [[B_TUPLE]] : $*(String, (Int, K)), 0 - // CHECK: [[B_ELT_0_VAL:%.*]] = load [copy] [[B_ELT_0_REF]] : $*String - // CHECK: [[B_ELT_1_REF:%.*]] = tuple_element_addr [[B_TUPLE]] : $*(String, (Int, K)), 1 - // CHECK: [[B_ELT_1_0_REF:%.*]] = tuple_element_addr [[B_ELT_1_REF]] : $*(Int, K), 0 - // CHECK: [[B_ELT_1_0_VAL:%.*]] = load [trivial] [[B_ELT_1_0_REF]] : $*Int - // CHECK: [[B_ELT_1_1_REF:%.*]] = tuple_element_addr [[B_ELT_1_REF]] : $*(Int, K), 1 - // CHECK: [[K:%.*]] = alloc_stack $K - // CHECK: copy_addr [[B_ELT_1_1_REF]] to [init] [[K]] : $*K - // CHECK: [[C_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(a: Int, b: (String, (Int, K)), c: Dictionary, d: Optional), 2 - // CHECK: [[C_VAL:%.*]] = load [copy] [[C_REF]] : $*Dictionary - // CHECK: [[D_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(a: Int, b: (String, (Int, K)), c: Dictionary, d: Optional), 3 - // CHECK: [[D_VAL:%.*]] = alloc_stack $Optional - // CHECK: copy_addr [[D_REF]] to [init] [[D_VAL]] : $*Optional - // CHECK: end_access [[LOCAL_STORAGE_ACCESS]] : $*(a: Int, b: (String, (Int, K)), c: Dictionary, d: Optional) - // CHECK: [[STORAGE_METATYPE:%.*]] = metatype $@thin TupleFlatteningTest.$Storage.Type - // CHECK: {{.*}} = apply [[STORAGE_INIT_REF]]([[STORAGE_INST:%.*]], [[A]], [[B_ELT_0_VAL]], [[B_ELT_1_0_VAL]], [[K]], [[C_VAL]], [[D_VAL]], [[STORAGE_METATYPE]]) : $@convention(method) <τ_0_0, τ_0_1 where τ_0_0 : Collection, τ_0_0 : Hashable> (Int, @owned String, Int, @in τ_0_0, @owned Dictionary<τ_0_0, τ_0_1>, @in Optional<τ_0_1>, @thin TupleFlatteningTest<τ_0_0, τ_0_1>.$Storage.Type) -> @out TupleFlatteningTest<τ_0_0, τ_0_1>.$Storage - // CHECK: dealloc_stack [[D_VAL]] : $*Optional - // CHECK: dealloc_stack [[K]] : $*K - init(a: Int, b: (String, (Int, K)), c: [K: V], d: V?) { - self.a = a - self.b = b - self.c = c - self.d = d - } -} - -// Make sure that all the branches have `self.$storage` initialized. - -@Wrapper -class TestConditionalInjection { - var b: T? - - // CHECK-LABEL: sil hidden [ossa] @$s4test24TestConditionalInjectionC4cond1vACyxGSb_xSgtcfc - - // CHECK: [[LOCAL_STORAGE:%.*]] = alloc_stack [lexical] $(b: Optional), var, name "_storage", implicit - - // CHECK: [[COND:%.*]] = struct_extract [[COND_ARG:%.*]] : $Bool, #Bool._value - // CHECK-NEXT: cond_br [[COND]], [[COND_TRUE:bb[0-9]+]], [[COND_FALSE:bb[0-9]+]] - - // CHECK: [[COND_TRUE]]: - // CHECK: copy_addr [[V_ARG:%.*]] to [init] [[V:%.*]] : $*Optional - // CHECK: switch_enum_addr [[V]] : $*Optional, case #Optional.some!enumelt: [[V_SOME:bb[0-9]+]], case #Optional.none!enumelt: [[V_NONE:bb[0-9]+]] - - // CHECK: [[V_NONE]]: - // CHECK-NEXT: destroy_addr [[V]] : $*Optional - // CHECK-NEXT: dealloc_stack [[V]] : $*Optional - // CHECK: br [[STORAGE_INIT_NIL:bb[0-9]+]] - - - // CHECK: [[V_SOME]]: - // CHECK: [[V_VAL:%.*]] = unchecked_take_enum_data_addr [[V]] : $*Optional, #Optional.some!enumelt - // CHECK: copy_addr [take] [[V_VAL:%.*]] to [init] [[T:%.*]] : $*T - // CHECK: br [[STORAGE_INIT_SOME:bb[0-9]+]] - - // CHECK: [[STORAGE_INIT_SOME]]: - // CHECK: [[B_PROP:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(b: Optional), 0 - // CHECK: assign_by_wrapper origin type_wrapper, {{.*}} : $*Optional to [init] [[B_PROP]] : $*Optional, set {{.*}} : $@callee_guaranteed (@in Optional) -> () - // CHECK: [[STORAGE_INIT_REF:%.*]] = function_ref @$s4test24TestConditionalInjectionC8$StorageV1bAEyx_GxSg_tcfC - // CHECK: [[LOCAL_STORAGE_ACCESS:%.*]] = begin_access [read] [unsafe] [[LOCAL_STORAGE:%.*]] : $*(b: Optional) - // CHECK: [[B_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(b: Optional), 0 - // CHECK: [[B_VAL:%.*]] = alloc_stack $Optional - // CHECK: copy_addr [[B_REF]] to [init] [[B_VAL]] : $*Optional - // CHECK: end_access [[LOCAL_STORAGE_ACCESS]] : $*(b: Optional) - // CHECK: {{.*}} = apply [[STORAGE_INIT_REF]]([[STORAGE_INST:%.*]], [[B_VAL]], {{.*}}) : $@convention(method) <τ_0_0> (@in Optional<τ_0_0>, @thin TestConditionalInjection<τ_0_0>.$Storage.Type) -> @out TestConditionalInjection<τ_0_0>.$Storage - // CHECK: [[SELF_ACCESS:%.*]] = begin_borrow [[SELF:%.*]] : $TestConditionalInjection - // CHECK: [[STORAGE_PROP:%.*]] = ref_element_addr [[SELF_ACCESS]] : $TestConditionalInjection, #TestConditionalInjection.$storage - // CHECK: [[WRAPPED_METATYPE:%.*]] = metatype $@thick TestConditionalInjection.Type - // CHECK: [[WRAPPER_INST:%.*]] = alloc_stack $Wrapper, TestConditionalInjection.$Storage> - // CHECK: {{.*}} = apply [[WRAPPER_INIT_REF:%.*]], TestConditionalInjection.$Storage>([[WRAPPER_INST]], [[WRAPPED_METATYPE]], [[STORAGE_INST]], {{.*}}) - // CHECK: [[STORAGE_PROP_ACCESS:%.*]] = begin_access [modify] [dynamic] [[STORAGE_PROP]] - // CHECK: copy_addr [take] [[WRAPPER_INST]] to [init] [[STORAGE_PROP_ACCESS]] - // CHECK: end_access [[STORAGE_PROP_ACCESS]] - // CHECK: end_borrow [[SELF_ACCESS]] - // CHECK: dealloc_stack [[B_VAL]] - // CHECK: dealloc_stack [[STORAGE_INST]] - - // CHECK: [[STORAGE_INIT_NIL]]: - // CHECK: [[B_PROP:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(b: Optional), 0 - // CHECK: assign_by_wrapper origin type_wrapper, {{.*}} : $*Optional to [init] [[B_PROP]] : $*Optional, set {{.*}} : $@callee_guaranteed (@in Optional) -> () - // CHECK: [[STORAGE_INIT_REF:%.*]] = function_ref @$s4test24TestConditionalInjectionC8$StorageV1bAEyx_GxSg_tcfC - // CHECK: [[LOCAL_STORAGE_ACCESS:%.*]] = begin_access [read] [unsafe] [[LOCAL_STORAGE:%.*]] : $*(b: Optional) - // CHECK: [[B_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(b: Optional), 0 - // CHECK: [[B_VAL:%.*]] = alloc_stack $Optional - // CHECK: copy_addr [[B_REF]] to [init] [[B_VAL]] : $*Optional - // CHECK: end_access [[LOCAL_STORAGE_ACCESS]] : $*(b: Optional) - // CHECK: {{.*}} = apply [[STORAGE_INIT_REF]]([[STORAGE_INST:%.*]], [[B_VAL]], {{.*}}) : $@convention(method) <τ_0_0> (@in Optional<τ_0_0>, @thin TestConditionalInjection<τ_0_0>.$Storage.Type) -> @out TestConditionalInjection<τ_0_0>.$Storage - // CHECK: [[SELF_ACCESS:%.*]] = begin_borrow [[SELF:%.*]] : $TestConditionalInjection - // CHECK: [[STORAGE_PROP:%.*]] = ref_element_addr [[SELF_ACCESS]] : $TestConditionalInjection, #TestConditionalInjection.$storage - // CHECK: [[WRAPPED_METATYPE:%.*]] = metatype $@thick TestConditionalInjection.Type - // CHECK: [[WRAPPER_INST:%.*]] = alloc_stack $Wrapper, TestConditionalInjection.$Storage> - // CHECK: {{.*}} = apply [[WRAPPER_INIT_REF:%.*]], TestConditionalInjection.$Storage>([[WRAPPER_INST]], [[WRAPPED_METATYPE]], [[STORAGE_INST]], {{.*}}) - // CHECK: [[STORAGE_PROP_ACCESS:%.*]] = begin_access [modify] [dynamic] [[STORAGE_PROP]] - // CHECK: copy_addr [take] [[WRAPPER_INST]] to [init] [[STORAGE_PROP_ACCESS]] - // CHECK: end_access [[STORAGE_PROP_ACCESS]] - // CHECK: end_borrow [[SELF_ACCESS]] - // CHECK: dealloc_stack [[B_VAL]] - // CHECK: dealloc_stack [[STORAGE_INST]] - - // CHECK: [[COND_FALSE]]: - // CHECK-NEXT: br [[V_CHECK:bb[0-9]+]] - - // CHECK: [[V_CHECK]]: - // CHECK: switch_enum_addr [[V:%.*]] : $*Optional, case #Optional.some!enumelt: [[V_SOME:bb[0-9]+]], case #Optional.none!enumelt: [[V_NONE:bb[0-9]+]] - - // CHECK: [[V_NONE]]: - // CHECK-NEXT: destroy_addr [[V]] : $*Optional - // CHECK-NEXT: dealloc_stack [[V]] : $*Optional - // CHECK: br [[STORAGE_INIT_NIL:bb[0-9]+]] - - - // CHECK: [[V_SOME]]: - // CHECK: [[V_VAL:%.*]] = unchecked_take_enum_data_addr [[V]] : $*Optional, #Optional.some!enumelt - // CHECK: copy_addr [take] [[V_VAL:%.*]] to [init] [[T:%.*]] : $*T - // CHECK: br [[STORAGE_INIT_SOME:bb[0-9]+]] - - // CHECK: [[STORAGE_INIT_SOME]]: - // CHECK: [[B_PROP:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(b: Optional), 0 - // CHECK: assign_by_wrapper origin type_wrapper, {{.*}} : $*Optional to [init] [[B_PROP]] : $*Optional, set {{.*}} : $@callee_guaranteed (@in Optional) -> () - // CHECK: [[STORAGE_INIT_REF:%.*]] = function_ref @$s4test24TestConditionalInjectionC8$StorageV1bAEyx_GxSg_tcfC - // CHECK: [[LOCAL_STORAGE_ACCESS:%.*]] = begin_access [read] [unsafe] [[LOCAL_STORAGE:%.*]] : $*(b: Optional) - // CHECK: [[B_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(b: Optional), 0 - // CHECK: [[B_VAL:%.*]] = alloc_stack $Optional - // CHECK: copy_addr [[B_REF]] to [init] [[B_VAL]] : $*Optional - // CHECK: end_access [[LOCAL_STORAGE_ACCESS]] : $*(b: Optional) - // CHECK: {{.*}} = apply [[STORAGE_INIT_REF]]([[STORAGE_INST:%.*]], [[B_VAL]], {{.*}}) : $@convention(method) <τ_0_0> (@in Optional<τ_0_0>, @thin TestConditionalInjection<τ_0_0>.$Storage.Type) -> @out TestConditionalInjection<τ_0_0>.$Storage - // CHECK: [[SELF_ACCESS:%.*]] = begin_borrow [[SELF:%.*]] : $TestConditionalInjection - // CHECK: [[STORAGE_PROP:%.*]] = ref_element_addr [[SELF_ACCESS]] : $TestConditionalInjection, #TestConditionalInjection.$storage - // CHECK: [[WRAPPED_METATYPE:%.*]] = metatype $@thick TestConditionalInjection.Type - // CHECK: [[WRAPPER_INST:%.*]] = alloc_stack $Wrapper, TestConditionalInjection.$Storage> - // CHECK: {{.*}} = apply [[WRAPPER_INIT_REF:%.*]], TestConditionalInjection.$Storage>([[WRAPPER_INST]], [[WRAPPED_METATYPE]], [[STORAGE_INST]], {{.*}}) - // CHECK: [[STORAGE_PROP_ACCESS:%.*]] = begin_access [modify] [dynamic] [[STORAGE_PROP]] - // CHECK: copy_addr [take] [[WRAPPER_INST]] to [init] [[STORAGE_PROP_ACCESS]] - // CHECK: end_access [[STORAGE_PROP_ACCESS]] - // CHECK: end_borrow [[SELF_ACCESS]] - // CHECK: dealloc_stack [[B_VAL]] - // CHECK: dealloc_stack [[STORAGE_INST]] - - // CHECK: [[STORAGE_INIT_NIL]]: - // CHECK: [[B_PROP:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(b: Optional), 0 - // CHECK: assign_by_wrapper origin type_wrapper, {{.*}} : $*Optional to [init] [[B_PROP]] : $*Optional, set {{.*}} : $@callee_guaranteed (@in Optional) -> () - // CHECK: [[STORAGE_INIT_REF:%.*]] = function_ref @$s4test24TestConditionalInjectionC8$StorageV1bAEyx_GxSg_tcfC - // CHECK: [[LOCAL_STORAGE_ACCESS:%.*]] = begin_access [read] [unsafe] [[LOCAL_STORAGE:%.*]] : $*(b: Optional) - // CHECK: [[B_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(b: Optional), 0 - // CHECK: [[B_VAL:%.*]] = alloc_stack $Optional - // CHECK: copy_addr [[B_REF]] to [init] [[B_VAL]] : $*Optional - // CHECK: end_access [[LOCAL_STORAGE_ACCESS]] : $*(b: Optional) - // CHECK: {{.*}} = apply [[STORAGE_INIT_REF]]([[STORAGE_INST:%.*]], [[B_VAL]], {{.*}}) : $@convention(method) <τ_0_0> (@in Optional<τ_0_0>, @thin TestConditionalInjection<τ_0_0>.$Storage.Type) -> @out TestConditionalInjection<τ_0_0>.$Storage - // CHECK: [[SELF_ACCESS:%.*]] = begin_borrow [[SELF:%.*]] : $TestConditionalInjection - // CHECK: [[STORAGE_PROP:%.*]] = ref_element_addr [[SELF_ACCESS]] : $TestConditionalInjection, #TestConditionalInjection.$storage - // CHECK: [[WRAPPED_METATYPE:%.*]] = metatype $@thick TestConditionalInjection.Type - // CHECK: [[WRAPPER_INST:%.*]] = alloc_stack $Wrapper, TestConditionalInjection.$Storage> - // CHECK: {{.*}} = apply [[WRAPPER_INIT_REF:%.*]], TestConditionalInjection.$Storage>([[WRAPPER_INST]], [[WRAPPED_METATYPE]], [[STORAGE_INST]], {{.*}}) - // CHECK: [[STORAGE_PROP_ACCESS:%.*]] = begin_access [modify] [dynamic] [[STORAGE_PROP]] - // CHECK: copy_addr [take] [[WRAPPER_INST]] to [init] [[STORAGE_PROP_ACCESS]] - // CHECK: end_access [[STORAGE_PROP_ACCESS]] - // CHECK: dealloc_stack [[B_VAL]] - // CHECK: dealloc_stack [[STORAGE_INST]] - - public init(cond: Bool = false, v: T? = nil) { - if cond { - if let v { - self.b = v - } else { - self.b = nil - } - } else { - if let v { - self.b = v - } else { - self.b = nil - } - } - } -} - -/// Make sure that convenience initializers don't get _storage variable injected and don't produce any assign_by_wrapper instructions -@Wrapper -class ClassWithConvenienceInit { - var a: T? - var b: String - - init(a: T?, b: String) { - self.a = a - self.b = b - } - - // CHECK-LABEL: sil hidden [ossa] @$s4test24ClassWithConvenienceInitC1aACyxGxSg_tcfC - // CHECK-NOT: {{.*}} alloc_stack [lexical] $(b: Optional), var, name "_storage", implicit - // CHECK-NOT: assign_by_wrapper {{.*}} - - convenience init(a: T? = nil) { - self.init(a: a, b: "") - - // CHECK: [[A_GETTER:%.*]] = class_method [[SELF:%.*]] : $ClassWithConvenienceInit, #ClassWithConvenienceInit.a!getter - // CHECK-NEXT: {{.*}} = apply [[A_GETTER]]({{.*}}) - _ = self.a - // CHECK: [[B_GETTER:%.*]] = class_method [[SELF:%.*]] : $ClassWithConvenienceInit, #ClassWithConvenienceInit.b!getter - // CHECK-NEXT: {{.*}} = apply [[B_GETTER]]({{.*}}) - _ = self.b - - // CHECK: [[A_SETTER:%.*]] = class_method [[SELF:%.*]] : $ClassWithConvenienceInit, #ClassWithConvenienceInit.a!setter - // CHECK-NEXT: {{.*}} = apply [[A_SETTER]]({{.*}}) - self.a = a - - if let a { - // CHECK: [[B_SETTER:%.*]] = class_method [[SELF:%.*]] : $ClassWithConvenienceInit, #ClassWithConvenienceInit.b!setter - // CHECK-NEXT: {{.*}} = apply [[B_SETTER]]({{.*}}) - self.b = "ultimate question" - } - } -} - -@Wrapper -struct TypeWithLetProperties { - let a: T - let b: Int - - // CHECK-LABEL: sil hidden [ossa] @$s4test21TypeWithLetPropertiesV1a1b5onSetACyxGx_SiSgyycSgtcfC - // CHECK: [[LOCAL_STORAGE:%.*]] = alloc_stack [lexical] $(a: T, b: Int), var, name "_storage", implicit - public init(a: T, b: Int? = nil, onSet: (() -> Void)? = nil) { - // CHECK: [[LOCAL_STORAGE_ACCESS:%.*]] = begin_access [modify] [static] [[LOCAL_STORAGE]] : $*(a: T, b: Int) - // CHECK-NEXT: [[A_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(a: T, b: Int), 0 - // CHECK-NEXT: copy_addr [take] {{%[^,]+}} to [init] [[A_REF]] : $*T - // CHECK-NOT: {{.*}} = assign_by_wrapper {{.*}} - // CHECK-NEXT: end_access [[LOCAL_STORAGE_ACCESS]] - self.a = a - if let b { - // CHECK: [[LOCAL_STORAGE_ACCESS:%.*]] = begin_access [modify] [static] [[LOCAL_STORAGE]] : $*(a: T, b: Int) - // CHECK-NEXT: [[B_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(a: T, b: Int), 1 - // CHECK-NEXT: assign {{.*}} to [init] [[B_REF]] : $*Int - // CHECK-NOT: {{.*}} = assign_by_wrapper {{.*}} - // CHECK-NEXT: end_access [[LOCAL_STORAGE_ACCESS]] : $*(a: T, b: Int) - - // CHECK: [[STORAGE:%.*]] = alloc_stack $TypeWithLetProperties.$Storage - // CHECK: [[STORAGE_INIT_REF:%.*]] = function_ref @$s4test21TypeWithLetPropertiesV8$StorageV1a1bAEyx_Gx_SitcfC - - // CHECK: [[LOCAL_STORAGE_ACCESS:%.*]] = begin_access [read] [unsafe] [[LOCAL_STORAGE]] : $*(a: T, b: Int) - // CHECK-NEXT: [[A_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(a: T, b: Int), 0 - // CHECK-NEXT: [[T:%.*]] = alloc_stack $T - // CHECK-NEXT: copy_addr [[A_REF]] to [init] [[T]] : $*T - // CHECK-NEXT: [[B_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(a: T, b: Int), 1 - // CHECK-NEXT: [[B_VAL:%.*]] = load [trivial] [[B_REF]] : $*Int - // CHECK-NEXT: end_access [[LOCAL_STORAGE_ACCESS]] : $*(a: T, b: Int) - - // CHECK-NEXT: [[STORAGE_METATYPE:%.*]] = metatype $@thin TypeWithLetProperties.$Storage.Type - // CHECK-NEXT: {{.*}} = apply [[STORAGE_INIT_REF]]([[STORAGE]], [[T]], [[B_VAL]], [[STORAGE_METATYPE]]) - - // CHECK: [[WRAPPER_INIT_REF:%.*]] = function_ref @$s4test7WrapperV3for7storageACyxq_Gxm_q_tcfC - // CHECK: [[STORAGE_PROP:%.*]] = struct_element_addr [[SELF_ACCESS:%.*]] : $*TypeWithLetProperties, #TypeWithLetProperties.$storage - // CHECK: [[WRAPPED_METATYPE:%.*]] = metatype $@thick TypeWithLetProperties.Type - // CHECK: [[WRAPPER_INST:%.*]] = alloc_stack $Wrapper, TypeWithLetProperties.$Storage> - // CHECK-NEXT: {{.*}} = apply [[WRAPPER_INIT_REF]], TypeWithLetProperties.$Storage>([[WRAPPER_INST]], [[WRAPPED_METATYPE]], [[STORAGE]], [[WRAPPER_METATYPE:%.*]]) - - // CHECK: [[STORAGE_PROP_ACCESS:%.*]] = begin_access [modify] [dynamic] [[STORAGE_PROP]] - // CHECK-NEXT: copy_addr [take] [[WRAPPER_INST]] to [init] [[STORAGE_PROP_ACCESS]] - // CHECK-NEXT: end_access [[STORAGE_PROP_ACCESS]] - self.b = b - } else { - // CHECK: [[LOCAL_STORAGE_ACCESS:%.*]] = begin_access [modify] [static] [[LOCAL_STORAGE]] : $*(a: T, b: Int) - // CHECK-NEXT: [[B_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(a: T, b: Int), 1 - // CHECK-NEXT: assign {{.*}} to [init] [[B_REF]] : $*Int - // CHECK-NOT: {{.*}} = assign_by_wrapper {{.*}} - // CHECK-NEXT: end_access [[LOCAL_STORAGE_ACCESS]] : $*(a: T, b: Int) - - // CHECK: [[STORAGE:%.*]] = alloc_stack $TypeWithLetProperties.$Storage - // CHECK: [[STORAGE_INIT_REF:%.*]] = function_ref @$s4test21TypeWithLetPropertiesV8$StorageV1a1bAEyx_Gx_SitcfC - - // CHECK: [[LOCAL_STORAGE_ACCESS:%.*]] = begin_access [read] [unsafe] [[LOCAL_STORAGE]] : $*(a: T, b: Int) - // CHECK-NEXT: [[A_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(a: T, b: Int), 0 - // CHECK-NEXT: [[T:%.*]] = alloc_stack $T - // CHECK-NEXT: copy_addr [[A_REF]] to [init] [[T]] : $*T - // CHECK-NEXT: [[B_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(a: T, b: Int), 1 - // CHECK-NEXT: [[B_VAL:%.*]] = load [trivial] [[B_REF]] : $*Int - // CHECK-NEXT: end_access [[LOCAL_STORAGE_ACCESS]] : $*(a: T, b: Int) - - // CHECK-NEXT: [[STORAGE_METATYPE:%.*]] = metatype $@thin TypeWithLetProperties.$Storage.Type - // CHECK-NEXT: {{.*}} = apply [[STORAGE_INIT_REF]]([[STORAGE]], [[T]], [[B_VAL]], [[STORAGE_METATYPE]]) - - // CHECK: [[WRAPPER_INIT_REF:%.*]] = function_ref @$s4test7WrapperV3for7storageACyxq_Gxm_q_tcfC - // CHECK: [[STORAGE_PROP:%.*]] = struct_element_addr [[SELF_ACCESS:%.*]] : $*TypeWithLetProperties, #TypeWithLetProperties.$storage - // CHECK: [[WRAPPED_METATYPE:%.*]] = metatype $@thick TypeWithLetProperties.Type - // CHECK: [[WRAPPER_INST:%.*]] = alloc_stack $Wrapper, TypeWithLetProperties.$Storage> - // CHECK-NEXT: {{.*}} = apply [[WRAPPER_INIT_REF]], TypeWithLetProperties.$Storage>([[WRAPPER_INST]], [[WRAPPED_METATYPE]], [[STORAGE]], [[WRAPPER_METATYPE:%.*]]) - - // CHECK: [[STORAGE_PROP_ACCESS:%.*]] = begin_access [modify] [dynamic] [[STORAGE_PROP]] - // CHECK-NEXT: copy_addr [take] [[WRAPPER_INST]] to [init] [[STORAGE_PROP_ACCESS]] - // CHECK-NEXT: end_access [[STORAGE_PROP_ACCESS]] - self.b = 0 - } - } -} - -@Wrapper -struct TypeWithDefaultedProperties { - let a: [String] - let b: T? = nil - var c: Int = 42 - - // CHECK-LABEL: sil hidden [ossa] @$s4test27TypeWithDefaultedPropertiesV1aACyxGSaySSG_tcfC - // --> Defaults handling - // CHECK: [[LOCAL_STORAGE:%.*]] = alloc_stack [lexical] $(a: Array, b: Optional, c: Int), var, name "_storage", implicit - // CHECK-NEXT: [[A_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Array, b: Optional, c: Int), 0 - // CHECK-NEXT: [[B_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Array, b: Optional, c: Int), 1 - // CHECK-NEXT: [[C_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Array, b: Optional, c: Int), 2 - // CHECK-NEXT: inject_enum_addr [[B_REF]] : $*Optional, #Optional.none!enumelt - // CHECK-NEXT: [[C_DEFAULT_VAL:%.*]] = integer_literal $Builtin.IntLiteral, 42 - // CHECK: [[INT_INIT_REF:%.*]] = function_ref @$sSi22_builtinIntegerLiteralSiBI_tcfC : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int - // CHECK-NEXT: [[C_VAL:%.*]] = apply [[INT_INIT_REF]]([[C_DEFAULT_VAL]], {{.*}}) : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int - // CHECK-NEXT: store [[C_VAL:%.*]] to [trivial] [[C_REF]] : $*Int - // --> Assignment to `let a` - // CHECK: [[LOCAL_STORAGE_ACCESS:%.*]] = begin_access [modify] [static] [[LOCAL_STORAGE]] : $*(a: Array, b: Optional, c: Int) - // CHECK-NEXT: [[A_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE_ACCESS]] : $*(a: Array, b: Optional, c: Int), 0 - // CHECK-NEXT: assign [[A_ARG:%.*]] to [init] {{%[^,]+}} : $*Array - // CHECK-NEXT: end_access [[LOCAL_STORAGE_ACCESS]] - // --> Assignment to `var c`, note that the assignment is done to a wrapped value - // CHECK: [[C_REF:%.*]] = tuple_element_addr [[LOCAL_STORAGE]] : $*(a: Array, b: Optional, c: Int), 2 - // CHECK: assign_by_wrapper origin type_wrapper, {{.*}} : $Int to [assign_wrapped_value] [[C_REF]] : $*Int, set {{.*}} - init(a: [String]) { - self.a = a - self.c = 0 - } -} diff --git a/test/Serialization/type-wrapper-cycle.swift b/test/Serialization/type-wrapper-cycle.swift deleted file mode 100644 index 3e2cf061269b0..0000000000000 --- a/test/Serialization/type-wrapper-cycle.swift +++ /dev/null @@ -1,45 +0,0 @@ -// RUN: %empty-directory(%t) -// RUN: %empty-directory(%t/src) -// RUN: %empty-directory(%t/sdk) -// RUN: split-file %s %t/src - -// REQUIRES: asserts - -// RUN: %target-swift-frontend -emit-module %t/src/PublicModule.swift \ -// RUN: -module-name PublicModule -swift-version 5 \ -// RUN: -emit-module-path %t/sdk/PublicModule.swiftmodule \ -// RUN: -enable-experimental-feature TypeWrappers - -// RUN: %target-swift-frontend -typecheck %t/src/Client.swift \ -// RUN: -enable-experimental-feature TypeWrappers \ -// RUN: -module-name Client -I %t/sdk - -//--- PublicModule.swift -@typeWrapper -public struct Wrapper { - public init(for: W.Type, storage: S) {} - - public subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { - get { fatalError() } - } - - public subscript(propertyKeyPath _: KeyPath, storageKeyPath path: WritableKeyPath) -> V { - get { fatalError() } - set { } - } -} - -@Wrapper -public protocol Wrapped { - init(storageWrapper: Wrapper) -} - -//--- Client.swift -import PublicModule - -struct Test : Wrapped { - var x: Int = 42 -} - -let test = Test() -_ = test.$storage diff --git a/test/Serialization/type_wrapper_in_swiftinterface.swift b/test/Serialization/type_wrapper_in_swiftinterface.swift deleted file mode 100644 index d0cd18f15da49..0000000000000 --- a/test/Serialization/type_wrapper_in_swiftinterface.swift +++ /dev/null @@ -1,95 +0,0 @@ -// RUN: %empty-directory(%t/src) -// RUN: %empty-directory(%t/sdk) -// RUN: split-file %s %t/src - -/// Build the library. -// RUN: %target-swift-frontend -emit-module %t/src/PublicModule.swift \ -// RUN: -module-name PublicModule -swift-version 5 -enable-library-evolution \ -// RUN: -emit-module-path %t/PublicModule.swiftmodule \ -// RUN: -emit-module-interface-path %t/PublicModule.swiftinterface \ -// RUN: -emit-private-module-interface-path %t/PublicModule.private.swiftinterface \ -// RUN: -enable-experimental-feature TypeWrappers - -/// Verify swiftinterfaces. -// RUN: %target-swift-typecheck-module-from-interface(%t/PublicModule.swiftinterface) -module-name PublicModule -// RUN: %target-swift-typecheck-module-from-interface(%t/PublicModule.private.swiftinterface) -module-name PublicModule - -/// Test the client against the binary swiftmodule. -// RUN: %target-swift-frontend -typecheck %t/src/Client.swift \ -// RUN: -enable-experimental-feature TypeWrappers \ -// RUN: -module-name Client -I %t - -/// Test the client against the private swiftinterface. -// RUN: rm %t/PublicModule.swiftmodule -// RUN: %target-swift-frontend -typecheck %t/src/Client.swift \ -// RUN: -module-name Client -I %t \ -// RUN: -enable-experimental-feature TypeWrappers - -/// Test the client against the public swiftinterface. -// RUN: rm %t/PublicModule.private.swiftinterface -// RUN: %target-swift-frontend -typecheck %t/src/Client.swift \ -// RUN: -module-name Client -I %t \ -// RUN: -enable-experimental-feature TypeWrappers - -// REQUIRES: asserts - -//--- PublicModule.swift -@typeWrapper -public struct Wrapper { - public init(for: W.Type, storage: S) {} - - public subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { - get { fatalError() } - } - - public subscript(propertyKeyPath _: KeyPath, storageKeyPath path: WritableKeyPath) -> V { - get { fatalError() } - set { } - } -} - -@Wrapper -public protocol Wrapped {} - -public struct MyWrappedType : Wrapped { - var x: Int = 0 - - public init() {} -} - -@propertyWrapper -public struct PropWrapper { - typealias Wrapped = T - - public var wrappedValue: T { - get { fatalError() } - set { value = newValue } - } - - var value: T? - - public init() {} - - public init(wrappedValue: T) { - self.value = wrappedValue - } -} - -//--- Client.swift - -import PublicModule - -final class S : Wrapped { - @PropWrapper var x: Int - - required init() {} - - required init(x: Int) { - self.x = x - } -} - -let s = S() -_ = s.$storage - -_ = MyWrappedType() diff --git a/test/type/type_wrapper.swift b/test/type/type_wrapper.swift deleted file mode 100644 index 54d1b74b9cb14..0000000000000 --- a/test/type/type_wrapper.swift +++ /dev/null @@ -1,689 +0,0 @@ -// RUN: %target-typecheck-verify-swift -disable-availability-checking -enable-experimental-feature TypeWrappers - -// REQUIRES: asserts - -@typeWrapper -struct ConcreteTypeWrapper { // expected-error {{type wrapper has to declare two generic parameters: wrapped and storage types}} - init(storage: Int) {} -} - -@typeWrapper -struct EmptyTypeWrapper { // expected-error {{type wrapper type 'EmptyTypeWrapper' does not contain a required initializer - init(for:storage:)}} -} - -@typeWrapper -struct NoMemberwiseInit { - // expected-error@-1 {{type wrapper type 'NoMemberwiseInit' does not contain a required initializer - init(for:storage:)}} - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { - get { fatalError() } - } -} - -@typeWrapper -struct FailableInit { - init?(for: W.Type, storage: S) { // expected-error {{type wrapper initializer 'init(for:storage:)' cannot be failable}} - } - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { - get { fatalError() } - } -} - -// Okay because there is a valid `init(for:storage:)` overload. -@typeWrapper -struct FailableAndValidInit { - // expected-error@-1 {{type wrapper type 'FailableAndValidInit' does not contain a required writable subscript}} - // expected-note@-2 {{do you want to add a stub?}} {{36-36=\nsubscript(propertyKeyPath propPath: KeyPath<<#WrappedType#>, Value>, storageKeyPath storagePath: WritableKeyPath<<#Base#>, Value>) -> Value { get { <#code#> \} set { <#code#> \} \}}} - - init(for: W.Type, storage: S) { - } - - init?(for: W.Type, storage: S) where S == Int { - } - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { - get { fatalError() } - } -} - -@typeWrapper -public struct InaccessibleInit { - fileprivate init(for: W.Type, storage: S) { - // expected-error@-1 {{fileprivate initializer 'init(for:storage:)' cannot have more restrictive access than its enclosing type wrapper type 'InaccessibleInit' (which is public)}} - } - - private init?(for: W.Type, storage: S) where S: AnyObject { - // expected-error@-1 {{private initializer 'init(for:storage:)' cannot have more restrictive access than its enclosing type wrapper type 'InaccessibleInit' (which is public)}} - // expected-error@-2 {{type wrapper initializer 'init(for:storage:)' cannot be failable}} - } - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { - get { fatalError() } - } -} - -@typeWrapper -struct NoSubscripts { - // expected-error@-1 {{type wrapper type 'NoSubscripts' does not contain a required subscript - subscript(propertyKeyPath:storageKeyPath:)}} - init(for: W.Type, storage: S) {} -} - -@typeWrapper -struct InaccessibleOrInvalidSubscripts { - init(for: W.Type, storage: S) {} - - fileprivate subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { - // expected-error@-1 {{fileprivate subscript 'subscript(propertyKeyPath:storageKeyPath:)' cannot have more restrictive access than its enclosing type wrapper type 'InaccessibleOrInvalidSubscripts' (which is internal)}} - get { fatalError() } - } - - private subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> [V] { - // expected-error@-1 {{private subscript 'subscript(propertyKeyPath:storageKeyPath:)' cannot have more restrictive access than its enclosing type wrapper type 'InaccessibleOrInvalidSubscripts' (which is internal)}} - get { fatalError() } - } - - private subscript(propertyKeyPath _: KeyPath, storageKeyPath path: WritableKeyPath) -> [V] { - // expected-error@-1 {{private subscript 'subscript(propertyKeyPath:storageKeyPath:)' cannot have more restrictive access than its enclosing type wrapper type 'InaccessibleOrInvalidSubscripts' (which is internal)}} - get { fatalError() } - } - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: Int) -> Bool { // expected-error {{type wrapper subscript parameter 'storageKeyPath' expects a key path (got: 'Int')}} - get { true } - } - - subscript(propertyKeyPath _: Int.Type, storageKeyPath path: KeyPath) -> Bool { - // expected-error@-1 {{type wrapper subscript parameter 'propertyKeyPath' expects a key path (got: 'Int.Type')}} - get { true } - } -} - -@typeWrapper -struct OverloadedCtorWrapper { - init(for: W.Type, storage: S) {} // expected-error {{cannot overload type wrapper initializer 'init(for:storage:)'}} - init(for: W.Type, storage: Int) {} - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> [V] { get { fatalError() } } - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: WritableKeyPath) -> [V] { - get { fatalError() } - set { } - } -} - -do { - @typeWrapper - struct InvalidInit1 { - init(for: Int, storage: S) {} // expected-error {{first parameter of type wrapper initializer should be wrapped type - 'W.Type'}} - } - - @typeWrapper - struct InvalidInit2 { - init(for: W, storage: S) {} // expected-error {{first parameter of type wrapper initializer should be wrapped type - 'W.Type'}} - } - - @typeWrapper - struct InvalidInit3 { - init(for: Int.Type, storage: S) {} // expected-error {{first parameter of type wrapper initializer should be wrapped type - 'W.Type'}} - } - - @typeWrapper - struct InvalidInit4 { - init(for: W.Type, storage: Int) {} // expected-error {{second parameter of type wrapper initializer should be storage type - 'S'}} - } - - @typeWrapper - struct InvalidInit5 { - init(for: W.Type, storage: UnknownType) {} // expected-error {{second parameter of type wrapper initializer should be storage type - 'S'}} - // expected-error@-1 {{cannot find type 'UnknownType' in scope}} - } - - @typeWrapper - struct InvalidInit6 { - init(for: UnknownType.Type, storage: S) {} // expected-error {{first parameter of type wrapper initializer should be wrapped type - 'W.Type'}} - // expected-error@-1 {{cannot find type 'UnknownType' in scope}} - } -} - -@typeWrapper -struct NoopWrapper { - // expected-note@-1 {{arguments to generic parameter 'W' ('Test1' and 'Test2') are expected to be equal}} - // expected-note@-2 {{arguments to generic parameter 'S' ('Test1.$Storage' and 'Test2.$Storage') are expected to be equal}} - - init(for: W.Type, storage: S) {} - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { - get { fatalError() } - } - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: WritableKeyPath) -> V { - get { fatalError() } - set { } - } -} - -@NoopWrapper -struct A { - var a: String - var b: Int -} - -@NoopWrapper -class GenericA { - var data: [K: V] -} - -@NoopWrapper -protocol P { // Ok -} - -@NoopWrapper // expected-error {{type wrapper attribute 'NoopWrapper' can only be applied to a class, struct}} -enum E { - var x: Int { get { 42 } } -} - -func testWrappedTypeAccessChecking() { - let a = A(a: "", b: 42) // synthesized init - let b = GenericA(data: ["ultimate question": 42]) // generic synthesized init - - _ = a.a // Ok - _ = b.data // Ok -} - -struct Parent { - @typeWrapper - struct Wrapper { - init(for: W.Type, storage: S) {} - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { - get { fatalError() } - } - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: WritableKeyPath) -> V { - get { fatalError() } - set { } - } - } -} - -func testLocalWithNestedWrapper() { - @Parent.Wrapper - class WithNestedWrapper { - var test: [T] - - var computed: String { - get { "" } - } - } - - let t = WithNestedWrapper(test: [1, 2]) // synthesized init - _ = t.test // Ok - _ = t.computed // Ok -} - -func testTypeWrapperWithDefaults() { - @NoopWrapper - struct A { - var question: String = "Ultimate Question" - var answer: Int = 42 - } - - let a = A() - _ = a.question - _ = a.answer - - _ = A(question: "") - _ = A(answer: 0) -} - -/// Properties with property wrappers - -@propertyWrapper -struct Wrapper { - public var value: Value - - init(wrappedValue: Value) { - self.value = wrappedValue - } - - var projectedValue: Self { return self } - - var wrappedValue: Value { - get { - self.value - } - set { - self.value = newValue - } - } -} - -@propertyWrapper -struct WrapperWithoutInit { - public var value: Value - - var projectedValue: Self { return self } - - var wrappedValue: Value { - get { - self.value - } - set { - self.value = newValue - } - } -} - -@propertyWrapper -struct WrapperWithoutProjection { - public var value: Value - - init(wrappedValue: Value) { - self.value = wrappedValue - } - - var wrappedValue: Value { - get { - self.value - } - set { - self.value = newValue - } - } -} - -func propertyWrapperTests() { - @NoopWrapper - struct WrapperTest { - @Wrapper var test: Int - } - - _ = WrapperTest(test: 42) // Ok - - @NoopWrapper - struct DefaultedWrapperTest { - @Wrapper var test: Int = 42 - } - - _ = DefaultedWrapperTest() // Ok - _ = DefaultedWrapperTest(test: 42) // Ok - - @NoopWrapper - struct NoInitTest { - @WrapperWithoutInit var test: Int - } - - _ = NoInitTest(test: WrapperWithoutInit(value: 42)) // Ok - - @NoopWrapper - struct DefaultedNoInitTest { - @WrapperWithoutInit(value: 42) var test: Int - } - - _ = DefaultedNoInitTest() // Ok - _ = DefaultedNoInitTest(test: WrapperWithoutInit(value: 0)) // Ok - - @NoopWrapper - struct NoProjection { - @WrapperWithoutProjection var test: Int // expected-note {{'test' declared here}} - } - - let noProj = NoProjection(test: 42) // Ok - _ = noProj.test // Ok - _ = noProj.$test // expected-error {{value of type 'NoProjection' has no member '$test'}} - - @NoopWrapper - struct NoInitComposition1 { - @Wrapper @WrapperWithoutInit var test: Int - } - - _ = NoInitComposition1(test: Wrapper(wrappedValue: WrapperWithoutInit(value: 42))) // Ok - - @NoopWrapper - struct NoInitComposition2 { - @WrapperWithoutInit @Wrapper var test: Int - } - - _ = NoInitComposition2(test: WrapperWithoutInit(value: Wrapper(wrappedValue: 42))) // Ok - - @NoopWrapper - struct NoInitCompositionWithDefault { - @Wrapper(wrappedValue: WrapperWithoutInit(value: 42)) @WrapperWithoutInit var test: Int - } - - _ = NoInitCompositionWithDefault() // Ok - _ = NoInitCompositionWithDefault(test: Wrapper(wrappedValue: WrapperWithoutInit(value: 0))) // Ok - - @NoopWrapper - struct Complex1 { - @Wrapper var a: Int = 42 - var b: String = "" - } - - _ = Complex1() - _ = Complex1(b: "hello") // Ok - _ = Complex1(a: 0) // Ok - _ = Complex1(a: 0, b: "") // Ok - - @NoopWrapper - struct Complex2 { - @Wrapper var a: Int = 42 - @WrapperWithoutInit @Wrapper var b: String - } - - _ = Complex2(b: WrapperWithoutInit(value: Wrapper(wrappedValue: "hello"))) // Ok - _ = Complex2(a: 0, b: WrapperWithoutInit(value: Wrapper(wrappedValue: "hello"))) // Ok - _ = Complex2(b: WrapperWithoutInit(value: Wrapper(wrappedValue: "wrong")), a: 0) // expected-error {{argument 'a' must precede argument 'b'}} - - @NoopWrapper - struct Complex3 { - @Wrapper var a: Int = 42 - var b: String = "" - @WrapperWithoutProjection var c: [Int] = [] - } - - _ = Complex3() - _ = Complex3(b: "hello") // Ok - _ = Complex3(c: [1, 2, 3]) // Ok - _ = Complex3(a: 0) // Ok - _ = Complex3(a: 0, b: "") // Ok - _ = Complex3(a: 0, b: "", c: [1, 2, 3]) // Ok - - @NoopWrapper - struct Invalid { - @Wrapper(wrappedValue: "") var a: Int - // expected-error@-1 {{cannot convert value of type 'String' to expected argument type 'Int'}} - - @Wrapper var b: Int = "" - // expected-error@-1 {{cannot convert value of type 'String' to specified type 'Int'}} - - @Wrapper(wrappedValue: 0) var c: Int = 1 - // expected-error@-1 {{extra argument 'wrappedValue' in call}} - - @Wrapper(other: "") var d: Float - // expected-error@-1 {{incorrect argument label in call (have 'other:', expected 'wrappedValue:')}} - // expected-error@-2 {{cannot convert value of type 'String' to expected argument type 'Float'}} - } -} - -func testDeclarationsWithUnmanagedProperties() { - @NoopWrapper - struct WithLet { // expected-note {{'init(name:age:)' declared here}} - let name: String - var age: Int - } - _ = WithLet(age: 0) // expected-error {{missing argument for parameter 'name' in call}} - _ = WithLet(name: "", age: 0) // Ok - - @NoopWrapper - struct WithDefaultedLet { - let name: String = "Arthur Dent" - var age: Int - } - - _ = WithDefaultedLet(age: 32) // Ok - _ = WithDefaultedLet(name: "", age: 0) // Ok - - @NoopWrapper - struct WithLazy { - lazy var name: String = { - "Arthur Dent" - }() - - var age: Int = 30 - } - - _ = WithLazy() // Ok - _ = WithLazy(name: "") // expected-error {{extra argument 'name' in call}} - _ = WithLazy(name: "", age: 0) // expected-error {{extra argument 'name' in call}} - _ = WithLazy(age: 0) // Ok - - @NoopWrapper - struct OnlyLazyLetAndComputed { - let name: String - lazy var age: Int = { - 30 - }() - var planet: String { - get { "Earth" } - } - } - - _ = OnlyLazyLetAndComputed(name: "Arthur Dent") // Ok -} - -/* (!) FIXME: It's not possible to form a key path to an actor isolated property (rdar://84445219) - * -func testActors() async { - @NoopWrapper - actor Person { - var name: String - note@-1 {{mutation of this property is only permitted within the actor}} - var age: Int - ote@-1 {{mutation of this property is only permitted within the actor}} - } - - let person = Person(name: "Arthur Dent", age: 30) - - _ = await person.name - _ = await person.age - - person.name = "NoName" - error@-1 {{actor-isolated property 'name' can not be mutated from a non-isolated context}} - person.age = 0 - error@-1 {{actor-isolated property 'age' can not be mutated from a non-isolated context}} -} -*/ - -func testIgnoredAttr() { - @NoopWrapper - struct X { - @typeWrapperIgnored static var staticVar: Int = 0 // expected-error {{@typeWrapperIgnored must not be used on static properties}} - - @typeWrapperIgnored lazy var lazyVar: Int = { // expected-error {{@typeWrapperIgnored must not be used on lazy properties}} - 42 - }() - - @typeWrapperIgnored var x: Int // Ok - - var y: Int { - @typeWrapperIgnored get { // expected-error {{@typeWrapperIgnored may only be used on 'var' declarations}} - 42 - } - - @typeWrapperIgnored set { // expected-error {{@typeWrapperIgnored may only be used on 'var' declarations}} - } - } - - @typeWrapperIgnored var computed: Int { // expected-error {{@typeWrapperIgnored must not be used on computed properties}} - get { 42 } - set {} - } - - @typeWrapperIgnored let z: Int = 0 // expected-error {{@typeWrapperIgnored may only be used on 'var' declarations}} - - func test_param(@typeWrapperIgnored x: Int) {} // expected-error {{@typeWrapperIgnored may only be used on 'var' declarations}} - - func test_local() { - @typeWrapperIgnored var x: Int = 42 // expected-error {{@typeWrapperIgnored must not be used on local properties}} - // expected-warning@-1 {{variable 'x' was never used; consider replacing with '_' or removing it}} - - @typeWrapperIgnored let y: String // expected-error {{@typeWrapperIgnored must not be used on local properties}} - // expected-warning@-1 {{immutable value 'y' was never used; consider replacing with '_' or removing it}} - } - } -} - -func testMissingReadOnlyAndWritableSubscriptsAreDiagnosed() { - @typeWrapper - struct MissingReadOnly { - // expected-error@-1 {{type wrapper type 'MissingReadOnly' does not contain a required ready-only subscript}} - // expected-note@-2 {{do you want to add a stub?}} {{33-33=\nsubscript(propertyKeyPath propPath: KeyPath<<#WrappedType#>, Value>, storageKeyPath storagePath: KeyPath<<#Base#>, Value>) -> Value { get { <#code#> \} \}}} - - init(for: W.Type, storage: S) {} - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: WritableKeyPath) -> V { - get { fatalError() } - set { } - } - } - - @typeWrapper - struct MissingWritable { - // expected-error@-1 {{type wrapper type 'MissingWritable' does not contain a required writable subscript}} - // expected-note@-2 {{do you want to add a stub?}} {{33-33=\nsubscript(propertyKeyPath propPath: KeyPath<<#WrappedType#>, Value>, storageKeyPath storagePath: WritableKeyPath<<#Base#>, Value>) -> Value { get { <#code#> \} set { <#code#> \} \}}} - - init(for: W.Type, storage: S) {} - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { - get { fatalError() } - } - } -} - -func testIncorrectUsesOfImmutableProperties() { - class X { - var storage: [T] - - init(storage: [T]) { - self.storage = storage - } - } - - @NoopWrapper - struct Test { - let x: T? // expected-note {{change 'let' to 'var' to make it mutable}} - - init(x: T) { - self.x = x - } - } - - let test = Test(x: X(storage: [1, 2, 3])) - test.x = X(storage: [0]) // expected-error {{cannot assign to property: 'x' is a 'let' constant}} - test.x?.storage.append(0) // Ok -} - -func testWrappedSelfInReferenceOnlySubscript() { - @typeWrapper - struct WrappedSelfTests { - init(for: W.Type, storage: S) {} - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { - get { fatalError() } - set { } - } - - subscript(propertyKeyPath _: KeyPath, storageKeyPath path: WritableKeyPath) -> V { - get { fatalError() } - set { } - } - - subscript(wrappedSelf w: W, propertyKeyPath _: KeyPath, storageKeyPath path: WritableKeyPath) -> V { // Ok - get { fatalError() } - set { } - } - - subscript(wrappedSelf w: W, propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { // Ok - get { fatalError() } - } - - subscript(wrappedSelf w: Int, propertyKeyPath _: KeyPath, storageKeyPath path: KeyPath) -> V { // expected-error {{type wrapper subscript parameter 'wrappedSelf' expects type 'W' (got: 'Int')}} - get { fatalError() } - } - - subscript(wrappedSelf w: Int, propertyKeyPath _: String, storageKeyPath path: [Int]) -> V { - // expected-error@-1 {{type wrapper subscript parameter 'wrappedSelf' expects type 'W' (got: 'Int')}} - // expected-error@-2 {{subscript parameter 'propertyKeyPath' expects a key path (got: 'String')}} - // expected-error@-3 {{type wrapper subscript parameter 'storageKeyPath' expects a key path (got: '[Int]'}} - get { fatalError() } - } - } -} - -// rdar://99884355 - Missing argument for parameter 'name' -do { - struct Use { - var product = Product(name: "") - } - - @NoopWrapper - struct Product { - var name: String - } -} - -do { - @NoopWrapper - struct Test1 { - var a: Int - var b: [String] - } - - let wrapper = NoopWrapper(for: Test1.self, storage: Test1.$Storage(a: 42, b: [""])) - _ = Test1(storageWrapper: wrapper) // Ok - - @NoopWrapper - struct Test2 { - } - - _ = Test2(storageWrapper: NoopWrapper(for: Test2.self, storage: Test2.$Storage())) // Ok - _ = Test2(storageWrapper: wrapper) - // expected-error@-1 {{cannot convert value of type 'NoopWrapper' to expected argument type 'NoopWrapper'}} - - @NoopWrapper - struct Test3 { // expected-note {{'init(a:b:)' declared here}} - var a: Int - @typeWrapperIgnored var b: String - } - - // @typeWrapperIgnored suppresses `storageWrapper:` initializer - _ = Test3(storageWrapper: NoopWrapper(for: Test3.self, storage: Test3.$Storage(a: 42))) - // expected-error@-1 {{missing arguments for parameters 'a', 'b' in call}} - // expected-error@-2 {{extra argument 'storageWrapper' in call}} -} - -func test_multiple_wrapper_attributes() { - @Parent.Wrapper @NoopWrapper - // expected-note@-1 {{'Wrapper' declared here}} - // expected-note@-2 {{'NoopWrapper' declared here}} - struct Test1 {} // expected-error {{struct 'Test1' cannot use more than one type wrapper}} - - @Parent.Wrapper @NoopWrapper - // expected-note@-1 {{'Wrapper' declared here}} - // expected-note@-2 {{'NoopWrapper' declared here}} - class Test2 {} // expected-error {{class 'Test2' cannot use more than one type wrapper}} -} - -@NoopWrapper -protocol WrappedProto { -} - -do { - // @NoopWrapper is inferred from `WrappedProto` - struct InferenceFromProto : WrappedProto { // Ok - var x: Int - - func test() { - _ = $storage // Ok (to make sure that NoopWrapper is indeed inferred) - } - } - - @Parent.Wrapper // expected-note {{'Wrapper' declared here}} - struct ClashBetweenDirectAndInferred : WrappedProto { - // expected-error@-1 {{struct 'ClashBetweenDirectAndInferred' cannot use more than one type wrapper}} - // expected-note@-2 {{type wrapper 'NoopWrapper' inferred from protocol 'WrappedProto'}} - // expected-error@-3 {{type 'ClashBetweenDirectAndInferred' does not conform to protocol 'WrappedProto'}} - } - - @NoopWrapper - final class NoClashDueToSameWrapper : WrappedProto { // Ok - var v: [Int?] - - func test() { - let storage: NoopWrapper = $storage - _ = storage[propertyKeyPath: \.v, storageKeyPath: \$Storage.v] - } - } - - -} From 8305c7d5de9d11c2ab0cffa8dbdc26cc9e740d8a Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Wed, 8 Feb 2023 09:53:52 -0800 Subject: [PATCH 38/98] [interop][SwiftToCxx] add C++ bindings for static methods --- lib/PrintAsClang/DeclAndTypePrinter.cpp | 9 ++++-- lib/PrintAsClang/PrintClangFunction.cpp | 31 ++++++++++++------- lib/PrintAsClang/PrintClangFunction.h | 5 +-- .../methods/method-in-cxx-execution.cpp | 9 ++++++ .../SwiftToCxx/methods/method-in-cxx.swift | 19 ++++++++++++ 5 files changed, 56 insertions(+), 17 deletions(-) diff --git a/lib/PrintAsClang/DeclAndTypePrinter.cpp b/lib/PrintAsClang/DeclAndTypePrinter.cpp index 1b7d70bd1a0da..7cfe848387f89 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.cpp +++ b/lib/PrintAsClang/DeclAndTypePrinter.cpp @@ -977,6 +977,10 @@ class DeclAndTypePrinter::Implementation getForeignResultType(AFD, methodTy, asyncConvention, errorConvention); if (outputLang == OutputLanguageMode::Cxx) { + // FIXME: Support operators. + if (AFD->isOperator() || (AFD->isStatic() && AFD->isImplicit())) + return; + auto *typeDeclContext = dyn_cast(AFD->getParent()); if (!typeDeclContext) { typeDeclContext = @@ -1013,6 +1017,7 @@ class DeclAndTypePrinter::Implementation declPrinter.printCxxMethod(typeDeclContext, AFD, funcABI->getSignature(), funcABI->getSymbolName(), resultTy, + /*isStatic=*/isClassMethod, /*isDefinition=*/false); } @@ -1035,6 +1040,7 @@ class DeclAndTypePrinter::Implementation } else { defPrinter.printCxxMethod(typeDeclContext, AFD, funcABI->getSignature(), funcABI->getSymbolName(), resultTy, + /*isStatic=*/isClassMethod, /*isDefinition=*/true); } @@ -1652,9 +1658,6 @@ class DeclAndTypePrinter::Implementation void visitFuncDecl(FuncDecl *FD) { if (outputLang == OutputLanguageMode::Cxx) { - // FIXME: Support static methods. - if (FD->getDeclContext()->isTypeContext() && FD->isStatic()) - return; if (FD->getDeclContext()->isTypeContext()) return printAbstractFunctionAsMethod(FD, FD->isStatic()); diff --git a/lib/PrintAsClang/PrintClangFunction.cpp b/lib/PrintAsClang/PrintClangFunction.cpp index cd53e19f148a3..a4d9646e58c40 100644 --- a/lib/PrintAsClang/PrintClangFunction.cpp +++ b/lib/PrintAsClang/PrintClangFunction.cpp @@ -472,6 +472,14 @@ class CFunctionSignatureTypePrinter return visitPart(ds->getSelfType(), optionalKind, isInOutParam); } + ClangRepresentation visitMetatypeType(MetatypeType *mt, + Optional optionalKind, + bool isInOutParam) { + if (typeUseKind == FunctionSignatureTypeUse::TypeReference) + return visitPart(mt->getInstanceType(), optionalKind, isInOutParam); + return ClangRepresentation::unsupported; + } + ClangRepresentation visitPart(Type Ty, Optional optionalKind, bool isInOutParam) { @@ -1056,7 +1064,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody( const AbstractFunctionDecl *FD, const LoweredFunctionSignature &signature, StringRef swiftSymbolName, const NominalTypeDecl *typeDeclContext, const ModuleDecl *moduleContext, Type resultTy, const ParameterList *params, - bool hasThrows, const AnyFunctionType *funcType) { + bool hasThrows, const AnyFunctionType *funcType, bool isStaticMethod) { if (typeDeclContext) ClangSyntaxPrinter(os).printNominalTypeOutsideMemberDeclInnerStaticAssert( typeDeclContext); @@ -1085,7 +1093,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody( emitNewParam(); std::string paramName; if (param.isSelfParameter()) { - bool needsStaticSelf = isa(FD); + bool needsStaticSelf = isa(FD) || isStaticMethod; if (needsStaticSelf) { os << "swift::TypeMetadataTrait<"; CFunctionSignatureTypePrinter typePrinter( @@ -1093,7 +1101,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody( interopContext, CFunctionSignatureTypePrinterModifierDelegate(), moduleContext, declPrinter, FunctionSignatureTypeUse::TypeReference); - auto result = typePrinter.visit(param.getType(), OTK_None, + auto result = typePrinter.visit(param.getInterfaceType(), OTK_None, /*isInOutParam=*/false); assert(!result.isUnsupported()); os << ">::getTypeMetadata()"; @@ -1285,7 +1293,6 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody( }); } } - } static StringRef getConstructorName(const AbstractFunctionDecl *FD) { @@ -1301,19 +1308,19 @@ static StringRef getConstructorName(const AbstractFunctionDecl *FD) { void DeclAndTypeClangFunctionPrinter::printCxxMethod( const NominalTypeDecl *typeDeclContext, const AbstractFunctionDecl *FD, const LoweredFunctionSignature &signature, StringRef swiftSymbolName, - Type resultTy, bool isDefinition) { + Type resultTy, bool isStatic, bool isDefinition) { bool isConstructor = isa(FD); os << " "; FunctionSignatureModifiers modifiers; if (isDefinition) modifiers.qualifierContext = typeDeclContext; - modifiers.isStatic = isConstructor && !isDefinition; + modifiers.isStatic = (isStatic || isConstructor) && !isDefinition; modifiers.isInline = true; bool isMutating = isa(FD) ? cast(FD)->isMutating() : false; - modifiers.isConst = - !isa(typeDeclContext) && !isMutating && !isConstructor; + modifiers.isConst = !isa(typeDeclContext) && !isMutating && + !isConstructor && !isStatic; modifiers.hasSymbolUSR = !isDefinition; auto result = printFunctionSignature( FD, signature, @@ -1329,10 +1336,10 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod( os << " {\n"; // FIXME: should it be objTy for resultTy? - printCxxThunkBody(FD, signature, swiftSymbolName, typeDeclContext, - FD->getModuleContext(), resultTy, FD->getParameters(), - FD->hasThrows(), - FD->getInterfaceType()->castTo()); + printCxxThunkBody( + FD, signature, swiftSymbolName, typeDeclContext, FD->getModuleContext(), + resultTy, FD->getParameters(), FD->hasThrows(), + FD->getInterfaceType()->castTo(), isStatic); os << " }\n"; } diff --git a/lib/PrintAsClang/PrintClangFunction.h b/lib/PrintAsClang/PrintClangFunction.h index 37d104e41dc9c..4ff67283c412c 100644 --- a/lib/PrintAsClang/PrintClangFunction.h +++ b/lib/PrintAsClang/PrintClangFunction.h @@ -113,14 +113,15 @@ class DeclAndTypeClangFunctionPrinter { const NominalTypeDecl *typeDeclContext, const ModuleDecl *moduleContext, Type resultTy, const ParameterList *params, bool hasThrows = false, - const AnyFunctionType *funcType = nullptr); + const AnyFunctionType *funcType = nullptr, + bool isStaticMethod = false); /// Print the Swift method as C++ method declaration/definition, including /// constructors. void printCxxMethod(const NominalTypeDecl *typeDeclContext, const AbstractFunctionDecl *FD, const LoweredFunctionSignature &signature, - StringRef swiftSymbolName, Type resultTy, + StringRef swiftSymbolName, Type resultTy, bool isStatic, bool isDefinition); /// Print the C++ getter/setter method signature. diff --git a/test/Interop/SwiftToCxx/methods/method-in-cxx-execution.cpp b/test/Interop/SwiftToCxx/methods/method-in-cxx-execution.cpp index 8d28030b11076..c0b6422ac9526 100644 --- a/test/Interop/SwiftToCxx/methods/method-in-cxx-execution.cpp +++ b/test/Interop/SwiftToCxx/methods/method-in-cxx-execution.cpp @@ -63,5 +63,14 @@ int main() { // CHECK-NEXT: PassStructInClassMethod.retStruct 2; // CHECK-NEXT: -578, 2, -100, 42, 67, -10101 } + + { + LargeStruct::staticMethod(); +// CHECK-NEXT: LargeStruct.staticMethod; + auto largeStruct = ClassWithMethods::staticFinalClassMethod(9075); +// CHECK-NEXT: ClassWithMethods.staticFinalClassMethod; + largeStruct.dump(); +// CHECK-NEXT: 1, -1, -9075, -2, 9075, -456 + } return 0; } diff --git a/test/Interop/SwiftToCxx/methods/method-in-cxx.swift b/test/Interop/SwiftToCxx/methods/method-in-cxx.swift index c8b8f8f979dc0..d7938974aca25 100644 --- a/test/Interop/SwiftToCxx/methods/method-in-cxx.swift +++ b/test/Interop/SwiftToCxx/methods/method-in-cxx.swift @@ -22,6 +22,10 @@ public struct LargeStruct { public func added(_ x: LargeStruct) -> LargeStruct { return LargeStruct(x1: x1 + x.x1, x2: x2 + x.x2, x3: x3 + x.x3, x4: x4 + x.x4, x5: x5 + x.x5, x6: x6 + x.x6) } + + static public func staticMethod() { + print("LargeStruct.staticMethod;") + } } public final class ClassWithMethods { @@ -50,6 +54,11 @@ public final class ClassWithMethods { public func deepCopy(_ x: Int) -> ClassWithMethods { return ClassWithMethods(field + x) } + + static public func staticFinalClassMethod(x: Int) -> LargeStruct { + print("ClassWithMethods.staticFinalClassMethod;") + return LargeStruct(x1: 1, x2: -1, x3: -x, x4: -2, x5: x, x6: -456) + } } public final class PassStructInClassMethod { @@ -83,6 +92,7 @@ public final class PassStructInClassMethod { // CHECK-NEXT: inline ClassWithMethods sameRet() SWIFT_SYMBOL("s:7Methods09ClassWithA0C7sameRetACyF"); // CHECK-NEXT: inline void mutate() SWIFT_SYMBOL("s:7Methods09ClassWithA0C6mutateyyF"); // CHECK-NEXT: inline ClassWithMethods deepCopy(swift::Int x) SWIFT_SYMBOL("s:7Methods09ClassWithA0C8deepCopyyACSiF"); +// CHECK-NEXT: static inline LargeStruct staticFinalClassMethod(swift::Int x) SWIFT_SYMBOL("s:7Methods09ClassWithA0C011staticFinalB6Method1xAA11LargeStructVSi_tFZ"); // CHECK: class SWIFT_SYMBOL("s:7Methods11LargeStructV") LargeStruct final { // CHECK: inline LargeStruct(LargeStruct &&) @@ -90,6 +100,7 @@ public final class PassStructInClassMethod { // CHECK-NEXT: inline void dump() const SWIFT_SYMBOL("s:7Methods11LargeStructV4dumpyyF"); // CHECK-NEXT: inline LargeStruct scaled(swift::Int x, swift::Int y) const SWIFT_SYMBOL("s:7Methods11LargeStructV6scaledyACSi_SitF"); // CHECK-NEXT: inline LargeStruct added(const LargeStruct& x) const SWIFT_SYMBOL("s:7Methods11LargeStructV5addedyA2CF"); +// CHECK-NEXT: static inline void staticMethod() SWIFT_SYMBOL("s:7Methods11LargeStructV12staticMethodyyFZ"); // CHECK-NEXT: private public func createClassWithMethods(_ x: Int) -> ClassWithMethods { @@ -117,6 +128,11 @@ public func createPassStructInClassMethod() -> PassStructInClassMethod { // CHECK-NEXT: inline ClassWithMethods ClassWithMethods::deepCopy(swift::Int x) { // CHECK-NEXT: return _impl::_impl_ClassWithMethods::makeRetained(_impl::$s7Methods09ClassWithA0C8deepCopyyACSiF(x, ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this))); // CHECK-NEXT: } +// CHECK-NEXT: inline LargeStruct ClassWithMethods::staticFinalClassMethod(swift::Int x) { +// CHECK-NEXT: return _impl::_impl_LargeStruct::returnNewValue([&](char * _Nonnull result) { +// CHECK-NEXT: _impl::$s7Methods09ClassWithA0C011staticFinalB6Method1xAA11LargeStructVSi_tFZ(result, x, swift::TypeMetadataTrait::getTypeMetadata()); +// CHECK-NEXT: }); +// CHECK-NEXT: } // CHECK: inline LargeStruct LargeStruct::doubled() const { // CHECK-NEXT: return _impl::_impl_LargeStruct::returnNewValue([&](char * _Nonnull result) { @@ -136,6 +152,9 @@ public func createPassStructInClassMethod() -> PassStructInClassMethod { // CHECK-NEXT: _impl::$s7Methods11LargeStructV5addedyA2CF(result, _impl::_impl_LargeStruct::getOpaquePointer(x), _getOpaquePointer()); // CHECK-NEXT: }); // CHECK-NEXT: } +// CHECK-NEXT: inline void LargeStruct::staticMethod() { +// CHECK-NEXT: return _impl::$s7Methods11LargeStructV12staticMethodyyFZ(); +// CHECK-NEXT: } // CHECK: inline LargeStruct PassStructInClassMethod::retStruct(swift::Int x) { // CHECK-NEXT: return _impl::_impl_LargeStruct::returnNewValue([&](char * _Nonnull result) { From 85ba74b81289c32a21b55b80825cb41d3e23017c Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 7 Feb 2023 16:15:37 -0800 Subject: [PATCH 39/98] Introduce a separate feature to cover `@freestanding(expression)` --- include/swift/Basic/Features.def | 3 +++ lib/AST/ASTPrinter.cpp | 8 ++++++++ test/ModuleInterface/macros.swift | 4 ++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 11ad64b57201c..e87a436c4a148 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -92,6 +92,9 @@ SUPPRESSIBLE_LANGUAGE_FEATURE(PrimaryAssociatedTypes2, 346, "Primary associated SUPPRESSIBLE_LANGUAGE_FEATURE(UnavailableFromAsync, 0, "@_unavailableFromAsync", true) SUPPRESSIBLE_LANGUAGE_FEATURE(NoAsyncAvailability, 340, "@available(*, noasync)", true) LANGUAGE_FEATURE(BuiltinIntLiteralAccessors, 368, "Builtin.IntLiteral accessors", true) +LANGUAGE_FEATURE( + FreestandingExpressionMacros, 0, "@freestanding(expression) macros", + langOpts.hasFeature(Feature::Macros)) UPCOMING_FEATURE(ConciseMagicFile, 274, 6) UPCOMING_FEATURE(ForwardTrailingClosures, 286, 6) diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 2cf35e9db0cca..2a12c10159814 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -3171,6 +3171,14 @@ static bool usesFeatureBuiltinMacros(Decl *decl) { static bool usesFeatureImportSymbolicCXXDecls(Decl *decl) { return false; } +static bool usesFeatureFreestandingExpressionMacros(Decl *decl) { + auto macro = dyn_cast(decl); + if (!macro) + return false; + + return macro->getMacroRoles().contains(MacroRole::Expression); +} + static void suppressingFeatureNoAsyncAvailability(PrintOptions &options, llvm::function_ref action) { diff --git a/test/ModuleInterface/macros.swift b/test/ModuleInterface/macros.swift index 213d05b2d7fab..3f7bc525281fc 100644 --- a/test/ModuleInterface/macros.swift +++ b/test/ModuleInterface/macros.swift @@ -6,12 +6,12 @@ // RUN: %FileCheck %s < %t/Macros.swiftinterface --check-prefix CHECK // RUN: %target-swift-frontend -compile-module-from-interface %t/Macros.swiftinterface -o %t/Macros.swiftmodule -// CHECK: #if compiler(>=5.3) && $Macros +// CHECK: #if compiler(>=5.3) && $FreestandingExpressionMacros && $Macros // CHECK-NEXT: @freestanding(expression) public macro publicStringify(_ value: T) -> (T, Swift.String) = #externalMacro(module: "SomeModule", type: "StringifyMacro") // CHECK-NEXT: #endif @freestanding(expression) public macro publicStringify(_ value: T) -> (T, String) = #externalMacro(module: "SomeModule", type: "StringifyMacro") -// CHECK: #if compiler(>=5.3) && $Macros +// CHECK: #if compiler(>=5.3) && $FreestandingExpressionMacros && $Macros // CHECK: @freestanding(expression) public macro publicLine: T = #externalMacro(module: "SomeModule", type: "Line") where T : Swift.ExpressibleByIntegerLiteral // CHECK-NEXT: #endif @freestanding(expression) public macro publicLine: T = #externalMacro(module: "SomeModule", type: "Line") From 0b0cc99c989a2865c91939b70a36f5491d526c3c Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Wed, 8 Feb 2023 12:33:07 -0800 Subject: [PATCH 40/98] [interop][SwiftToCxx] avoid -Wshadow warning for C++ representation of enum member parameters --- lib/PrintAsClang/PrintClangFunction.cpp | 21 +++++++++++++ lib/PrintAsClang/_SwiftStdlibCxxOverlay.h | 3 +- .../enum-member-param-no-shadow-case.swift | 31 +++++++++++++++++++ .../functions/swift-functions-errors.swift | 2 +- .../stdlib/stdlib-dep-inline-in-cxx.swift | 2 +- .../stdlib/swift-stdlib-in-cxx.swift | 4 +-- 6 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 test/Interop/SwiftToCxx/enums/enum-member-param-no-shadow-case.swift diff --git a/lib/PrintAsClang/PrintClangFunction.cpp b/lib/PrintAsClang/PrintClangFunction.cpp index a4d9646e58c40..1c25059f8438d 100644 --- a/lib/PrintAsClang/PrintClangFunction.cpp +++ b/lib/PrintAsClang/PrintClangFunction.cpp @@ -639,6 +639,25 @@ static void printDirectReturnOrParamCType( }); } +/// Make adjustments to the Swift parameter name in generated C++, to +/// avoid things like additional warnings. +static void renameCxxParameterIfNeeded(const AbstractFunctionDecl *FD, + std::string ¶mName) { + if (paramName.empty()) + return; + const auto *enumDecl = FD->getDeclContext()->getSelfEnumDecl(); + if (!enumDecl) + return; + // Rename a parameter in an enum method that shadows an existing case name, + // to avoid a -Wshadow warning in Clang. + for (const auto *Case : enumDecl->getAllElements()) { + if (Case->getNameStr() == paramName) { + paramName = (llvm::Twine(paramName) + "_").str(); + return; + } + } +} + ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature( const AbstractFunctionDecl *FD, const LoweredFunctionSignature &signature, StringRef name, Type resultTy, FunctionSignatureKind kind, @@ -895,6 +914,7 @@ ClangRepresentation DeclAndTypeClangFunctionPrinter::printFunctionSignature( param, param->getInterfaceType()); std::string paramName = param->getName().empty() ? "" : param->getName().str().str(); + renameCxxParameterIfNeeded(FD, paramName); // Always emit a named parameter for the C++ inline thunk to ensure it // can be referenced in the body. if (kind == FunctionSignatureKind::CxxInlineThunk && paramName.empty()) { @@ -1113,6 +1133,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody( paramOS << "_" << paramIndex; } else { paramName = param.getName().str().str(); + renameCxxParameterIfNeeded(FD, paramName); } ++paramIndex; printCxxToCFunctionParameterUse(param.getInterfaceType(), paramName, diff --git a/lib/PrintAsClang/_SwiftStdlibCxxOverlay.h b/lib/PrintAsClang/_SwiftStdlibCxxOverlay.h index 532e7b09adc5a..1566d723f727d 100644 --- a/lib/PrintAsClang/_SwiftStdlibCxxOverlay.h +++ b/lib/PrintAsClang/_SwiftStdlibCxxOverlay.h @@ -81,8 +81,7 @@ template class CollectionIterator { using Index = decltype(reinterpret_cast(0x123)->getStartIndex()); - SWIFT_INLINE_THUNK CollectionIterator(const Collection &collection) - : collection(collection) { + SWIFT_INLINE_THUNK CollectionIterator(const Collection &c) : collection(c) { index = collection.getStartIndex(); endIndex = collection.getEndIndex(); // FIXME: Begin read access. diff --git a/test/Interop/SwiftToCxx/enums/enum-member-param-no-shadow-case.swift b/test/Interop/SwiftToCxx/enums/enum-member-param-no-shadow-case.swift new file mode 100644 index 0000000000000..2cfc0c0047abe --- /dev/null +++ b/test/Interop/SwiftToCxx/enums/enum-member-param-no-shadow-case.swift @@ -0,0 +1,31 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -typecheck -module-name Enums -clang-header-expose-decls=all-public -emit-clang-header-path %t/enums.h +// RUN: %FileCheck %s < %t/enums.h + +// RUN: %check-interop-cxx-header-in-clang(%t/enums.h -Wno-unused-private-field -Wno-unused-function) + +public enum E { + case a + case b(Int) + + public init(_ b: Int) { + self = .b(b) + } + + public func takeParamA(_ a: Int) {} + + public static func takeParamB(_ b: Int) {} +} + +// CHECK: static inline E init(swift::Int b_) +// CHECK: inline void takeParamA(swift::Int a_) +// CHECK: static inline void takeParamB(swift::Int b_) + +// CHECK: E E::init(swift::Int b_) { +// CHECK: _impl::$s5Enums1EOyACSicfC(b_) + +// CHECK: void E::takeParamA(swift::Int a_) const { +// CHECK: _impl::$s5Enums1EO10takeParamAyySiF(a_, + +// CHECK: void E::takeParamB(swift::Int b_) { +// CHECK: return _impl::$s5Enums1EO10takeParamByySiFZ(b_); diff --git a/test/Interop/SwiftToCxx/functions/swift-functions-errors.swift b/test/Interop/SwiftToCxx/functions/swift-functions-errors.swift index 0a2b4af8f8806..d4b09591ae7e7 100644 --- a/test/Interop/SwiftToCxx/functions/swift-functions-errors.swift +++ b/test/Interop/SwiftToCxx/functions/swift-functions-errors.swift @@ -2,7 +2,7 @@ // RUN: %target-swift-frontend %s -typecheck -module-name Functions -enable-experimental-cxx-interop -emit-clang-header-path %t/functions.h // RUN: %FileCheck %s < %t/functions.h -// RUN: %check-interop-cxx-header-in-clang(%t/functions.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY -Wno-shadow -Wno-unused-function) +// RUN: %check-interop-cxx-header-in-clang(%t/functions.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY -Wno-unused-function) // CHECK-LABEL: namespace Functions __attribute__((swift_private)) SWIFT_SYMBOL_MODULE("Functions") { diff --git a/test/Interop/SwiftToCxx/stdlib/stdlib-dep-inline-in-cxx.swift b/test/Interop/SwiftToCxx/stdlib/stdlib-dep-inline-in-cxx.swift index eaf9e17bfff37..86fb99356808d 100644 --- a/test/Interop/SwiftToCxx/stdlib/stdlib-dep-inline-in-cxx.swift +++ b/test/Interop/SwiftToCxx/stdlib/stdlib-dep-inline-in-cxx.swift @@ -7,7 +7,7 @@ // RUN: cat %t/stdlib.h %t/stdlib2.h > %t/two_includes.h -// RUN: %check-interop-cxx-header-in-clang(-DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY %t/two_includes.h -Wno-unused-private-field -Wno-unused-function -Wno-shadow) +// RUN: %check-interop-cxx-header-in-clang(-DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY %t/two_includes.h -Wno-unused-private-field -Wno-unused-function) @_expose(Cxx) public func test() -> String { diff --git a/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift b/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift index 81e6a23582f44..522ff98147bea 100644 --- a/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift +++ b/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift @@ -2,9 +2,7 @@ // RUN: %target-swift-frontend -parse-as-library %platform-module-dir/Swift.swiftmodule/%module-target-triple.swiftinterface -enable-library-evolution -disable-objc-attr-requires-foundation-module -typecheck -module-name Swift -parse-stdlib -enable-experimental-cxx-interop -emit-clang-header-path %t/Swift.h -experimental-skip-all-function-bodies // RUN: %FileCheck %s < %t/Swift.h -// RUN: %check-interop-cxx-header-in-clang(%t/Swift.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY -Wno-unused-private-field -Wno-unused-function -Wno-shadow) - -// FIXME: remove need for -Wno-shadow +// RUN: %check-interop-cxx-header-in-clang(%t/Swift.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY -Wno-unused-private-field -Wno-unused-function) // CHECK: namespace Swift __attribute__((swift_private)) SWIFT_SYMBOL_MODULE("Swift") { From feceaa2ff7314088c75445d2a10dee874198b058 Mon Sep 17 00:00:00 2001 From: Meghana Gupta Date: Wed, 8 Feb 2023 12:40:33 -0800 Subject: [PATCH 41/98] Fix bug during IR generation of a signed access --- lib/IRGen/IRGenSIL.cpp | 2 +- test/IRGen/ptrauth_field_fptr_import.swift | 3 +-- test/SILGen/ptrauth_field_fptr_import.swift | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 309cbddebea07..91bcb39bebade 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -5930,7 +5930,7 @@ void IRGenSILFunction::visitEndAccessInst(EndAccessInst *i) { } case SILAccessEnforcement::Signed: { - if (access->getAccessKind() != SILAccessKind::Modify || + if (access->getAccessKind() != SILAccessKind::Modify && access->getAccessKind() != SILAccessKind::Init) { // nothing to do. return; diff --git a/test/IRGen/ptrauth_field_fptr_import.swift b/test/IRGen/ptrauth_field_fptr_import.swift index 970025f52d160..7a05283dec819 100644 --- a/test/IRGen/ptrauth_field_fptr_import.swift +++ b/test/IRGen/ptrauth_field_fptr_import.swift @@ -1,5 +1,4 @@ -// RUN: %swift-frontend %s -enable-import-ptrauth-field-function-pointers -emit-ir -target arm64e-apple-ios13.0 -I %S/Inputs/ -validate-tbd-against-ir=none 2>&1 | %FileCheck %s - +// RUN: %swift-frontend %s -enable-import-ptrauth-field-function-pointers -emit-ir -target arm64e-apple-ios13.0 -I %S/Inputs/ -validate-tbd-against-ir=none | %FileCheck %s // REQUIRES: CPU=arm64e // REQUIRES: OS=ios diff --git a/test/SILGen/ptrauth_field_fptr_import.swift b/test/SILGen/ptrauth_field_fptr_import.swift index b8be6014daf35..d5e11caa90cd1 100644 --- a/test/SILGen/ptrauth_field_fptr_import.swift +++ b/test/SILGen/ptrauth_field_fptr_import.swift @@ -1,4 +1,4 @@ -// RUN: %swift-frontend %s -enable-import-ptrauth-field-function-pointers -emit-silgen -target arm64e-apple-ios13.0 -I %S/Inputs/ 2>&1 | %FileCheck %s +// RUN: %swift-frontend %s -enable-import-ptrauth-field-function-pointers -emit-silgen -target arm64e-apple-ios13.0 -I %S/Inputs/ | %FileCheck %s // REQUIRES: CPU=arm64e // REQUIRES: OS=ios From b70ba0963d7c6c5901cf2f081722beeda9b3920e Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Wed, 8 Feb 2023 12:43:30 -0800 Subject: [PATCH 42/98] [interop][SwiftToCxx] remove extraneous ';' from generated header --- lib/PrintAsClang/PrintClangValueType.cpp | 2 +- test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/PrintAsClang/PrintClangValueType.cpp b/lib/PrintAsClang/PrintClangValueType.cpp index 8c76f3b8504f1..0dd45f6dbb6d4 100644 --- a/lib/PrintAsClang/PrintClangValueType.cpp +++ b/lib/PrintAsClang/PrintClangValueType.cpp @@ -130,7 +130,7 @@ static void addCppExtensionsToStdlibType(const NominalTypeDecl *typeDecl, "memcpy(&result._3, value + 8, 4);\n" "#endif\n" "return result;\n" - "};\n"; + "}\n"; cPrologueOS << "SWIFT_EXTERN void *_Nonnull " "$sSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF(swift_interop_stub_" "Swift_String) SWIFT_NOEXCEPT SWIFT_CALL;\n"; diff --git a/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift b/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift index 522ff98147bea..5fbaebe053ed2 100644 --- a/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift +++ b/test/Interop/SwiftToCxx/stdlib/swift-stdlib-in-cxx.swift @@ -2,7 +2,7 @@ // RUN: %target-swift-frontend -parse-as-library %platform-module-dir/Swift.swiftmodule/%module-target-triple.swiftinterface -enable-library-evolution -disable-objc-attr-requires-foundation-module -typecheck -module-name Swift -parse-stdlib -enable-experimental-cxx-interop -emit-clang-header-path %t/Swift.h -experimental-skip-all-function-bodies // RUN: %FileCheck %s < %t/Swift.h -// RUN: %check-interop-cxx-header-in-clang(%t/Swift.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY -Wno-unused-private-field -Wno-unused-function) +// RUN: %check-interop-cxx-header-in-clang(%t/Swift.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY -Wno-unused-private-field -Wno-unused-function -Wc++98-compat-extra-semi) // CHECK: namespace Swift __attribute__((swift_private)) SWIFT_SYMBOL_MODULE("Swift") { From f4877645b8036578f757f2c0ed0107d5ed41fb26 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Feb 2023 12:53:23 -0800 Subject: [PATCH 43/98] [move-only][borrow2destructure] Add support for processing switch_enum. As mentioned previously, I am right now creating new Implementations for each switch_enum argument. Once I get tests working I am going to refactor and reuse the same implementation so I can reuse memory. NOTE: This currently does not impact SILGen since I did not change SILGenPattern to emit noncopyable switched upon values as guaranteed. Keep in mind that I am not going to do this for no implicit copy though since no implicit copy values cannot contain move only values so we do not need to work with destructures. --- .../Mandatory/MoveOnlyAddressChecker.cpp | 38 ++ .../MoveOnlyBorrowToDestructureTransform.cpp | 296 ++++++++++++- .../Mandatory/MoveOnlyObjectChecker.cpp | 14 - ...veonly_borrow_to_destructure_transform.sil | 401 ++++++++++++++++++ 4 files changed, 715 insertions(+), 34 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp index b719beff32a34..83f1771549973 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp @@ -1986,6 +1986,44 @@ class MoveOnlyCheckerPass : public SILFunctionTransform { if (MoveOnlyChecker(getFunction(), deAnalysis, domTree).checkFunction()) { invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); } + + if (!getOptions().VerifyAll) + return; + + // TODO: Enable this by default when verify all is disabled and make it a + // diagnostic error saying file a bug. + for (auto &block : *getFunction()) { + for (auto &inst : block) { + if (auto *cvi = dyn_cast(&inst)) { + if (cvi->getOperand()->getType().isMoveOnly()) { + llvm::errs() << "Should have eliminated copy at this point: " + << *cvi; + llvm::report_fatal_error("standard compiler error"); + } + continue; + } + + if (auto *li = dyn_cast(&inst)) { + if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy && + li->getType().isMoveOnly()) { + llvm::errs() << "Should have eliminated copy at this point: " + << *li; + llvm::report_fatal_error("standard compiler error"); + } + continue; + } + + if (auto *copyAddr = dyn_cast(&inst)) { + if (!copyAddr->isTakeOfSrc() && + copyAddr->getSrc()->getType().isMoveOnly()) { + llvm::errs() << "Should have eliminated copy at this point: " + << *copyAddr; + llvm::report_fatal_error("standard compiler error"); + } + continue; + } + } + } } }; diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp index e070739c402fd..2f29877b7df5d 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp @@ -213,10 +213,10 @@ struct borrowtodestructure::Implementation { initialValue = SILValue(); } - void init(SILValue rootAddress) { + void init(SILValue rootValue) { clear(); - liveness.init(rootAddress); - liveness.initializeDef(rootAddress, TypeTreeLeafTypeRange(rootAddress)); + liveness.init(rootValue); + liveness.initializeDef(rootValue, TypeTreeLeafTypeRange(rootValue)); } bool gatherUses(SILValue value); @@ -233,7 +233,7 @@ struct borrowtodestructure::Implementation { /// Rewrite all of the uses of our borrow on our borrow operand, performing /// destructures as appropriate. - void rewriteUses(); + void rewriteUses(InstructionDeleter *deleter = nullptr); void cleanup(); @@ -241,7 +241,7 @@ struct borrowtodestructure::Implementation { /// Returns mark_must_check if we are processing borrows or the enum argument /// if we are processing switch_enum. - SILValue getRootValue() const { return interface.mmci; } + SILValue getRootValue() const { return liveness.getRootValue(); } DiagnosticEmitter &getDiagnostics() const { return interface.diagnosticEmitter; @@ -261,7 +261,7 @@ struct borrowtodestructure::Implementation { }; bool Implementation::gatherUses(SILValue value) { - LLVM_DEBUG(llvm::dbgs() << "Gathering uses!\n"); + LLVM_DEBUG(llvm::dbgs() << "Gathering uses for: " << *value); StackList useWorklist(value->getFunction()); for (auto *use : value->getUses()) { @@ -980,15 +980,8 @@ AvailableValues &Implementation::computeAvailableValues(SILBasicBlock *block) { // destructure. for (unsigned i : range(predAvailableValues.size())) { if (auto value = predAvailableValues[i]) { -#if false - auto iterOffsetSize = TypeOffsetSizePair(value, mmci); - typeSpanToValue.insert(iterOffsetSize.startOffset, - iterOffsetSize.getEndOffset(), - value); -#else // We check later that we store entire values. typeSpanToValue.insert(i, i + 1, value); -#endif } } @@ -1168,7 +1161,7 @@ dumpIntervalMap(IntervalMapAllocator::Map &map) { } #endif -void Implementation::rewriteUses() { +void Implementation::rewriteUses(InstructionDeleter *deleter) { blocksToUses.setFrozen(); LLVM_DEBUG(llvm::dbgs() @@ -1282,7 +1275,10 @@ void Implementation::rewriteUses() { first = builder.createOwnedMoveOnlyWrapperToCopyableValue( getSafeLoc(inst), first); } + SILInstruction *oldInst = operand.get()->getDefiningInstruction(); operand.set(first); + if (deleter) + deleter->forceTrackAsDead(oldInst); continue; } @@ -1313,7 +1309,10 @@ void Implementation::rewriteUses() { // NOTE: This needs to be /after/the interior pointer operand usage // above so that we can use the end scope of our interior pointer base // value. + SILInstruction *oldInst = operand.get()->getDefiningInstruction(); operand.set(innerValue); + if (deleter) + deleter->forceTrackAsDead(oldInst); continue; } @@ -1371,7 +1370,10 @@ void Implementation::rewriteUses() { borrowBuilder.createGuaranteedMoveOnlyWrapperToCopyableValue( loc, value); } + auto *oldInst = operand.get()->getDefiningInstruction(); operand.set(value); + if (deleter) + deleter->forceTrackAsDead(oldInst); // If we have a terminator that is a trivial use (e.x.: we // struct_extract a trivial value). Just put the end_borrow before the @@ -1441,7 +1443,10 @@ void Implementation::rewriteUses() { iterValue = consumeBuilder.createOwnedMoveOnlyWrapperToCopyableValue( loc, iterValue); } + auto *oldInst = operand.get()->getDefiningInstruction(); operand.set(iterValue); + if (deleter) + deleter->forceTrackAsDead(oldInst); // Then go through our available values and use the interval map to // update them with the destructured values if we have one for it. @@ -1593,11 +1598,132 @@ static bool gatherBorrows(MarkMustCheckInst *mmci, return true; } +//===----------------------------------------------------------------------===// +// MARK: Switch Enum Search +//===----------------------------------------------------------------------===// + +static bool +gatherSwitchEnum(SILValue value, + SmallVectorImpl &switchEnumWorklist) { + LLVM_DEBUG(llvm::dbgs() << "Gathering switch enums for value: " << *value); + + auto *fn = value->getFunction(); + StackList useWorklist(fn); + for (auto *use : value->getUses()) { + useWorklist.push_back(use); + } + + // Grab the start of our switch enum worklist, so that after we visit the + // switch_enums that are users of this value, we can recursively visit those + // values. + unsigned start = switchEnumWorklist.size(); + + while (!useWorklist.empty()) { + auto *nextUse = useWorklist.pop_back_val(); + LLVM_DEBUG(llvm::dbgs() << " NextUse: " << *nextUse->getUser()); + switch (nextUse->getOperandOwnership()) { + case OperandOwnership::NonUse: + continue; + + // Conservatively treat a conversion to an unowned value as a pointer + // escape. If we see this in the SIL, fail and return false so we emit a + // "compiler doesn't understand error". + case OperandOwnership::ForwardingUnowned: + case OperandOwnership::PointerEscape: + LLVM_DEBUG(llvm::dbgs() + << " Found forwarding unowned or pointer escape!\n"); + return false; + + // These might be uses that we need to perform a destructure or insert + // struct_extracts for. + case OperandOwnership::TrivialUse: + case OperandOwnership::InstantaneousUse: + case OperandOwnership::UnownedInstantaneousUse: + case OperandOwnership::InteriorPointer: + case OperandOwnership::BitwiseEscape: { + // Look through copy_value of a move only value. We treat copy_value of + // copyable values as normal uses. + if (auto *cvi = dyn_cast(nextUse->getUser())) { + if (cvi->getOperand()->getType().isMoveOnly()) { + LLVM_DEBUG(llvm::dbgs() << " Found copy value of move only " + "field... looking through!\n"); + for (auto *use : cvi->getUses()) + useWorklist.push_back(use); + continue; + } + + // If we don't have a copy of a move only type, we just treat this as a + // normal use, so we just continue. + } + continue; + } + + case OperandOwnership::ForwardingConsume: + case OperandOwnership::DestroyingConsume: + // We don't care about forwarding consumes or destroying consumes. + continue; + + case OperandOwnership::GuaranteedForwarding: + // Look through guaranteed forwarding unless we have a switch enum. If we + // have a switch enum, we add it to the list. + if (auto *switchEnum = dyn_cast(nextUse->getUser())) { + LLVM_DEBUG(llvm::dbgs() + << " Found switch enum: " << *nextUse->getUser()); + switchEnumWorklist.push_back(switchEnum); + continue; + } + + ForwardingOperand(nextUse).visitForwardedValues([&](SILValue value) { + for (auto *use : value->getUses()) { + useWorklist.push_back(use); + } + return true; + }); + continue; + + case OperandOwnership::Borrow: + LLVM_DEBUG(llvm::dbgs() << " Found recursive borrow!\n"); + // Look through borrows. + for (auto value : nextUse->getUser()->getResults()) { + for (auto *use : value->getUses()) { + useWorklist.push_back(use); + } + } + continue; + case OperandOwnership::EndBorrow: + LLVM_DEBUG(llvm::dbgs() << " Found end borrow!\n"); + continue; + case OperandOwnership::Reborrow: + llvm_unreachable("Unsupported for now?!"); + } + } + + unsigned end = switchEnumWorklist.size(); + if (start == end) + return true; + + for (unsigned i : range(start, end)) { + auto *s = switchEnumWorklist[i]; + for (auto argList : s->getSuccessorBlockArgumentLists()) { + for (SILValue value : argList) { + if (value->getType().isTrivial(*fn)) + continue; + + if (!gatherSwitchEnum(value, switchEnumWorklist)) + return false; + } + } + } + + return true; +} + //===----------------------------------------------------------------------===// // MARK: Top Level Entrypoint //===----------------------------------------------------------------------===// bool BorrowToDestructureTransform::transform() { + auto *fn = mmci->getFunction(); StackList borrowWorklist(mmci->getFunction()); // If we failed to gather borrows due to the transform not understanding part @@ -1610,8 +1736,136 @@ bool BorrowToDestructureTransform::transform() { if (borrowWorklist.empty()) return true; - // Attempt to gather uses. Return false if we saw something that we did not - // understand. + // Then go through our borrows and attempt to gather up guaranteed + // switch_enums. If we see any of them, we need to transform them into owned + // switch_enums. + SmallVector switchEnumWorklist; + for (auto *borrow : borrowWorklist) { + // Attempt to gather the switch enums and if we fail, return false. + if (!gatherSwitchEnum(borrow, switchEnumWorklist)) + return false; + } + + // Now perform the checking of our switch_enum, working in stack order. + { + SmallVector switchEnumArgCopyValueToDelete; + InstructionDeleter deleter; + while (!switchEnumWorklist.empty()) { + auto *s = switchEnumWorklist.pop_back_val(); + for (auto argList : s->getSuccessorBlockArgumentLists()) { + for (SILValue arg : argList) { + // Skip trivial or copyable values. If we have a copyable value, we + // will handle it as part of the cleanup phase at the end when we + // convert the actual switch_enum to be an owned switch_enum. + if (arg->getType().isTrivial(*fn) || !arg->getType().isMoveOnly()) + continue; + + SmallVector discoveredBlocks; + Implementation impl(*this, discoveredBlocks); + impl.init(arg); + if (!impl.gatherUses(arg)) { + diagnosticEmitter.emitCheckerDoesntUnderstandDiagnostic(mmci); + continue; + } + + // Next make sure that any destructure needing instructions are on the + // boundary in a per bit field sensitive manner. + unsigned diagnosticCount = diagnosticEmitter.getDiagnosticCount(); + impl.checkDestructureUsesOnBoundary(); + + // If we emitted any diagnostic, break out. We return true since we + // actually succeeded in our processing by finding the error. We only + // return false if we want to tell the rest of the checker that there + // was an internal compiler error that we need to emit a "compiler + // doesn't understand error". + if (diagnosticCount != diagnosticEmitter.getDiagnosticCount()) + return true; + + // Then check if we had two consuming uses on the same instruction or + // a consuming/non-consuming use on the same isntruction. + impl.checkForErrorsOnSameInstruction(); + + // If we emitted any diagnostic, break out. We return true since we + // actually succeeded in our processing by finding the error. We only + // return false if we want to tell the rest of the checker that there + // was an internal compiler error that we need to emit a "compiler + // doesn't understand error". + if (diagnosticCount != diagnosticEmitter.getDiagnosticCount()) + return true; + + // At this point, we know that all of our destructure requiring uses + // are on the boundary of our live range. Now we need to do the + // rewriting. + impl.blockToAvailableValues.emplace(impl.liveness); + impl.rewriteUses(&deleter); + + // Now that we have done our rewritting, we need to do a few cleanups + // starting by inserting compensating destroys for all of our inserted + // phis/destructures/initial value copy. + impl.cleanup(); + + // Now grab our initialValue which will be a copy_value from our + // argument and RAUW it. We are going to convert the argument + // later. We left it in to ensure that as we recreated instructions, + // OSSA invariants were satisfied locally (albeit the actual IR was + // not in a consistent state). + auto *cvi = cast(impl.initialValue); + switchEnumArgCopyValueToDelete.push_back(cvi); + } + } + + // Now that we have processed all of the arguments for this switch_enum, + // cleanup any dead instructions. + deleter.cleanupDeadInstructions(); + + // Now that we have processed the switch_enum, we need to convert the + // switch_enum to be owned. We do this by introducing a copy on the + // switch_enum argument and then insert a destroy_value after the single + // copy_value in each destination block that we originally inserted. + { + SmallVector discoveredBlocks; + SSAPrunedLiveness liveness(&discoveredBlocks); + PrunedLivenessBoundary boundary; + + SILBuilderWithScope builder(s); + SILValue newOperand = + builder.createCopyValue(getSafeLoc(s), s->getOperand()); + s->setOperand(0, newOperand); + s->setForwardingOwnershipKind(OwnershipKind::Owned); + for (auto argList : s->getSuccessorBlockArgumentLists()) { + for (SILArgument *arg : argList) { + if (arg->getType().isTrivial(*fn)) + continue; + arg->setOwnershipKind(OwnershipKind::Owned); + + if (arg->getType().isMoveOnly()) + continue; + + // If we have a copyable type, we need to insert compensating + // destroys. + SWIFT_DEFER { + liveness.clear(); + discoveredBlocks.clear(); + boundary.clear(); + }; + addCompensatingDestroys(liveness, boundary, arg); + } + } + } + + // Now eliminate our unneeded copyvalues from earlier than we inserted to + // satisfy OSSA invariants. + while (!switchEnumArgCopyValueToDelete.empty()) { + auto *cvi = switchEnumArgCopyValueToDelete.pop_back_val(); + cvi->replaceAllUsesWith(cvi->getOperand()); + cvi->eraseFromParent(); + } + } + } + + // Now that we have handled our switch_enum we need to handle our + // borrows... begin by gathering uses. Return false if we saw something that + // we did not understand. SmallVector discoveredBlocks; Implementation impl(*this, discoveredBlocks); impl.init(mmci); @@ -1657,10 +1911,12 @@ bool BorrowToDestructureTransform::transform() { // Then clean up all of our borrows/copies/struct_extracts which no longer // have any uses... - InstructionDeleter deleter; - while (!borrowWorklist.empty()) { - deleter.recursivelyForceDeleteUsersAndFixLifetimes( - borrowWorklist.pop_back_val()); + { + InstructionDeleter deleter; + while (!borrowWorklist.empty()) { + deleter.recursivelyForceDeleteUsersAndFixLifetimes( + borrowWorklist.pop_back_val()); + } } return true; diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp index 338d110743c7f..90c14772616d1 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp @@ -806,20 +806,6 @@ class MoveOnlyCheckerPass : public SILFunctionTransform { if (checker.changed) { invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); } - - if (getOptions().VerifyAll) { - for (auto &block : *getFunction()) { - for (auto &inst : block) { - if (auto *cvi = dyn_cast(&inst)) { - if (cvi->getOperand()->getType().isMoveOnly()) { - llvm::errs() << "Should have eliminated copy at this point: " - << *cvi; - llvm::report_fatal_error("standard compiler error"); - } - } - } - } - } } }; diff --git a/test/SILOptimizer/moveonly_borrow_to_destructure_transform.sil b/test/SILOptimizer/moveonly_borrow_to_destructure_transform.sil index 2216b7568d987..e29592b6f8381 100644 --- a/test/SILOptimizer/moveonly_borrow_to_destructure_transform.sil +++ b/test/SILOptimizer/moveonly_borrow_to_destructure_transform.sil @@ -43,11 +43,26 @@ struct SingleIntContainingStruct { var value: Builtin.Int32 } +@_moveOnly +enum E { +case first(MoveOnlyKlass) +case second(SingleIntContainingStruct) +case third(AggStruct2) +case fourth(Klass) +} + +@_moveOnly +enum E2 { +case lhs(AggStruct2) +case rhs(E) +} + sil @misc_use : $@convention(thin) () -> () sil @aggstruct_consume : $@convention(thin) (@owned AggStruct) -> () sil @moveonlyklass_consume : $@convention(thin) (@owned MoveOnlyKlass) -> () sil @moveonlyklass_use : $@convention(thin) (@guaranteed MoveOnlyKlass) -> () sil @klass_use : $@convention(thin) (@guaranteed Klass) -> () +sil @builtin32_use : $@convention(thin) (Builtin.Int32) -> () /////////// // Tests // @@ -626,3 +641,389 @@ bb3: %35 = tuple () return %35 : $() } + +/////////////////////// +// Switch Enum Tests // +/////////////////////// + +// CHECK-LABEL: sil [ossa] @switch_enum_without_destructure : $@convention(thin) (@guaranteed E) -> () { +// CHECK: bb0([[ARG:%.*]] : +// CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] +// CHECK: [[MARKED_VALUE:%.*]] = mark_must_check [no_copy] [[ARG_COPY]] +// CHECK: [[MARKED_COPY:%.*]] = copy_value [[MARKED_VALUE]] +// CHECK: switch_enum [[MARKED_COPY:%.*]] : $E, case #E.first!enumelt: [[BB_E_FIRST:bb[0-9]+]], case #E.second!enumelt: [[BB_E_SECOND:bb[0-9]+]], case #E.third!enumelt: [[BB_E_THIRD:bb[0-9]+]], case #E.fourth!enumelt: [[BB_E_FOURTH:bb[0-9]+]] +// +// CHECK: [[BB_E_FIRST]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT:bb[0-9]+]] +// +// CHECK: [[BB_E_SECOND]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_E_THIRD]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_E_FOURTH]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_CONT]]: +// CHECK: destroy_value [[MARKED_VALUE]] +// CHECK: } // end sil function 'switch_enum_without_destructure' +sil [ossa] @switch_enum_without_destructure : $@convention(thin) (@guaranteed E) -> () { +bb0(%0 : @guaranteed $E): + %1 = copy_value %0 : $E + %2 = mark_must_check [no_copy] %1 : $E + %3 = begin_borrow %2 : $E + switch_enum %3 : $E, case #E.first!enumelt: bb1, case #E.second!enumelt: bb2, case #E.third!enumelt: bb3, case #E.fourth!enumelt: bb4 + +bb1(%first : @guaranteed $MoveOnlyKlass): + br bbCont + +bb2(%second : @guaranteed $SingleIntContainingStruct): + br bbCont + +bb3(%third : @guaranteed $AggStruct2): + br bbCont + +bb4(%fourth: @guaranteed $Klass): + br bbCont + +bbCont: + end_borrow %3 : $E + destroy_value %2 : $E + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @switch_enum_with_destructure : $@convention(thin) (@guaranteed E) -> () { +// CHECK: bb0([[ARG:%.*]] : +// CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] +// CHECK: [[MARKED_VALUE:%.*]] = mark_must_check [no_copy] [[ARG_COPY]] +// CHECK: [[MARKED_COPY:%.*]] = copy_value [[MARKED_VALUE]] +// CHECK: switch_enum [[MARKED_COPY:%.*]] : $E, case #E.first!enumelt: [[BB_E_FIRST:bb[0-9]+]], case #E.second!enumelt: [[BB_E_SECOND:bb[0-9]+]], case #E.third!enumelt: [[BB_E_THIRD:bb[0-9]+]], case #E.fourth!enumelt: [[BB_E_FOURTH:bb[0-9]+]] +// +// CHECK: [[BB_E_FIRST]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT:bb[0-9]+]] +// +// CHECK: [[BB_E_SECOND]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_E_THIRD]]([[BBARG:%.*]] : @owned +// CHECK: [[FUNC:%.*]] = function_ref @moveonlyklass_consume : $@convention(thin) (@owned MoveOnlyKlass) -> () +// CHECK: ([[FIRST:%.*]], [[SECOND:%.*]], [[THIRD:%.*]]) = destructure_struct [[BBARG]] +// CHECK: destroy_value [[THIRD]] +// CHECK: destroy_value [[FIRST]] +// CHECK: ([[SECOND_FIRST:%.*]], [[SECOND_SECOND:%.*]]) = destructure_struct [[SECOND]] +// CHECK: destroy_value [[SECOND_SECOND]] +// CHECK: apply [[FUNC]]([[SECOND_FIRST]]) +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_E_FOURTH]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_CONT]]: +// CHECK: destroy_value [[MARKED_VALUE]] +// CHECK: } // end sil function 'switch_enum_with_destructure' +sil [ossa] @switch_enum_with_destructure : $@convention(thin) (@guaranteed E) -> () { +bb0(%0 : @guaranteed $E): + %1 = copy_value %0 : $E + %2 = mark_must_check [no_copy] %1 : $E + %3 = begin_borrow %2 : $E + switch_enum %3 : $E, case #E.first!enumelt: bb1, case #E.second!enumelt: bb2, case #E.third!enumelt: bb3, case #E.fourth!enumelt: bb4 + +bb1(%first : @guaranteed $MoveOnlyKlass): + br bbCont + +bb2(%second : @guaranteed $SingleIntContainingStruct): + br bbCont + +bb3(%third : @guaranteed $AggStruct2): + %third1 = begin_borrow %third : $AggStruct2 + %gep1 = struct_extract %third1 : $AggStruct2, #AggStruct2.pair + %gep2 = struct_extract %gep1 : $KlassPair2, #KlassPair2.lhs + %copy = copy_value %gep2 : $MoveOnlyKlass + %f = function_ref @moveonlyklass_consume : $@convention(thin) (@owned MoveOnlyKlass) -> () + apply %f(%copy) : $@convention(thin) (@owned MoveOnlyKlass) -> () + end_borrow %third1 : $AggStruct2 + br bbCont + +bb4(%fourth: @guaranteed $Klass): + br bbCont + +bbCont: + end_borrow %3 : $E + destroy_value %2 : $E + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @switch_enum_with_destructure_recursive : $@convention(thin) (@guaranteed E2) -> () { +// CHECK: bb0([[ARG:%.*]] : +// CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] +// CHECK: [[MARKED_VALUE:%.*]] = mark_must_check [no_copy] [[ARG_COPY]] +// CHECK: [[MARKED_COPY:%.*]] = copy_value [[MARKED_VALUE]] +// CHECK: switch_enum [[MARKED_COPY:%.*]] : $E2, case #E2.lhs!enumelt: [[BB_E2_LHS:bb[0-9]+]], case #E2.rhs!enumelt: [[BB_E2_RHS:bb[0-9]+]] +// +// CHECK: [[BB_E2_LHS]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT:bb[0-9]+]] +// +// CHECK: [[BB_E2_RHS]]([[BBARG:%.*]] : @owned +// CHECK: switch_enum [[BBARG]] : $E, case #E.first!enumelt: [[BB_E_FIRST:bb[0-9]+]], case #E.second!enumelt: [[BB_E_SECOND:bb[0-9]+]], case #E.third!enumelt: [[BB_E_THIRD:bb[0-9]+]], case #E.fourth!enumelt: [[BB_E_FOURTH:bb[0-9]+]] +// +// CHECK: [[BB_E_FIRST]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_E_SECOND]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_E_THIRD]]([[BBARG:%.*]] : @owned +// CHECK: [[FUNC:%.*]] = function_ref @moveonlyklass_consume : $@convention(thin) (@owned MoveOnlyKlass) -> () +// CHECK: ([[FIRST:%.*]], [[SECOND:%.*]], [[THIRD:%.*]]) = destructure_struct [[BBARG]] +// CHECK: destroy_value [[THIRD]] +// CHECK: destroy_value [[FIRST]] +// CHECK: ([[SECOND_FIRST:%.*]], [[SECOND_SECOND:%.*]]) = destructure_struct [[SECOND]] +// CHECK: destroy_value [[SECOND_SECOND]] +// CHECK: apply [[FUNC]]([[SECOND_FIRST]]) +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_E_FOURTH]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_CONT]]: +// CHECK: destroy_value [[MARKED_VALUE]] +// CHECK: } // end sil function 'switch_enum_with_destructure_recursive' +sil [ossa] @switch_enum_with_destructure_recursive : $@convention(thin) (@guaranteed E2) -> () { +bb0(%0 : @guaranteed $E2): + %1 = copy_value %0 : $E2 + %2 = mark_must_check [no_copy] %1 : $E2 + %3 = begin_borrow %2 : $E2 + switch_enum %3 : $E2, case #E2.lhs!enumelt: bbN1, case #E2.rhs!enumelt: bbN2 + +bbN1(%lhs : @guaranteed $AggStruct2): + br bbCont + +bbN2(%rhs: @guaranteed $E): + switch_enum %rhs : $E, case #E.first!enumelt: bb1, case #E.second!enumelt: bb2, case #E.third!enumelt: bb3, case #E.fourth!enumelt: bb4 + +bb1(%first : @guaranteed $MoveOnlyKlass): + br bbCont + +bb2(%second : @guaranteed $SingleIntContainingStruct): + br bbCont + +bb3(%third : @guaranteed $AggStruct2): + %third1 = begin_borrow %third : $AggStruct2 + %gep1 = struct_extract %third1 : $AggStruct2, #AggStruct2.pair + %gep2 = struct_extract %gep1 : $KlassPair2, #KlassPair2.lhs + %copy = copy_value %gep2 : $MoveOnlyKlass + %f = function_ref @moveonlyklass_consume : $@convention(thin) (@owned MoveOnlyKlass) -> () + apply %f(%copy) : $@convention(thin) (@owned MoveOnlyKlass) -> () + end_borrow %third1 : $AggStruct2 + br bbCont + +bb4(%fourth: @guaranteed $Klass): + br bbCont + +bbCont: + end_borrow %3 : $E2 + destroy_value %2 : $E2 + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @switch_enum_with_destructure_recursive_2 : $@convention(thin) (@guaranteed E2) -> () { +// CHECK: bb0([[ARG:%.*]] : +// CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] +// CHECK: [[MARKED_VALUE:%.*]] = mark_must_check [no_copy] [[ARG_COPY]] +// CHECK: [[MARKED_COPY:%.*]] = copy_value [[MARKED_VALUE]] +// CHECK: switch_enum [[MARKED_COPY:%.*]] : $E2, case #E2.lhs!enumelt: [[BB_E2_LHS:bb[0-9]+]], case #E2.rhs!enumelt: [[BB_E2_RHS:bb[0-9]+]] +// +// CHECK: [[BB_E2_LHS]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT:bb[0-9]+]] +// +// CHECK: [[BB_E2_RHS]]([[BBARG:%.*]] : @owned +// CHECK: switch_enum [[BBARG]] : $E, case #E.first!enumelt: [[BB_E_FIRST:bb[0-9]+]], case #E.second!enumelt: [[BB_E_SECOND:bb[0-9]+]], case #E.third!enumelt: [[BB_E_THIRD:bb[0-9]+]], case #E.fourth!enumelt: [[BB_E_FOURTH:bb[0-9]+]] +// +// CHECK: [[BB_E_FIRST]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_E_SECOND]]([[BBARG:%.*]] : @owned +// CHECK: [[BBARG_BORROW:%.*]] = begin_borrow [[BBARG]] +// CHECK: [[BBARG_BORROW_EXT:%.*]] = struct_extract [[BBARG_BORROW]] +// CHECK: apply {{%.*}}([[BBARG_BORROW_EXT]]) +// CHECK: end_borrow [[BBARG_BORROW]] +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_E_THIRD]]([[BBARG:%.*]] : @owned +// CHECK: [[FUNC:%.*]] = function_ref @moveonlyklass_consume : $@convention(thin) (@owned MoveOnlyKlass) -> () +// CHECK: ([[FIRST:%.*]], [[SECOND:%.*]], [[THIRD:%.*]]) = destructure_struct [[BBARG]] +// CHECK: destroy_value [[THIRD]] +// CHECK: destroy_value [[FIRST]] +// CHECK: ([[SECOND_FIRST:%.*]], [[SECOND_SECOND:%.*]]) = destructure_struct [[SECOND]] +// CHECK: destroy_value [[SECOND_SECOND]] +// CHECK: apply [[FUNC]]([[SECOND_FIRST]]) +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_E_FOURTH]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_CONT]]: +// CHECK: destroy_value [[MARKED_VALUE]] +// CHECK: } // end sil function 'switch_enum_with_destructure_recursive_2' +sil [ossa] @switch_enum_with_destructure_recursive_2 : $@convention(thin) (@guaranteed E2) -> () { +bb0(%0 : @guaranteed $E2): + %1 = copy_value %0 : $E2 + %2 = mark_must_check [no_copy] %1 : $E2 + %3 = begin_borrow %2 : $E2 + switch_enum %3 : $E2, case #E2.lhs!enumelt: bbN1, case #E2.rhs!enumelt: bbN2 + +bbN1(%lhs : @guaranteed $AggStruct2): + br bbCont + +bbN2(%rhs: @guaranteed $E): + switch_enum %rhs : $E, case #E.first!enumelt: bb1, case #E.second!enumelt: bb2, case #E.third!enumelt: bb3, case #E.fourth!enumelt: bb4 + +bb1(%first : @guaranteed $MoveOnlyKlass): + br bbCont + +bb2(%second : @guaranteed $SingleIntContainingStruct): + %second1 = begin_borrow %second : $SingleIntContainingStruct + %secondGep1 = struct_extract %second1 : $SingleIntContainingStruct, #SingleIntContainingStruct.value + %f2 = function_ref @builtin32_use : $@convention(thin) (Builtin.Int32) -> () + apply %f2(%secondGep1) : $@convention(thin) (Builtin.Int32) -> () + end_borrow %second1 : $SingleIntContainingStruct + br bbCont + +bb3(%third : @guaranteed $AggStruct2): + %third1 = begin_borrow %third : $AggStruct2 + %gep1 = struct_extract %third1 : $AggStruct2, #AggStruct2.pair + %gep2 = struct_extract %gep1 : $KlassPair2, #KlassPair2.lhs + %copy = copy_value %gep2 : $MoveOnlyKlass + %f = function_ref @moveonlyklass_consume : $@convention(thin) (@owned MoveOnlyKlass) -> () + apply %f(%copy) : $@convention(thin) (@owned MoveOnlyKlass) -> () + end_borrow %third1 : $AggStruct2 + br bbCont + +bb4(%fourth: @guaranteed $Klass): + br bbCont + +bbCont: + end_borrow %3 : $E2 + destroy_value %2 : $E2 + %9999 = tuple() + return %9999 : $() +} + +// CHECK-LABEL: sil [ossa] @switch_enum_with_destructure_recursive_3 : $@convention(thin) (@guaranteed E2) -> () { +// CHECK: bb0([[ARG:%.*]] : +// CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] +// CHECK: [[MARKED_VALUE:%.*]] = mark_must_check [no_copy] [[ARG_COPY]] +// CHECK: [[MARKED_COPY:%.*]] = copy_value [[MARKED_VALUE]] +// CHECK: switch_enum [[MARKED_COPY:%.*]] : $E2, case #E2.lhs!enumelt: [[BB_E2_LHS:bb[0-9]+]], case #E2.rhs!enumelt: [[BB_E2_RHS:bb[0-9]+]] +// +// CHECK: [[BB_E2_LHS]]([[BBARG:%.*]] : @owned +// CHECK: [[FUNC:%.*]] = function_ref @moveonlyklass_consume : $@convention(thin) (@owned MoveOnlyKlass) -> () +// CHECK: ([[FIRST:%.*]], [[SECOND:%.*]], [[THIRD:%.*]]) = destructure_struct [[BBARG]] +// CHECK: destroy_value [[THIRD]] +// CHECK: destroy_value [[FIRST]] +// CHECK: ([[SECOND_FIRST:%.*]], [[SECOND_SECOND:%.*]]) = destructure_struct [[SECOND]] +// CHECK: destroy_value [[SECOND_SECOND]] +// CHECK: apply [[FUNC]]([[SECOND_FIRST]]) +// CHECK: br [[BB_CONT:bb[0-9]+]] +// +// CHECK: [[BB_E2_RHS]]([[BBARG:%.*]] : @owned +// CHECK: switch_enum [[BBARG]] : $E, case #E.first!enumelt: [[BB_E_FIRST:bb[0-9]+]], case #E.second!enumelt: [[BB_E_SECOND:bb[0-9]+]], case #E.third!enumelt: [[BB_E_THIRD:bb[0-9]+]], case #E.fourth!enumelt: [[BB_E_FOURTH:bb[0-9]+]] +// +// CHECK: [[BB_E_FIRST]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_E_SECOND]]([[BBARG:%.*]] : @owned +// CHECK: [[BBARG_BORROW:%.*]] = begin_borrow [[BBARG]] +// CHECK: [[BBARG_BORROW_EXT:%.*]] = struct_extract [[BBARG_BORROW]] +// CHECK: apply {{%.*}}([[BBARG_BORROW_EXT]]) +// CHECK: end_borrow [[BBARG_BORROW]] +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_E_THIRD]]([[BBARG:%.*]] : @owned +// CHECK: [[FUNC:%.*]] = function_ref @moveonlyklass_consume : $@convention(thin) (@owned MoveOnlyKlass) -> () +// CHECK: ([[FIRST:%.*]], [[SECOND:%.*]], [[THIRD:%.*]]) = destructure_struct [[BBARG]] +// CHECK: destroy_value [[THIRD]] +// CHECK: destroy_value [[FIRST]] +// CHECK: ([[SECOND_FIRST:%.*]], [[SECOND_SECOND:%.*]]) = destructure_struct [[SECOND]] +// CHECK: destroy_value [[SECOND_SECOND]] +// CHECK: apply [[FUNC]]([[SECOND_FIRST]]) +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_E_FOURTH]]([[BBARG:%.*]] : @owned +// CHECK: destroy_value [[BBARG]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_CONT]]: +// CHECK: destroy_value [[MARKED_VALUE]] +// CHECK: } // end sil function 'switch_enum_with_destructure_recursive_3' +sil [ossa] @switch_enum_with_destructure_recursive_3 : $@convention(thin) (@guaranteed E2) -> () { +bb0(%0 : @guaranteed $E2): + %1 = copy_value %0 : $E2 + %2 = mark_must_check [no_copy] %1 : $E2 + %3 = begin_borrow %2 : $E2 + switch_enum %3 : $E2, case #E2.lhs!enumelt: bbN1, case #E2.rhs!enumelt: bbN2 + +bbN1(%lhs : @guaranteed $AggStruct2): + %lhs1 = begin_borrow %lhs : $AggStruct2 + %lhsgep1 = struct_extract %lhs1 : $AggStruct2, #AggStruct2.pair + %lhsgep2 = struct_extract %lhsgep1 : $KlassPair2, #KlassPair2.lhs + %lhscopy = copy_value %lhsgep2 : $MoveOnlyKlass + %f1 = function_ref @moveonlyklass_consume : $@convention(thin) (@owned MoveOnlyKlass) -> () + apply %f1(%lhscopy) : $@convention(thin) (@owned MoveOnlyKlass) -> () + end_borrow %lhs1 : $AggStruct2 + br bbCont + +bbN2(%rhs: @guaranteed $E): + switch_enum %rhs : $E, case #E.first!enumelt: bb1, case #E.second!enumelt: bb2, case #E.third!enumelt: bb3, case #E.fourth!enumelt: bb4 + +bb1(%first : @guaranteed $MoveOnlyKlass): + br bbCont + +bb2(%second : @guaranteed $SingleIntContainingStruct): + %second1 = begin_borrow %second : $SingleIntContainingStruct + %secondGep1 = struct_extract %second1 : $SingleIntContainingStruct, #SingleIntContainingStruct.value + %f2 = function_ref @builtin32_use : $@convention(thin) (Builtin.Int32) -> () + apply %f2(%secondGep1) : $@convention(thin) (Builtin.Int32) -> () + end_borrow %second1 : $SingleIntContainingStruct + br bbCont + +bb3(%third : @guaranteed $AggStruct2): + %third1 = begin_borrow %third : $AggStruct2 + %gep1 = struct_extract %third1 : $AggStruct2, #AggStruct2.pair + %gep2 = struct_extract %gep1 : $KlassPair2, #KlassPair2.lhs + %copy = copy_value %gep2 : $MoveOnlyKlass + %f = function_ref @moveonlyklass_consume : $@convention(thin) (@owned MoveOnlyKlass) -> () + apply %f(%copy) : $@convention(thin) (@owned MoveOnlyKlass) -> () + end_borrow %third1 : $AggStruct2 + br bbCont + +bb4(%fourth: @guaranteed $Klass): + br bbCont + +bbCont: + end_borrow %3 : $E2 + destroy_value %2 : $E2 + %9999 = tuple() + return %9999 : $() +} \ No newline at end of file From e70a7228cc169604917e56bdd24cf1c563d1dbda Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Tue, 7 Feb 2023 14:26:54 -0800 Subject: [PATCH 44/98] [move-only] Integrate BorrowToDestructureTransform into the AddressChecker so we handle load [copy] switch_enum. --- .../Mandatory/MoveOnlyAddressChecker.cpp | 53 +++++++++++++++++-- .../Mandatory/MoveOnlyBorrowToDestructure.h | 7 +-- .../MoveOnlyBorrowToDestructureTransform.cpp | 33 ++++++++---- ...OnlyBorrowToDestructureTransformTester.cpp | 4 +- .../Mandatory/MoveOnlyDiagnostics.cpp | 1 + .../Mandatory/MoveOnlyDiagnostics.h | 5 ++ .../Mandatory/MoveOnlyObjectChecker.cpp | 4 +- 7 files changed, 87 insertions(+), 20 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp index 83f1771549973..59cb4d59f9508 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyAddressChecker.cpp @@ -156,6 +156,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" +#include "MoveOnlyBorrowToDestructure.h" #include "MoveOnlyDiagnostics.h" #include "MoveOnlyObjectChecker.h" @@ -866,9 +867,15 @@ struct MoveOnlyChecker { /// Information about destroys that we use when inserting destroys. ConsumeInfo consumes; + /// Allocator used by the BorrowToDestructureTransform. + borrowtodestructure::IntervalMapAllocator allocator; + + /// PostOrderAnalysis used by the BorrowToDestructureTransform. + PostOrderAnalysis *poa; + MoveOnlyChecker(SILFunction *fn, DeadEndBlocks *deBlocks, - DominanceInfo *domTree) - : fn(fn), deleter(), canonicalizer(), diagnosticEmitter() { + DominanceInfo *domTree, PostOrderAnalysis *poa) + : fn(fn), deleter(), canonicalizer(), diagnosticEmitter(), poa(poa) { deleter.setCallbacks(std::move( InstModCallbacks().onDelete([&](SILInstruction *instToDelete) { if (auto *mvi = dyn_cast(instToDelete)) @@ -1121,6 +1128,31 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) { li->getOwnershipQualifier() == LoadOwnershipQualifier::Take) { SWIFT_DEFER { moveChecker.canonicalizer.clear(); }; + // Before we do anything, run the borrow to destructure transform in case + // we have a switch_enum user. + unsigned numDiagnostics = + moveChecker.diagnosticEmitter.getDiagnosticCount(); + BorrowToDestructureTransform borrowToDestructure( + moveChecker.allocator, markedValue, li, moveChecker.diagnosticEmitter, + moveChecker.poa); + if (!borrowToDestructure.transform()) { + assert(moveChecker.diagnosticEmitter + .didEmitCheckerDoesntUnderstandDiagnostic()); + LLVM_DEBUG(llvm::dbgs() + << "Failed to perform borrow to destructure transform!\n"); + emittedEarlyDiagnostic = true; + return false; + } + + // If we emitted an error diagnostic, do not transform further and instead + // mark that we emitted an early diagnostic and return true. + if (numDiagnostics != + moveChecker.diagnosticEmitter.getDiagnosticCount()) { + LLVM_DEBUG(llvm::dbgs() << "Emitting borrow to destructure error!\n"); + emittedEarlyDiagnostic = true; + return true; + } + // Canonicalize the lifetime of the load [take], load [copy]. moveChecker.changed |= moveChecker.canonicalizer.canonicalize(li); @@ -1558,6 +1590,19 @@ void MoveOnlyChecker::cleanupAfterEmittingDiagnostic() { changed = true; } } + + // Convert any copy_value of move_only type to explicit copy value. + if (auto *cvi = dyn_cast(inst)) { + if (!cvi->getOperand()->getType().isMoveOnly()) + continue; + SILBuilderWithScope b(cvi); + auto *expCopy = + b.createExplicitCopyValue(cvi->getLoc(), cvi->getOperand()); + cvi->replaceAllUsesWith(expCopy); + cvi->eraseFromParent(); + changed = true; + continue; + } } } } @@ -1982,8 +2027,10 @@ class MoveOnlyCheckerPass : public SILFunctionTransform { auto *dominanceAnalysis = getAnalysis(); DominanceInfo *domTree = dominanceAnalysis->get(fn); auto *deAnalysis = getAnalysis()->get(fn); + auto *poa = getAnalysis(); - if (MoveOnlyChecker(getFunction(), deAnalysis, domTree).checkFunction()) { + if (MoveOnlyChecker(getFunction(), deAnalysis, domTree, poa) + .checkFunction()) { invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); } diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h index f3bd9012927a8..bb260c0b6d163 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructure.h @@ -59,6 +59,7 @@ class BorrowToDestructureTransform { IntervalMapAllocator &allocator; MarkMustCheckInst *mmci; + SILValue rootValue; DiagnosticEmitter &diagnosticEmitter; PostOrderAnalysis *poa; PostOrderFunctionInfo *pofi = nullptr; @@ -67,11 +68,11 @@ class BorrowToDestructureTransform { public: BorrowToDestructureTransform(IntervalMapAllocator &allocator, - MarkMustCheckInst *mmci, + MarkMustCheckInst *mmci, SILValue rootValue, DiagnosticEmitter &diagnosticEmitter, PostOrderAnalysis *poa) - : allocator(allocator), mmci(mmci), diagnosticEmitter(diagnosticEmitter), - poa(poa) {} + : allocator(allocator), mmci(mmci), rootValue(rootValue), + diagnosticEmitter(diagnosticEmitter), poa(poa) {} bool transform(); diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp index 2f29877b7df5d..55bc745992af3 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransform.cpp @@ -1523,7 +1523,7 @@ void Implementation::cleanup() { /// /// Returns false if we found an escape and thus cannot process. It is assumed /// that the caller will fail in such a case. -static bool gatherBorrows(MarkMustCheckInst *mmci, +static bool gatherBorrows(SILValue rootValue, StackList &borrowWorklist) { // If we have a no implicit copy mark_must_check, we do not run the borrow to // destructure transform since: @@ -1534,15 +1534,16 @@ static bool gatherBorrows(MarkMustCheckInst *mmci, // 2. If we do not have a move only type, then we know that all fields that we // access directly and would cause a need to destructure must be copyable, // so no transformation/error is needed. - if (mmci->getType().isMoveOnlyWrapped()) { - LLVM_DEBUG(llvm::dbgs() << "Skipping move only wrapped inst: " << *mmci); + if (rootValue->getType().isMoveOnlyWrapped()) { + LLVM_DEBUG(llvm::dbgs() + << "Skipping move only wrapped inst: " << *rootValue); return true; } - LLVM_DEBUG(llvm::dbgs() << "Searching for borrows for inst: " << *mmci); + LLVM_DEBUG(llvm::dbgs() << "Searching for borrows for inst: " << *rootValue); - StackList worklist(mmci->getFunction()); - for (auto *op : mmci->getUses()) + StackList worklist(rootValue->getFunction()); + for (auto *op : rootValue->getUses()) worklist.push_back(op); while (!worklist.empty()) { @@ -1728,8 +1729,10 @@ bool BorrowToDestructureTransform::transform() { // If we failed to gather borrows due to the transform not understanding part // of the SIL, fail and return false. - if (!gatherBorrows(mmci, borrowWorklist)) + if (!gatherBorrows(rootValue, borrowWorklist)) { + diagnosticEmitter.emitCheckerDoesntUnderstandDiagnostic(mmci); return false; + } // If we do not have any borrows to process, return true early to show we // succeeded in processing. @@ -1742,8 +1745,10 @@ bool BorrowToDestructureTransform::transform() { SmallVector switchEnumWorklist; for (auto *borrow : borrowWorklist) { // Attempt to gather the switch enums and if we fail, return false. - if (!gatherSwitchEnum(borrow, switchEnumWorklist)) + if (!gatherSwitchEnum(borrow, switchEnumWorklist)) { + diagnosticEmitter.emitCheckerDoesntUnderstandDiagnostic(mmci); return false; + } } // Now perform the checking of our switch_enum, working in stack order. @@ -1863,15 +1868,23 @@ bool BorrowToDestructureTransform::transform() { } } + // At this point, we have correct OSSA SIL for our switch_enums. Check if for + // any of our switch_enum we emitted a we don't understand diagnostic... in + // such a case, exit before we do further work. + if (diagnosticEmitter.didEmitCheckerDoesntUnderstandDiagnostic()) + return false; + // Now that we have handled our switch_enum we need to handle our // borrows... begin by gathering uses. Return false if we saw something that // we did not understand. SmallVector discoveredBlocks; Implementation impl(*this, discoveredBlocks); - impl.init(mmci); + impl.init(rootValue); for (auto *bbi : borrowWorklist) { - if (!impl.gatherUses(bbi)) + if (!impl.gatherUses(bbi)) { + diagnosticEmitter.emitCheckerDoesntUnderstandDiagnostic(mmci); return false; + } } // Next make sure that any destructure needing instructions are on the diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp index 40a33f89fd573..ba11718ed7f13 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyBorrowToDestructureTransformTester.cpp @@ -55,8 +55,8 @@ static bool runTransform(SILFunction *fn, auto *mmci = moveIntroducersToProcess.back(); moveIntroducersToProcess = moveIntroducersToProcess.drop_back(); - BorrowToDestructureTransform transform(allocator, mmci, diagnosticEmitter, - poa); + BorrowToDestructureTransform transform(allocator, mmci, mmci, + diagnosticEmitter, poa); transform.transform(); madeChange = true; } diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.cpp index 649f549ed6c2e..30103d3270166 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.cpp @@ -102,6 +102,7 @@ void DiagnosticEmitter::emitCheckerDoesntUnderstandDiagnostic( diag::sil_moveonlychecker_not_understand_moveonly); } registerDiagnosticEmitted(markedValue); + emittedCheckerDoesntUnderstandDiagnostic = true; } //===----------------------------------------------------------------------===// diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.h b/lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.h index 33fc612745823..9c3063ced1955 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.h +++ b/lib/SILOptimizer/Mandatory/MoveOnlyDiagnostics.h @@ -50,6 +50,8 @@ class DiagnosticEmitter { /// diagnosics while running a callee. unsigned diagnosticCount = 0; + bool emittedCheckerDoesntUnderstandDiagnostic = false; + public: void init(SILFunction *inputFn, OSSACanonicalizer *inputCanonicalizer) { fn = inputFn; @@ -65,6 +67,9 @@ class DiagnosticEmitter { } unsigned getDiagnosticCount() const { return diagnosticCount; } + bool didEmitCheckerDoesntUnderstandDiagnostic() const { + return emittedCheckerDoesntUnderstandDiagnostic; + } void emitCheckerDoesntUnderstandDiagnostic(MarkMustCheckInst *markedValue); void emitObjectGuaranteedDiagnostic(MarkMustCheckInst *markedValue); diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp index 90c14772616d1..b001348ea17f5 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp @@ -423,8 +423,8 @@ struct MoveOnlyChecker { bool MoveOnlyChecker::convertBorrowExtractsToOwnedDestructures( MarkMustCheckInst *mmci, DiagnosticEmitter &diagnosticEmitter, DominanceInfo *domTree, PostOrderAnalysis *poa) { - BorrowToDestructureTransform transform(allocator, mmci, diagnosticEmitter, - poa); + BorrowToDestructureTransform transform(allocator, mmci, mmci, + diagnosticEmitter, poa); if (!transform.transform()) { LLVM_DEBUG(llvm::dbgs() << "Failed to perform borrow to destructure transform!\n"); From a624fece3e8d2ba192b8c269c8af2ce4439cefd9 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 8 Feb 2023 13:24:16 -0800 Subject: [PATCH 45/98] [move-only] Move MoveOnlyObjectChecker /before/ MoveOnlyAddressChecker in the pass pipeline and move post checker verification into the MoveOnlyAddressChecker. The reason why I am doing this is that: 1. There are sometimes copy_value on move only values loaded from memory that the MoveOnlyAddressChecker needs to eliminate. 2. Previously, the move only address checker did not rewrite copy_value -> explicit_copy_value if it failed in diagnostics (it did handle load [copy] and copy_addr though). This could then cause the copy_value elimination verification in the MoveOnlyObjectChecker to then fail. So this suggested that I needed to move the verification from the object checkt to the address checker. 3. If we run the verification in the address checker, then the object checker (which previously ran before the address checker) naturally needed to run /before/ the address checker. --- lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp | 3 +++ lib/SILOptimizer/PassManager/PassPipeline.cpp | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp index b001348ea17f5..d9c55279d213b 100644 --- a/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp +++ b/lib/SILOptimizer/Mandatory/MoveOnlyObjectChecker.cpp @@ -806,6 +806,9 @@ class MoveOnlyCheckerPass : public SILFunctionTransform { if (checker.changed) { invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); } + + // NOTE: We validate in the MoveOnlyAddressChecker (which runs after this) + // that we eliminated all copy_value and emit errors otherwise. } }; diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 5c4549edc439b..e37d33a57febb 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -154,10 +154,14 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) { // resolution of nonescaping closure lifetimes to correctly check the use // of move-only values as captures in nonescaping closures as borrows. - // Check noImplicitCopy and move only types for addresses. - P.addMoveOnlyAddressChecker(); // Check noImplicitCopy and move only types for objects + // + // NOTE: It is important that this is run /before/ move only address checker + // since if the address checker emits an error, it will cleanup copy_value of + // move only objects meaning that we could lose object level diagnostics. P.addMoveOnlyObjectChecker(); + // Check noImplicitCopy and move only types for addresses. + P.addMoveOnlyAddressChecker(); // Convert last destroy_value to deinits. P.addMoveOnlyDeinitInsertion(); // Lower move only wrapped trivial types. From 61b63839b2bb71269f3b3e9ee0500fe25467f902 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Mon, 6 Feb 2023 14:57:21 -0800 Subject: [PATCH 46/98] [move-only] Now that borrow to destructure transform knows how to handle switch_enum, emit move only enum switches at +0. --- lib/SILGen/SILGenPattern.cpp | 20 +- test/SILGen/moveonly.swift | 216 +++++++++++++----- .../moveonly_objectchecker_diagnostics.swift | 46 ++++ 3 files changed, 222 insertions(+), 60 deletions(-) diff --git a/lib/SILGen/SILGenPattern.cpp b/lib/SILGen/SILGenPattern.cpp index d83eaa7167258..db49dcc3cf466 100644 --- a/lib/SILGen/SILGenPattern.cpp +++ b/lib/SILGen/SILGenPattern.cpp @@ -2843,14 +2843,28 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) { // Inline constructor for subject. auto subject = ([&]() -> ConsumableManagedValue { - // If we have a move only value, ensure plus one and convert it. Switches - // always consume move only values. + // If we have a noImplicitCopy value, ensure plus one and convert + // it. Switches always consume move only values. + // + // NOTE: We purposely do not do this for pure move only types since for them + // we emit everything at +0 and then run the BorrowToDestructure transform + // upon them. The reason that we do this is that internally to + // SILGenPattern, we always attempt to move from +1 -> +0 meaning that even + // if we start at +1, we will go back to +0 given enough patterns to go + // through. It is simpler to just let SILGenPattern do what it already wants + // to do, rather than fight it or try to resusitate the "fake owned borrow" + // path that we still use for address only types (and that we want to delete + // once we have opaque values). if (subjectMV.getType().isMoveOnly() && subjectMV.getType().isObject()) { if (subjectMV.getType().isMoveOnlyWrapped()) { subjectMV = B.createOwnedMoveOnlyWrapperToCopyableValue( S, subjectMV.ensurePlusOne(*this, S)); } else { - subjectMV = B.createMoveValue(S, subjectMV.ensurePlusOne(*this, S)); + // If we have a pure move only type and it is owned, borrow it so that + // BorrowToDestructure can handle it. + if (subjectMV.getOwnershipKind() == OwnershipKind::Owned) { + subjectMV = subjectMV.borrow(*this, S); + } } } diff --git a/test/SILGen/moveonly.swift b/test/SILGen/moveonly.swift index 1ac340f82bd65..dacc048a029f4 100644 --- a/test/SILGen/moveonly.swift +++ b/test/SILGen/moveonly.swift @@ -29,18 +29,12 @@ public final class Klass { } } -public func nonConsumingUseKlass(_ k: Klass) {} -public func nonConsumingUseCopyableKlass(_ k: CopyableKlass) {} -public func nonConsumingUseKlass2(_ k: Klass2) {} - @_moveOnly public struct NonTrivialStruct2 { var moveOnlyKlass = Klass() var copyableKlass = CopyableKlass() } -public func nonConsumingUseNonTrivialStruct2(_ s: NonTrivialStruct2) {} - @_moveOnly public struct NonTrivialStruct { var moveOnlyKlass = Klass() @@ -49,8 +43,6 @@ public struct NonTrivialStruct { var nonTrivialCopyableStruct = NonTrivialCopyableStruct() } -public func nonConsumingUseNonTrivialStruct(_ s: NonTrivialStruct) {} - public struct NonTrivialCopyableStruct2 { var copyableKlass = CopyableKlass() var copyableKlass2 = CopyableKlass() @@ -61,9 +53,6 @@ public struct NonTrivialCopyableStruct { var nonTrivialCopyableStruct2 = NonTrivialCopyableStruct2() } -public func nonConsumingUseCopyableStruct(_ k: NonTrivialCopyableStruct) {} -public func nonConsumingUseCopyableStruct2(_ k: NonTrivialCopyableStruct2) {} - @_moveOnly public enum NonTrivialEnum { case first @@ -71,7 +60,23 @@ public enum NonTrivialEnum { case third(NonTrivialStruct) } -public func nonConsumingUseNonTrivialEnum(_ e : NonTrivialEnum) {} +public func borrowVal(_ e : NonTrivialEnum) {} +public func borrowVal(_ k: CopyableKlass) {} +public func borrowVal(_ k: Klass) {} +public func borrowVal(_ k: Klass2) {} +public func borrowVal(_ k: NonTrivialCopyableStruct) {} +public func borrowVal(_ k: NonTrivialCopyableStruct2) {} +public func borrowVal(_ s: NonTrivialStruct) {} +public func borrowVal(_ s: NonTrivialStruct2) {} + +public func consumeVal(_ e : __owned NonTrivialEnum) {} +public func consumeVal(_ k: __owned CopyableKlass) {} +public func consumeVal(_ k: __owned Klass) {} +public func consumeVal(_ k: __owned Klass2) {} +public func consumeVal(_ k: __owned NonTrivialCopyableStruct) {} +public func consumeVal(_ k: __owned NonTrivialCopyableStruct2) {} +public func consumeVal(_ s: __owned NonTrivialStruct) {} +public func consumeVal(_ s: __owned NonTrivialStruct2) {} /////////// // Tests // @@ -87,9 +92,9 @@ public func nonConsumingUseNonTrivialEnum(_ e : NonTrivialEnum) {} // CHECK: [[MARKED_OWNED_ARG:%.*]] = mark_must_check [no_copy] [[OWNED_ARG]] // CHECK: } // end sil function '$s8moveonly8useKlassyyAA0C0CF' public func useKlass(_ k: Klass) { - nonConsumingUseKlass(k) + borrowVal(k) let k2 = k - nonConsumingUseKlass(k) + borrowVal(k) let _ = k2 } @@ -100,10 +105,9 @@ public func useKlass(_ k: Klass) { // CHECK: mark_must_check [no_implicit_copy] [[LEXICAL_MOVE]] // CHECK: } // end sil function '$s8moveonly15useKlassConsumeyyAA0C0CnF' public func useKlassConsume(_ k: __owned Klass) { - nonConsumingUseKlass(k) + borrowVal(k) let k2 = k - // NOTE: We should mark the next line as a lifetime extending use. - nonConsumingUseKlass(k) + borrowVal(k) let _ = k2 } @@ -113,11 +117,11 @@ public func useKlassConsume(_ k: __owned Klass) { // CHECK: mark_must_check [no_copy] [[COPIED_ARG]] // CHECK: } // end sil function '$s8moveonly19useNonTrivialStructyyAA0cdE0VF' public func useNonTrivialStruct(_ s: NonTrivialStruct) { - nonConsumingUseNonTrivialStruct(s) + borrowVal(s) let s2 = s let k = s.moveOnlyKlass let _ = k - nonConsumingUseNonTrivialStruct(s) + borrowVal(s) let _ = s2 } @@ -127,11 +131,11 @@ public func useNonTrivialStruct(_ s: NonTrivialStruct) { // CHECK: mark_must_check [no_implicit_copy] [[MOVED_ARG]] // CHECK: } // end sil function '$s8moveonly24useNonTrivialOwnedStructyyAA0cdF0VnF' public func useNonTrivialOwnedStruct(_ s: __owned NonTrivialStruct) { - nonConsumingUseNonTrivialStruct(s) + borrowVal(s) let s2 = s let k = s.moveOnlyKlass let _ = k - nonConsumingUseNonTrivialStruct(s) + borrowVal(s) let _ = s2 } @@ -141,13 +145,13 @@ public func useNonTrivialOwnedStruct(_ s: __owned NonTrivialStruct) { // CHECK: mark_must_check [no_copy] [[COPIED_ARG]] // CHECK: } // end sil function '$s8moveonly17useNonTrivialEnumyyAA0cdE0OF' public func useNonTrivialEnum(_ s: NonTrivialEnum) { - nonConsumingUseNonTrivialEnum(s) + borrowVal(s) let s2 = s switch s { case _: break } - nonConsumingUseNonTrivialEnum(s) + borrowVal(s) let _ = s2 } @@ -157,13 +161,13 @@ public func useNonTrivialEnum(_ s: NonTrivialEnum) { // CHECK: mark_must_check [no_implicit_copy] [[MOVED_ARG]] // CHECK: } // end sil function '$s8moveonly22useNonTrivialOwnedEnumyyAA0cdF0OnF' public func useNonTrivialOwnedEnum(_ s: __owned NonTrivialEnum) { - nonConsumingUseNonTrivialEnum(s) + borrowVal(s) let s2 = s switch s { case _: break } - nonConsumingUseNonTrivialEnum(s) + borrowVal(s) let _ = s2 } @@ -286,20 +290,20 @@ func blackHoleVarInitialization2() { // CHECK-LABEL: sil hidden [ossa] @$s8moveonly24borrowObjectFunctionCallyyF : $@convention(thin) () -> () { // CHECK: [[CLS:%.*]] = mark_must_check [no_implicit_copy] // CHECK: [[BORROW:%.*]] = begin_borrow [[CLS]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly20nonConsumingUseKlassyyAA0E0CF +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA5KlassCF // CHECK: apply [[FN]]([[BORROW]]) // CHECK: end_borrow [[BORROW]] // CHECK: } // end sil function '$s8moveonly24borrowObjectFunctionCallyyF' func borrowObjectFunctionCall() { let k = Klass() - nonConsumingUseKlass(k) + borrowVal(k) } // CHECK-LABEL: sil hidden [ossa] @$s8moveonly25borrowAddressFunctionCallyyF : $@convention(thin) () -> () { // CHECK: [[BOX:%.*]] = mark_must_check [no_implicit_copy] // CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[BOX]] // CHECK: [[BORROW:%.*]] = load_borrow [[ACCESS]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly20nonConsumingUseKlassyyAA0E0CF +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA5KlassCF // CHECK: apply [[FN]]([[BORROW]]) // CHECK: end_borrow [[BORROW]] // CHECK: end_access [[ACCESS]] @@ -307,7 +311,7 @@ func borrowObjectFunctionCall() { func borrowAddressFunctionCall() { var k = Klass() k = Klass() - nonConsumingUseKlass(k) + borrowVal(k) } // We currently have the wrong behavior here since the class is treated by @@ -321,7 +325,7 @@ func borrowAddressFunctionCall() { func klassBorrowAddressFunctionCall2() { var k = Klass() k = Klass() - nonConsumingUseKlass2(k.moveOnlyKlass) + borrowVal(k.moveOnlyKlass) } // We copy here since we have a class as our base like the above. @@ -341,14 +345,14 @@ func klassBorrowAddressFunctionCall2() { func klassBorrowCopyableAddressFunctionCall() { var k = Klass() k = Klass() - nonConsumingUseCopyableKlass(k.copyableKlass) + borrowVal(k.copyableKlass) } // CHECK-LABEL: sil hidden [ossa] @$s8moveonly29moveOnlyStructNonConsumingUseyyF : $@convention(thin) () -> () { // CHECK: [[MARKED_ADDR:%.*]] = mark_must_check [no_implicit_copy] // CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[MARKED_ADDR]] // CHECK: [[BORROW:%.*]] = load_borrow [[ACCESS]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly31nonConsumingUseNonTrivialStructyyAA0efG0VF : +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA16NonTrivialStructVF : // CHECK: apply [[FN]]([[BORROW]]) // CHECK: end_borrow [[BORROW]] // CHECK: end_access [[ACCESS]] @@ -356,7 +360,7 @@ func klassBorrowCopyableAddressFunctionCall() { func moveOnlyStructNonConsumingUse() { var k = NonTrivialStruct() k = NonTrivialStruct() - nonConsumingUseNonTrivialStruct(k) + borrowVal(k) } // CHECK-LABEL: sil hidden [ossa] @$s8moveonly018moveOnlyStructMoveC20KlassNonConsumingUseyyF : $@convention(thin) () -> () { @@ -364,7 +368,7 @@ func moveOnlyStructNonConsumingUse() { // CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[MARKED_ADDR]] // CHECK: [[STRUCT_EXT:%.*]] = struct_element_addr [[ACCESS]] // CHECK: [[BORROW:%.*]] = load_borrow [[STRUCT_EXT]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly20nonConsumingUseKlassyyAA0E0CF +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA5KlassCF // CHECK: apply [[FN]]([[BORROW]]) // CHECK: end_borrow [[BORROW]] // CHECK: end_access [[ACCESS]] @@ -372,7 +376,7 @@ func moveOnlyStructNonConsumingUse() { func moveOnlyStructMoveOnlyKlassNonConsumingUse() { var k = NonTrivialStruct() k = NonTrivialStruct() - nonConsumingUseKlass(k.moveOnlyKlass) + borrowVal(k.moveOnlyKlass) } // We fail here b/c we are accessing through a class. @@ -387,7 +391,7 @@ func moveOnlyStructMoveOnlyKlassNonConsumingUse() { // CHECK: [[ELT_ADDR:%.*]] = ref_element_addr [[STRUCT_EXT_COPY_BORROW]] // CHECK: [[ACCESS_ELT_ADDR:%.*]] = begin_access [read] [dynamic] [[ELT_ADDR]] // CHECK: [[KLS:%.*]] = load_borrow [[ACCESS_ELT_ADDR]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly21nonConsumingUseKlass2yyAA0E0CF +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA6Klass2CF // CHECK: apply [[FN]]([[KLS]]) // CHECK: end_borrow [[KLS]] // CHECK: destroy_value [[STRUCT_EXT_COPY]] @@ -395,7 +399,7 @@ func moveOnlyStructMoveOnlyKlassNonConsumingUse() { func moveOnlyStructMoveOnlyKlassMoveOnlyKlassNonConsumingUse() { var k = NonTrivialStruct() k = NonTrivialStruct() - nonConsumingUseKlass2(k.moveOnlyKlass.moveOnlyKlass) + borrowVal(k.moveOnlyKlass.moveOnlyKlass) } // We fail here b/c we are accessing through a class. @@ -410,7 +414,7 @@ func moveOnlyStructMoveOnlyKlassMoveOnlyKlassNonConsumingUse() { // CHECK: [[FIELD:%.*]] = ref_element_addr [[STRUCT_EXT_COPY_BORROW]] // CHECK: [[ACCESS:%.*]] = begin_access [read] [dynamic] [[FIELD]] // CHECK: [[BORROWED_KLS:%.*]] = load_borrow [[ACCESS]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly28nonConsumingUseCopyableKlassyyAA0eF0CF +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA13CopyableKlassCF // CHECK: apply [[FN]]([[BORROWED_KLS]]) // CHECK: end_borrow [[BORROWED_KLS]] // CHECK: destroy_value [[STRUCT_EXT_COPY]] @@ -418,7 +422,7 @@ func moveOnlyStructMoveOnlyKlassMoveOnlyKlassNonConsumingUse() { func moveOnlyStructMoveOnlyKlassCopyableKlassNonConsumingUse() { var k = NonTrivialStruct() k = NonTrivialStruct() - nonConsumingUseCopyableKlass(k.moveOnlyKlass.copyableKlass) + borrowVal(k.moveOnlyKlass.copyableKlass) } // CHECK-LABEL: sil hidden [ossa] @$s8moveonly018moveOnlyStructMovecD15NonConsumingUseyyF : $@convention(thin) () -> () { @@ -426,7 +430,7 @@ func moveOnlyStructMoveOnlyKlassCopyableKlassNonConsumingUse() { // CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[MARKED_ADDR]] // CHECK: [[GEP:%.*]] = struct_element_addr [[ACCESS]] : $*NonTrivialStruct, #NonTrivialStruct.nonTrivialStruct2 // CHECK: [[BORROW:%.*]] = load_borrow [[GEP]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly32nonConsumingUseNonTrivialStruct2yyAA0efG0VF : $@convention(thin) (@guaranteed NonTrivialStruct2) -> () +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA17NonTrivialStruct2VF : $@convention(thin) (@guaranteed NonTrivialStruct2) -> () // CHECK: apply [[FN]]([[BORROW]]) // CHECK: end_borrow [[BORROW]] // CHECK: end_access [[ACCESS]] @@ -434,7 +438,7 @@ func moveOnlyStructMoveOnlyKlassCopyableKlassNonConsumingUse() { func moveOnlyStructMoveOnlyStructNonConsumingUse() { var k = NonTrivialStruct() k = NonTrivialStruct() - nonConsumingUseNonTrivialStruct2(k.nonTrivialStruct2) + borrowVal(k.nonTrivialStruct2) } // CHECK-LABEL: sil hidden [ossa] @$s8moveonly018moveOnlyStructMovecdeC20KlassNonConsumingUseyyF : $@convention(thin) () -> () { @@ -443,7 +447,7 @@ func moveOnlyStructMoveOnlyStructNonConsumingUse() { // CHECK: [[GEP1:%.*]] = struct_element_addr [[ACCESS]] : $*NonTrivialStruct, #NonTrivialStruct.nonTrivialStruct2 // CHECK: [[GEP2:%.*]] = struct_element_addr [[GEP1]] : $*NonTrivialStruct2, #NonTrivialStruct2.moveOnlyKlass // CHECK: [[BORROW:%.*]] = load_borrow [[GEP2]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly20nonConsumingUseKlassyyAA0E0CF : $@convention(thin) (@guaranteed Klass) -> () +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA5KlassCF : $@convention(thin) (@guaranteed Klass) -> () // CHECK: apply [[FN]]([[BORROW]]) // CHECK: end_borrow [[BORROW]] // CHECK: end_access [[ACCESS]] @@ -451,7 +455,7 @@ func moveOnlyStructMoveOnlyStructNonConsumingUse() { func moveOnlyStructMoveOnlyStructMoveOnlyKlassNonConsumingUse() { var k = NonTrivialStruct() k = NonTrivialStruct() - nonConsumingUseKlass(k.nonTrivialStruct2.moveOnlyKlass) + borrowVal(k.nonTrivialStruct2.moveOnlyKlass) } // CHECK-LABEL: sil hidden [ossa] @$s8moveonly018moveOnlyStructMovecD28CopyableKlassNonConsumingUseyyF : $@convention(thin) () -> () { @@ -460,7 +464,7 @@ func moveOnlyStructMoveOnlyStructMoveOnlyKlassNonConsumingUse() { // CHECK: [[GEP1:%.*]] = struct_element_addr [[ACCESS]] : $*NonTrivialStruct, #NonTrivialStruct.nonTrivialStruct2 // CHECK: [[GEP2:%.*]] = struct_element_addr [[GEP1]] : $*NonTrivialStruct2, #NonTrivialStruct2.copyableKlass // CHECK: [[BORROW:%.*]] = load_borrow [[GEP2]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly28nonConsumingUseCopyableKlassyyAA0eF0CF : $@convention(thin) (@guaranteed CopyableKlass) -> () +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA13CopyableKlassCF : $@convention(thin) (@guaranteed CopyableKlass) -> () // CHECK: apply [[FN]]([[BORROW]]) // CHECK: end_borrow [[BORROW]] // CHECK: end_access [[ACCESS]] @@ -468,7 +472,7 @@ func moveOnlyStructMoveOnlyStructMoveOnlyKlassNonConsumingUse() { func moveOnlyStructMoveOnlyStructCopyableKlassNonConsumingUse() { var k = NonTrivialStruct() k = NonTrivialStruct() - nonConsumingUseCopyableKlass(k.nonTrivialStruct2.copyableKlass) + borrowVal(k.nonTrivialStruct2.copyableKlass) } // CHECK-LABEL: sil hidden [ossa] @$s8moveonly42moveOnlyStructCopyableKlassNonConsumingUseyyF : $@convention(thin) () -> () { @@ -476,7 +480,7 @@ func moveOnlyStructMoveOnlyStructCopyableKlassNonConsumingUse() { // CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[MARKED_ADDR]] // CHECK: [[GEP:%.*]] = struct_element_addr [[ACCESS]] : $*NonTrivialStruct, #NonTrivialStruct.copyableKlass // CHECK: [[BORROW:%.*]] = load_borrow [[GEP]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly28nonConsumingUseCopyableKlassyyAA0eF0CF : $@convention(thin) (@guaranteed CopyableKlass) -> () +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA13CopyableKlassCF : $@convention(thin) (@guaranteed CopyableKlass) -> () // CHECK: apply [[FN]]([[BORROW]]) // CHECK: end_borrow [[BORROW]] // CHECK: end_access [[ACCESS]] @@ -484,7 +488,7 @@ func moveOnlyStructMoveOnlyStructCopyableKlassNonConsumingUse() { func moveOnlyStructCopyableKlassNonConsumingUse() { var k = NonTrivialStruct() k = NonTrivialStruct() - nonConsumingUseCopyableKlass(k.copyableKlass) + borrowVal(k.copyableKlass) } // CHECK-LABEL: sil hidden [ossa] @$s8moveonly022moveOnlyStructCopyableD15NonConsumingUseyyF : $@convention(thin) () -> () { @@ -492,7 +496,7 @@ func moveOnlyStructCopyableKlassNonConsumingUse() { // CHECK: [[ACCESS:%.*]] = begin_access [read] [unknown] [[MARKED_ADDR]] // CHECK: [[GEP:%.*]] = struct_element_addr [[ACCESS]] : $*NonTrivialStruct, #NonTrivialStruct.nonTrivialCopyableStruct // CHECK: [[BORROW:%.*]] = load_borrow [[GEP]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly29nonConsumingUseCopyableStructyyAA010NonTrivialeF0VF : +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA24NonTrivialCopyableStructVF : // CHECK: apply [[FN]]([[BORROW]]) // CHECK: end_borrow [[BORROW]] // CHECK: end_access [[ACCESS]] @@ -500,7 +504,7 @@ func moveOnlyStructCopyableKlassNonConsumingUse() { func moveOnlyStructCopyableStructNonConsumingUse() { var k = NonTrivialStruct() k = NonTrivialStruct() - nonConsumingUseCopyableStruct(k.nonTrivialCopyableStruct) + borrowVal(k.nonTrivialCopyableStruct) } // CHECK-LABEL: sil hidden [ossa] @$s8moveonly022moveOnlyStructCopyabledE20KlassNonConsumingUseyyF : $@convention(thin) () -> () { @@ -509,7 +513,7 @@ func moveOnlyStructCopyableStructNonConsumingUse() { // CHECK: [[GEP1:%.*]] = struct_element_addr [[ACCESS]] : $*NonTrivialStruct, #NonTrivialStruct.nonTrivialCopyableStruct // CHECK: [[GEP2:%.*]] = struct_element_addr [[GEP1]] : $*NonTrivialCopyableStruct, #NonTrivialCopyableStruct.copyableKlass // CHECK: [[BORROW:%.*]] = load_borrow [[GEP2]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly28nonConsumingUseCopyableKlassyyAA0eF0CF : +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA13CopyableKlassCF : // CHECK: apply [[FN]]([[BORROW]]) // CHECK: end_borrow [[BORROW]] // CHECK: end_access [[ACCESS]] @@ -517,7 +521,7 @@ func moveOnlyStructCopyableStructNonConsumingUse() { func moveOnlyStructCopyableStructCopyableKlassNonConsumingUse() { var k = NonTrivialStruct() k = NonTrivialStruct() - nonConsumingUseCopyableKlass(k.nonTrivialCopyableStruct.copyableKlass) + borrowVal(k.nonTrivialCopyableStruct.copyableKlass) } // CHECK-LABEL: sil hidden [ossa] @$s8moveonly022moveOnlyStructCopyabledeD15NonConsumingUseyyF : $@convention(thin) () -> () { @@ -526,7 +530,7 @@ func moveOnlyStructCopyableStructCopyableKlassNonConsumingUse() { // CHECK: [[GEP1:%.*]] = struct_element_addr [[ACCESS]] : $*NonTrivialStruct, #NonTrivialStruct.nonTrivialCopyableStruct // CHECK: [[GEP2:%.*]] = struct_element_addr [[GEP1]] : $*NonTrivialCopyableStruct, #NonTrivialCopyableStruct.nonTrivialCopyableStruct2 // CHECK: [[BORROW:%.*]] = load_borrow [[GEP2]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly30nonConsumingUseCopyableStruct2yyAA010NonTrivialeF0VF : +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA25NonTrivialCopyableStruct2VF : // CHECK: apply [[FN]]([[BORROW]]) // CHECK: end_borrow [[BORROW]] // CHECK: end_access [[ACCESS]] @@ -534,7 +538,7 @@ func moveOnlyStructCopyableStructCopyableKlassNonConsumingUse() { func moveOnlyStructCopyableStructCopyableStructNonConsumingUse() { var k = NonTrivialStruct() k = NonTrivialStruct() - nonConsumingUseCopyableStruct2(k.nonTrivialCopyableStruct.nonTrivialCopyableStruct2) + borrowVal(k.nonTrivialCopyableStruct.nonTrivialCopyableStruct2) } // CHECK-LABEL: sil hidden [ossa] @$s8moveonly022moveOnlyStructCopyablededE20KlassNonConsumingUseyyF : $@convention(thin) () -> () { @@ -544,7 +548,7 @@ func moveOnlyStructCopyableStructCopyableStructNonConsumingUse() { // CHECK: [[GEP2:%.*]] = struct_element_addr [[GEP1]] : $*NonTrivialCopyableStruct, #NonTrivialCopyableStruct.nonTrivialCopyableStruct2 // CHECK: [[GEP3:%.*]] = struct_element_addr [[GEP2]] : $*NonTrivialCopyableStruct2, #NonTrivialCopyableStruct2.copyableKlass // CHECK: [[BORROW:%.*]] = load_borrow [[GEP3]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly28nonConsumingUseCopyableKlassyyAA0eF0CF : +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA13CopyableKlassCF : // CHECK: apply [[FN]]([[BORROW]]) // CHECK: end_borrow [[BORROW]] // CHECK: end_access [[ACCESS]] @@ -552,7 +556,7 @@ func moveOnlyStructCopyableStructCopyableStructNonConsumingUse() { func moveOnlyStructCopyableStructCopyableStructCopyableKlassNonConsumingUse() { var k = NonTrivialStruct() k = NonTrivialStruct() - nonConsumingUseCopyableKlass(k.nonTrivialCopyableStruct.nonTrivialCopyableStruct2.copyableKlass) + borrowVal(k.nonTrivialCopyableStruct.nonTrivialCopyableStruct2.copyableKlass) } // We fail here b/c we are accessing through a class. @@ -569,7 +573,7 @@ func moveOnlyStructCopyableStructCopyableStructCopyableKlassNonConsumingUse() { // CHECK: [[FIELD:%.*]] = ref_element_addr [[BORROWED_COPYABLE_KLASS]] // CHECK: [[ACCESS:%.*]] = begin_access [read] [dynamic] [[FIELD]] // CHECK: [[BORROWED_MOVEONLY_KLASS:%.*]] = load_borrow [[ACCESS]] -// CHECK: [[FN:%.*]] = function_ref @$s8moveonly20nonConsumingUseKlassyyAA0E0CF : +// CHECK: [[FN:%.*]] = function_ref @$s8moveonly9borrowValyyAA5KlassCF : // CHECK: apply [[FN]]([[BORROWED_MOVEONLY_KLASS]]) // CHECK: end_borrow [[BORROWED_MOVEONLY_KLASS]] // CHECK: destroy_value [[COPYABLE_KLASS]] @@ -577,5 +581,103 @@ func moveOnlyStructCopyableStructCopyableStructCopyableKlassNonConsumingUse() { func moveOnlyStructCopyableStructCopyableStructCopyableKlassMoveOnlyKlassNonConsumingUse() { var k = NonTrivialStruct() k = NonTrivialStruct() - nonConsumingUseKlass(k.nonTrivialCopyableStruct.nonTrivialCopyableStruct2.copyableKlass.k) + borrowVal(k.nonTrivialCopyableStruct.nonTrivialCopyableStruct2.copyableKlass.k) +} + +/////////////////////// +// Enum Switch Tests // +/////////////////////// + +enum EnumSwitchTests { + @_moveOnly + enum E2 { + case lhs(CopyableKlass) + case rhs(Klass) + } + + @_moveOnly + enum E { + case first(NonTrivialStruct2) + case second(NonTrivialStruct) + case third(CopyableKlass) + case fourth(E2) + } +} + +func consumeVal(_ e: __owned EnumSwitchTests.E2) {} + +var booleanGuard: Bool { false } +var booleanGuard2: Bool { false } + +// CHECK-LABEL: sil hidden [ossa] @$s8moveonly15enumSwitchTest1yyAA04EnumC5TestsO1EOF : $@convention(thin) (@guaranteed EnumSwitchTests.E) -> () { +// CHECK: bb0([[ARG:%.*]] : @guaranteed +// CHECK: [[COPY_ARG:%.*]] = copy_value [[ARG]] +// CHECK: [[MARKED_VALUE:%.*]] = mark_must_check [no_copy] [[COPY_ARG]] +// CHECK: [[BORROWED_VALUE:%.*]] = begin_borrow [[MARKED_VALUE]] +// CHECK: switch_enum [[BORROWED_VALUE]] : $EnumSwitchTests.E, case #EnumSwitchTests.E.first!enumelt: [[BB_E_1:bb[0-9]+]], case #EnumSwitchTests.E.second!enumelt: [[BB_E_2:bb[0-9]+]], case #EnumSwitchTests.E.third!enumelt: [[BB_E_3:bb[0-9]+]], case #EnumSwitchTests.E.fourth!enumelt: [[BB_E_4:bb[0-9]+]] +// +// CHECK: [[BB_E_1]]([[BBARG:%.*]] : @guaranteed +// CHECK: end_borrow [[BORROWED_VALUE]] +// CHECK: br [[BB_CONT:bb[0-9]+]] +// +// CHECK: [[BB_E_2]]([[BBARG:%.*]] : @guaranteed +// CHECK: [[BBARG_COPY:%.*]] = copy_value [[BBARG]] +// CHECK: [[NEW_VAL:%.*]] = move_value [lexical] [[BBARG_COPY]] +// CHECK: end_borrow [[BORROWED_VALUE]] +// CHECK: br [[BB_CONT]] +// +// This case is copyable +// CHECK: [[BB_E_3]]([[BBARG:%.*]] : @guaranteed +// CHECK: [[BBARG_COPY:%.*]] = copy_value [[BBARG]] +// CHECK: begin_borrow [lexical] [[BBARG_COPY]] +// CHECK: end_borrow [[BORROWED_VALUE]] +// CHECK: br [[BB_CONT]] +// +// This is a guard case. +// CHECK: [[BB_E_4]]([[BBARG:%.*]] : @guaranteed +// CHECK: cond_br {{%.*}}, [[BB_GUARD_1:bb[0-9]+]], [[BB_GUARD_2:bb[0-9]+]] +// +// CHECK: [[BB_GUARD_1]]: +// CHECK: end_borrow [[BORROWED_VALUE]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_GUARD_2]]: +// CHECK: switch_enum [[BBARG]] : $EnumSwitchTests.E2, case #EnumSwitchTests.E2.lhs!enumelt: [[BB_E2_LHS:bb[0-9]+]], case #EnumSwitchTests.E2.rhs!enumelt: [[BB_E2_RHS:bb[0-9]+]] +// +// Copyable case +// CHECK: [[BB_E2_LHS]]([[BBARG:%.*]] : @guaranteed +// CHECK: [[BBARG_COPY:%.*]] = copy_value [[BBARG]] +// CHECK: begin_borrow [lexical] [[BBARG_COPY]] +// CHECK: end_borrow [[BORROWED_VALUE]] +// CHECK: br [[BB_CONT]] +// +// Move only case. +// CHECK: [[BB_E2_RHS]]([[BBARG:%.*]] : @guaranteed +// CHECK: [[BBARG_COPY:%.*]] = copy_value [[BBARG]] +// CHECK: move_value [lexical] [[BBARG_COPY]] +// CHECK: end_borrow [[BORROWED_VALUE]] +// CHECK: br [[BB_CONT]] +// +// CHECK: [[BB_CONT]]: +// CHECK: destroy_value [[MARKED_VALUE]] +// CHECK: } // end sil function '$s8moveonly15enumSwitchTest1yyAA04EnumC5TestsO1EOF' +func enumSwitchTest1(_ e: EnumSwitchTests.E) { + switch e { + case .first: + break + case .second(let x): + borrowVal(x) + break + case .third(let y): + borrowVal(y) + break + case .fourth where booleanGuard: + break + case .fourth(.lhs(let lhs)): + borrowVal(lhs) + break + case .fourth(.rhs(let rhs)): + consumeVal(rhs) + break + } } diff --git a/test/SILOptimizer/moveonly_objectchecker_diagnostics.swift b/test/SILOptimizer/moveonly_objectchecker_diagnostics.swift index ad2ddcaf96502..58dce27ea9732 100644 --- a/test/SILOptimizer/moveonly_objectchecker_diagnostics.swift +++ b/test/SILOptimizer/moveonly_objectchecker_diagnostics.swift @@ -4,6 +4,8 @@ // Declarations // ////////////////// +public class CopyableKlass {} + @_moveOnly public final class Klass { var intField: Int @@ -17,6 +19,7 @@ public final class Klass { var boolValue: Bool { return true } public func borrowVal(_ x: __shared Klass) {} +public func borrowVal(_ x: __shared CopyableKlass) {} public func borrowVal(_ x: __shared FinalKlass) {} public func borrowVal(_ x: __shared AggStruct) {} public func borrowVal(_ x: __shared KlassPair) {} @@ -2432,3 +2435,46 @@ func sameCallSiteConsumeAndUse(_ k: __owned Klass) { // expected-error {{'k' con func consumeKlassAndUseKlass(_ k: __owned Klass, _ k2: Klass) {} consumeKlassAndUseKlass(k, k) // expected-note {{consuming and non-consuming uses here}} } + +//////////////////////////////// +// Recursive Enum Switch Test // +//////////////////////////////// + +enum EnumSwitchTests { + @_moveOnly + enum E2 { + case lhs(CopyableKlass) + case rhs(Klass) + } + + @_moveOnly + enum E { + case first(KlassPair) + case second(AggStruct) + case third(CopyableKlass) + case fourth(E2) + } +} + +func consumeVal(_ e: __owned EnumSwitchTests.E2) {} + +func enumSwitchTest1(_ e: __owned EnumSwitchTests.E) { + switch e { + case .first: + break + case .second(let x): + borrowVal(x) + break + case .third(let y): + borrowVal(y) + break + case .fourth where boolValue: + break + case .fourth(.lhs(let lhs)): + borrowVal(lhs) + break + case .fourth(.rhs(let rhs)): + consumeVal(rhs) + break + } +} From d56e16943e780a2ed97f9a2bca3fa4b4c0e69a36 Mon Sep 17 00:00:00 2001 From: Michael Gottesman Date: Wed, 8 Feb 2023 14:00:22 -0800 Subject: [PATCH 47/98] [move-only] Fix up a test slightly. This test was taken from some generated SIL when the address checker ran /before/ the object checker and before the address checker validated that the object checker also didn't leave any copy_value. This is just cleaning up the source as if the object checker ran on this specific function so the address checker check doesn't trip. --- test/SILOptimizer/moveonly_addresschecker.sil | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/SILOptimizer/moveonly_addresschecker.sil b/test/SILOptimizer/moveonly_addresschecker.sil index 77914c709f310..53562a775c1c2 100644 --- a/test/SILOptimizer/moveonly_addresschecker.sil +++ b/test/SILOptimizer/moveonly_addresschecker.sil @@ -283,10 +283,8 @@ bb0(%arg : @owned $Klass): %21 = move_value [lexical] %19 : $Klass %22 = mark_must_check [no_implicit_copy] %21 : $Klass debug_value %22 : $Klass, let, name "k2" - %24 = copy_value %22 : $Klass - %25 = move_value %24 : $Klass + %25 = move_value %22 : $Klass destroy_value %25 : $Klass - destroy_value %22 : $Klass destroy_addr %1 : $*Klass dealloc_stack %0 : $*Klass %30 = tuple () From bf61578f81a73c6f41faf5d90d23a642438a4156 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 8 Feb 2023 14:34:11 -0800 Subject: [PATCH 48/98] [Macros] Don't assert when a macro parameter has a default argument --- lib/Parse/ParseDecl.cpp | 3 +-- test/Macros/macros_diagnostics.swift | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 724619dff2cf4..1c0d2d2fc9343 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -9502,9 +9502,8 @@ ParserResult Parser::parseDeclMacro(DeclAttributes &attributes) { } else { // Parameter list. SmallVector namePieces; - DefaultArgumentInfo defaultArgs; auto parameterResult = parseSingleParameterClause( - ParameterContextKind::Macro, &namePieces, &defaultArgs); + ParameterContextKind::Macro, &namePieces, nullptr); status |= parameterResult; parameterList = parameterResult.getPtrOrNull(); diff --git a/test/Macros/macros_diagnostics.swift b/test/Macros/macros_diagnostics.swift index 5dd1e2a82b7cf..a399b6c56fd4a 100644 --- a/test/Macros/macros_diagnostics.swift +++ b/test/Macros/macros_diagnostics.swift @@ -134,3 +134,8 @@ func testExternalMacroOutOfPlace() { let _: Int = #externalMacro(module: "A", type: "B") // expected-error@-1{{macro 'externalMacro' can only be used to define another macro}} } + +@freestanding(expression) +public macro macroWithDefaults(_: Int = 17) = #externalMacro(module: "A", type: "B") +// expected-error@-1{{default arguments are not allowed in macros}} +// expected-warning@-2{{external macro implementation type 'A.B' could not be found for macro 'macroWithDefaults'}} From 0f4429b2240a7f6939cb4cbcbfcc5484eb610cd6 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 16 Dec 2022 15:09:55 -0800 Subject: [PATCH 49/98] DebugTypeInfo: Prefer the size of the Storage type derived from the TypeInfo. Previously type sizes would be inconsistently sourced from either the LLVM type or the FixedTypeInfo, depending on the call site. This was problematic because TypeInfo operates with a resolution of whole bytes, which means that types such as i1 would get a reported as having a size of 8. This patch now asserts that all occurrences of the same type have the same size as the first, cached occurence. To avoid triggering the cached type verification assertion, this patch avoids caching of storage-sized containers. It also removes the unique identifier from forward declarations, which could lead to type confusion during LTO. rdar://102367872 --- lib/IRGen/DebugTypeInfo.cpp | 15 +- lib/IRGen/DebugTypeInfo.h | 8 +- lib/IRGen/IRGenDebugInfo.cpp | 194 +++++++++++--------- lib/IRGen/IRGenSIL.cpp | 19 +- test/DebugInfo/BoundGenericEnum.swift | 12 +- test/DebugInfo/BoundGenericStruct.swift | 6 +- test/DebugInfo/Imports.swift | 1 - test/DebugInfo/ResilientSize.swift | 3 +- test/DebugInfo/apple-types-accel.swift | 7 +- test/DebugInfo/archetypes2.swift | 16 +- test/DebugInfo/async-boxed-arg.swift | 2 +- test/DebugInfo/bound-namealiastype.swift | 2 +- test/DebugInfo/enum.swift | 28 ++- test/DebugInfo/fnptr.swift | 7 +- test/DebugInfo/generic_arg.swift | 11 +- test/DebugInfo/generic_arg3.swift | 5 +- test/DebugInfo/generic_arg4.swift | 2 +- test/DebugInfo/generic_arg5.swift | 7 +- test/DebugInfo/generic_args.swift | 9 +- test/DebugInfo/generic_enum.swift | 4 +- test/DebugInfo/generic_enum_closure.swift | 2 +- test/DebugInfo/inlined-generics-basic.swift | 14 +- test/DebugInfo/mangling.swift | 2 +- test/DebugInfo/protocol-sugar.swift | 2 +- test/DebugInfo/struct_resilience.swift | 2 +- test/DebugInfo/structs.swift | 8 +- test/DebugInfo/variables.swift | 2 +- test/DebugInfo/vector.swift | 12 +- 28 files changed, 203 insertions(+), 199 deletions(-) diff --git a/lib/IRGen/DebugTypeInfo.cpp b/lib/IRGen/DebugTypeInfo.cpp index a9a34dcdbe114..d42e049898e42 100644 --- a/lib/IRGen/DebugTypeInfo.cpp +++ b/lib/IRGen/DebugTypeInfo.cpp @@ -47,10 +47,13 @@ static bool hasDefaultAlignment(swift::Type Ty) { } DebugTypeInfo DebugTypeInfo::getFromTypeInfo(swift::Type Ty, const TypeInfo &TI, + IRGenModule &IGM, bool IsFragmentTypeInfo) { Optional SizeInBits; llvm::Type *StorageType = TI.getStorageType(); - if (TI.isFixedSize()) { + if (StorageType->isSized()) + SizeInBits = IGM.DataLayout.getTypeSizeInBits(StorageType); + else if (TI.isFixedSize()) { const FixedTypeInfo &FixTy = *cast(&TI); Size::int_type Size = FixTy.getFixedSize().getValue() * 8; SizeInBits = Size; @@ -63,6 +66,7 @@ DebugTypeInfo DebugTypeInfo::getFromTypeInfo(swift::Type Ty, const TypeInfo &TI, DebugTypeInfo DebugTypeInfo::getLocalVariable(VarDecl *Decl, swift::Type Ty, const TypeInfo &Info, + IRGenModule &IGM, bool IsFragmentTypeInfo) { auto DeclType = Decl->getInterfaceType(); @@ -77,7 +81,7 @@ DebugTypeInfo DebugTypeInfo::getLocalVariable(VarDecl *Decl, swift::Type Ty, // the type hasn't been mucked with by an optimization pass. auto *Type = Sugared->isEqual(RealType) ? DeclType.getPointer() : RealType.getPointer(); - return getFromTypeInfo(Type, Info, IsFragmentTypeInfo); + return getFromTypeInfo(Type, Info, IGM, IsFragmentTypeInfo); } DebugTypeInfo DebugTypeInfo::getGlobalMetadata(swift::Type Ty, @@ -120,7 +124,7 @@ DebugTypeInfo DebugTypeInfo::getGlobal(SILGlobalVariable *GV, Type = DeclType.getPointer(); } auto &TI = IGM.getTypeInfoForUnlowered(Type); - DebugTypeInfo DbgTy = getFromTypeInfo(Type, TI, false); + DebugTypeInfo DbgTy = getFromTypeInfo(Type, TI, IGM, false); assert(FragmentStorageType && "FragmentStorageType is a nullptr"); assert(!DbgTy.isContextArchetype() && "type of global variable cannot be an archetype"); @@ -163,7 +167,7 @@ DebugTypeInfo DebugTypeInfo::getObjCClass(ClassDecl *theClass, DebugTypeInfo DebugTypeInfo::getErrorResult(swift::Type Ty, IRGenModule &IGM) { auto &TI = IGM.getTypeInfoForUnlowered(Ty); - DebugTypeInfo DbgTy = getFromTypeInfo(Ty, TI, false); + DebugTypeInfo DbgTy = getFromTypeInfo(Ty, TI, IGM, false); return DbgTy; } @@ -195,7 +199,8 @@ LLVM_DUMP_METHOD void DebugTypeInfo::dump() const { if (SizeInBits) llvm::errs() << "SizeInBits " << *SizeInBits << " "; llvm::errs() << "Alignment " << Align.getValue() << "] "; - getType()->dump(llvm::errs()); + if (auto *Type = getType()) + Type->dump(llvm::errs()); if (FragmentStorageType) { llvm::errs() << "FragmentStorageType="; diff --git a/lib/IRGen/DebugTypeInfo.h b/lib/IRGen/DebugTypeInfo.h index a1b911ee6008c..e119f1c1a0584 100644 --- a/lib/IRGen/DebugTypeInfo.h +++ b/lib/IRGen/DebugTypeInfo.h @@ -61,7 +61,7 @@ class DebugTypeInfo { /// Create type for a local variable. static DebugTypeInfo getLocalVariable(VarDecl *Decl, swift::Type Ty, - const TypeInfo &Info, + const TypeInfo &Info, IRGenModule &IGM, bool IsFragmentTypeInfo); /// Create type for global type metadata. static DebugTypeInfo getGlobalMetadata(swift::Type Ty, llvm::Type *StorageTy, @@ -75,6 +75,7 @@ class DebugTypeInfo { /// Create a standalone type from a TypeInfo object. static DebugTypeInfo getFromTypeInfo(swift::Type Ty, const TypeInfo &Info, + IRGenModule &IGM, bool IsFragmentTypeInfo); /// Global variables. static DebugTypeInfo getGlobal(SILGlobalVariable *GV, @@ -111,7 +112,6 @@ class DebugTypeInfo { return SizeIsFragmentSize ? llvm::None : SizeInBits; } Optional getRawSizeInBits() const { return SizeInBits; } - void setSizeInBits(Size::int_type NewSize) { SizeInBits = NewSize; } Alignment getAlignment() const { return Align; } bool isNull() const { return Type == nullptr; } bool isForwardDecl() const { return FragmentStorageType == nullptr; } @@ -139,9 +139,9 @@ class CompletedDebugTypeInfo : public DebugTypeInfo { } static Optional - getFromTypeInfo(swift::Type Ty, const TypeInfo &Info) { + getFromTypeInfo(swift::Type Ty, const TypeInfo &Info, IRGenModule &IGM) { return CompletedDebugTypeInfo::get( - DebugTypeInfo::getFromTypeInfo(Ty, Info, /*IsFragment*/ false)); + DebugTypeInfo::getFromTypeInfo(Ty, Info, IGM, /*IsFragment*/ false)); } Size::int_type getSizeInBits() const { return *SizeInBits; } diff --git a/lib/IRGen/IRGenDebugInfo.cpp b/lib/IRGen/IRGenDebugInfo.cpp index 9d5147d8d34a5..9eda96037ebfb 100644 --- a/lib/IRGen/IRGenDebugInfo.cpp +++ b/lib/IRGen/IRGenDebugInfo.cpp @@ -137,7 +137,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { /// \} /// A list of replaceable fwddecls that need to be RAUWed at the end. - std::vector> ReplaceMap; + std::vector> FwdDeclTypes; /// The set of imported modules. llvm::DenseSet ImportedModules; @@ -617,23 +617,10 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { return getOrCreateContext(DC->getParent()); case DeclContextKind::GenericTypeDecl: { auto *NTD = cast(DC); - auto *Ty = NTD->getDeclaredType().getPointer(); - if (auto *DITy = getTypeOrNull(Ty)) - return DITy; - + auto Ty = NTD->getDeclaredInterfaceType(); // Create a Forward-declared type. - auto Loc = getFilenameAndLocation(*this, NTD); - auto File = getOrCreateFile(Loc.filename); - // No line numbers are attached to type forward declarations. - auto Line = 0; - auto FwdDecl = DBuilder.createReplaceableCompositeType( - llvm::dwarf::DW_TAG_structure_type, NTD->getName().str(), - getOrCreateContext(DC->getParent()), File, Line, - llvm::dwarf::DW_LANG_Swift, 0, 0); - ReplaceMap.emplace_back( - std::piecewise_construct, std::make_tuple(Ty), - std::make_tuple(static_cast(FwdDecl))); - return FwdDecl; + auto DbgTy = DebugTypeInfo::getForwardDecl(Ty); + return getOrCreateType(DbgTy); } } return TheCU; @@ -641,10 +628,8 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { void createParameterType(llvm::SmallVectorImpl &Parameters, SILType type) { - bool IsFragment = false; auto RealType = type.getASTType(); - auto DbgTy = DebugTypeInfo::getFromTypeInfo(RealType, IGM.getTypeInfo(type), - IsFragment); + auto DbgTy = DebugTypeInfo::getForwardDecl(RealType); Parameters.push_back(getOrCreateType(DbgTy)); } @@ -993,14 +978,15 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { if (auto DbgTy = CompletedDebugTypeInfo::getFromTypeInfo( VD->getInterfaceType(), IGM.getTypeInfoForUnlowered( - IGM.getSILTypes().getAbstractionPattern(VD), memberTy))) + IGM.getSILTypes().getAbstractionPattern(VD), memberTy), + IGM)) Elements.push_back(createMemberType(*DbgTy, VD->getName().str(), OffsetInBits, Scope, File, Flags)); else // Without complete type info we can only create a forward decl. return DBuilder.createForwardDecl( - llvm::dwarf::DW_TAG_structure_type, Name, Scope, File, Line, - llvm::dwarf::DW_LANG_Swift, SizeInBits, 0, UniqueID); + llvm::dwarf::DW_TAG_structure_type, UniqueID, Scope, File, Line, + llvm::dwarf::DW_LANG_Swift, SizeInBits, 0); } if (OffsetInBits > SizeInBits) SizeInBits = OffsetInBits; @@ -1040,7 +1026,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { // LLVM now supports variant types in debug metadata, which may be a // better fit. Optional ElemDbgTy; - if (Decl->hasRawType()) + if (Decl->hasRawType()) { // An enum with a raw type (enum E : Int {}), similar to a // DWARF enum. // @@ -1048,23 +1034,21 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { // one of the raw type as long as it is large enough to hold // all enum values. Use the raw type for the debug type, but // the storage size from the enum. - ElemDbgTy = CompletedDebugTypeInfo::get( - DebugTypeInfo(Decl->getRawType(), DbgTy.getFragmentStorageType(), - DbgTy.getRawSizeInBits(), DbgTy.getAlignment(), true, - false, DbgTy.isSizeFragmentSize())); - else if (auto ArgTy = ElemDecl->getArgumentInterfaceType()) { + auto RawType = Decl->getRawType(); + auto &TI = IGM.getTypeInfoForUnlowered(RawType); + ElemDbgTy = CompletedDebugTypeInfo::getFromTypeInfo(RawType, TI, IGM); + } else if (auto ArgTy = ElemDecl->getArgumentInterfaceType()) { // A discriminated union. This should really be described as a // DW_TAG_variant_type. For now only describing the data. ArgTy = ElemDecl->getParentEnum()->mapTypeIntoContext(ArgTy); auto &TI = IGM.getTypeInfoForUnlowered(ArgTy); - ElemDbgTy = CompletedDebugTypeInfo::getFromTypeInfo(ArgTy, TI); + ElemDbgTy = CompletedDebugTypeInfo::getFromTypeInfo(ArgTy, TI, IGM); } else { // Discriminated union case without argument. Fallback to Int // as the element type; there is no storage here. Type IntTy = IGM.Context.getIntType(); - ElemDbgTy = CompletedDebugTypeInfo::get( - DebugTypeInfo(IntTy, DbgTy.getFragmentStorageType(), 0, - Alignment(1), true, false, false)); + auto &TI = IGM.getTypeInfoForUnlowered(IntTy); + ElemDbgTy = CompletedDebugTypeInfo::getFromTypeInfo(IntTy, TI, IGM); } if (!ElemDbgTy) { // Without complete type info we can only create a forward decl. @@ -1088,10 +1072,11 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { } llvm::DIType *getOrCreateDesugaredType(Type Ty, DebugTypeInfo DbgTy) { - DebugTypeInfo BlandDbgTy( - Ty, DbgTy.getFragmentStorageType(), DbgTy.getRawSizeInBits(), - DbgTy.getAlignment(), DbgTy.hasDefaultAlignment(), - DbgTy.isMetadataType(), DbgTy.isSizeFragmentSize()); + DebugTypeInfo BlandDbgTy(Ty, DbgTy.getFragmentStorageType(), + DbgTy.getRawSizeInBits(), DbgTy.getAlignment(), + DbgTy.hasDefaultAlignment(), + DbgTy.isMetadataType(), DbgTy.isSizeFragmentSize(), + DbgTy.isFixedBuffer()); return getOrCreateType(BlandDbgTy); } @@ -1139,8 +1124,8 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { UniqueType = cast(V); else { UniqueType = DBuilder.createForwardDecl( - llvm::dwarf::DW_TAG_structure_type, Name, Scope, File, Line, - llvm::dwarf::DW_LANG_Swift, 0, 0, MangledName); + llvm::dwarf::DW_TAG_structure_type, MangledName, Scope, File, Line, + llvm::dwarf::DW_LANG_Swift, 0, 0); if (BoundParams) DBuilder.replaceArrays(UniqueType, nullptr, BoundParams); InnerTypeCache[UID] = llvm::TrackingMDNodeRef(UniqueType); @@ -1281,7 +1266,8 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { for (auto ElemTy : TupleTy->getElementTypes()) { auto &elemTI = IGM.getTypeInfoForUnlowered( AbstractionPattern(genericSig, ElemTy->getCanonicalType()), ElemTy); - if (auto DbgTy = CompletedDebugTypeInfo::getFromTypeInfo(ElemTy, elemTI)) + if (auto DbgTy = + CompletedDebugTypeInfo::getFromTypeInfo(ElemTy, elemTI, IGM)) Elements.push_back( createMemberType(*DbgTy, "", OffsetInBits, Scope, MainFile, Flags)); else @@ -1449,12 +1435,12 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { StringRef Name = Decl->getName().str(); if (!DbgTy.getTypeSizeInBits()) return DBuilder.createForwardDecl( - llvm::dwarf::DW_TAG_structure_type, Name, Scope, File, FwdDeclLine, - llvm::dwarf::DW_LANG_Swift, 0, AlignInBits, MangledName); + llvm::dwarf::DW_TAG_structure_type, MangledName, Scope, File, + FwdDeclLine, llvm::dwarf::DW_LANG_Swift, 0, AlignInBits); if (DbgTy.isFixedBuffer()) return DBuilder.createForwardDecl( - llvm::dwarf::DW_TAG_structure_type, Name, Scope, File, FwdDeclLine, - llvm::dwarf::DW_LANG_Swift, 0, AlignInBits, MangledName); + llvm::dwarf::DW_TAG_structure_type, MangledName, Scope, File, + FwdDeclLine, llvm::dwarf::DW_LANG_Swift, 0, AlignInBits); return createOpaqueStruct(Scope, Name, File, FwdDeclLine, SizeInBits, AlignInBits, Flags, MangledName); } @@ -1577,7 +1563,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { auto FwdDecl = llvm::TempDIType(DBuilder.createReplaceableCompositeType( llvm::dwarf::DW_TAG_structure_type, MangledName, Scope, File, FwdDeclLine, llvm::dwarf::DW_LANG_Swift, SizeInBits, AlignInBits, - Flags, MangledName)); + Flags)); // Emit the protocols the archetypes conform to. SmallVector Protocols; @@ -1586,7 +1572,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { IGM.getLoweredType(ProtocolDecl->getInterfaceType()).getASTType(); auto PDbgTy = DebugTypeInfo::getFromTypeInfo( ProtocolDecl->getInterfaceType(), IGM.getTypeInfoForLowered(PTy), - false); + IGM, false); auto PDITy = getOrCreateType(PDbgTy); Protocols.push_back( DBuilder.createInheritance(FwdDecl.get(), PDITy, 0, 0, Flags)); @@ -1594,7 +1580,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { auto DITy = DBuilder.createStructType( Scope, MangledName, File, FwdDeclLine, SizeInBits, AlignInBits, Flags, DerivedFrom, DBuilder.getOrCreateArray(Protocols), - llvm::dwarf::DW_LANG_Swift, nullptr, MangledName); + llvm::dwarf::DW_LANG_Swift, nullptr); DBuilder.replaceTemporary(std::move(FwdDecl), DITy); return DITy; @@ -1657,7 +1643,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { auto *BuiltinVectorTy = BaseTy->castTo(); auto ElemTy = BuiltinVectorTy->getElementType(); auto ElemDbgTy = DebugTypeInfo::getFromTypeInfo( - ElemTy, IGM.getTypeInfoForUnlowered(ElemTy), false); + ElemTy, IGM.getTypeInfoForUnlowered(ElemTy), IGM, false); unsigned Count = BuiltinVectorTy->getNumElements(); auto Subscript = DBuilder.getOrCreateSubrange(0, Count ? Count : -1); return DBuilder.createVectorType(SizeInBits, AlignInBits, @@ -1691,10 +1677,10 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { // For TypeAlias types, the DeclContext for the aliased type is // in the decl of the alias type. - DebugTypeInfo AliasedDbgTy(AliasedTy, DbgTy.getFragmentStorageType(), - DbgTy.getRawSizeInBits(), DbgTy.getAlignment(), - DbgTy.hasDefaultAlignment(), false, - DbgTy.isSizeFragmentSize()); + DebugTypeInfo AliasedDbgTy( + AliasedTy, DbgTy.getFragmentStorageType(), DbgTy.getRawSizeInBits(), + DbgTy.getAlignment(), DbgTy.hasDefaultAlignment(), false, + DbgTy.isSizeFragmentSize(), DbgTy.isFixedBuffer()); return DBuilder.createTypedef(getOrCreateType(AliasedDbgTy), MangledName, File, 0, Scope); } @@ -1777,6 +1763,25 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { return DBuilder.createNameSpace(Parent, PD.str(), ExportSymbols); } +#ifndef NDEBUG + /// Verify that the size of this type matches the one of the cached type. + bool sanityCheckCachedType(DebugTypeInfo DbgTy, llvm::DIType *CachedType) { + if (DbgTy.isForwardDecl()) + return true; + auto SizeInBits = DbgTy.getTypeSizeInBits(); + unsigned CachedSizeInBits = getSizeInBits(CachedType); + if ((SizeInBits && CachedSizeInBits != *SizeInBits) || + (!SizeInBits && CachedSizeInBits)) { + CachedType->dump(); + DbgTy.dump(); + llvm::errs() << "SizeInBits = " << SizeInBits << "\n"; + llvm::errs() << "CachedSizeInBits = " << CachedSizeInBits << "\n"; + return false; + } + return true; + } +#endif + llvm::DIType *getOrCreateType(DebugTypeInfo DbgTy) { // Is this an empty type? if (DbgTy.isNull()) @@ -1785,20 +1790,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { // Look in the cache first. if (auto *DITy = getTypeOrNull(DbgTy.getType())) { - // FIXME: Enable this assertion. -#if SWIFT_DEBUGINFO_CACHE_VERIFICATION - if (auto SizeInBits = DbgTy.getTypeSizeInBits()) { - if (unsigned CachedSizeInBits = getSizeInBits(DITy)) { - if (CachedSizeInBits != *SizeInBits) { - DITy->dump(); - DbgTy.dump(); - llvm::errs() << "SizeInBits = " << *SizeInBits << "\n"; - llvm::errs() << "CachedSizeInBits = " << CachedSizeInBits << "\n"; - } - assert(CachedSizeInBits == *SizeInBits); - } - } -#endif + assert(sanityCheckCachedType(DbgTy, DITy)); return DITy; } @@ -1812,6 +1804,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { UID = llvm::MDString::get(IGM.getLLVMContext(), MangledName); if (llvm::Metadata *CachedTy = DIRefMap.lookup(UID)) { auto DITy = cast(CachedTy); + assert(sanityCheckCachedType(DbgTy, DITy)); return DITy; } } @@ -1861,28 +1854,50 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo { // Scope outermost fileprivate decls in an inline private discriminator // namespace. - if (auto *Decl = DbgTy.getDecl()) + StringRef Name = MangledName; + if (auto *Decl = DbgTy.getDecl()) { + Name = Decl->getName().str(); if (Decl->isOutermostPrivateOrFilePrivateScope()) Scope = getFilePrivateScope(Scope, Decl); + } // If this is a forward decl, create one for this mangled name and don't // cache it. if (DbgTy.isForwardDecl() && !isa(DbgTy.getType())) { + // In LTO type uniquing is performed based on the UID. Forward + // declarations may not have a unique ID to avoid a forward declaration + // winning over a full definition. auto *FwdDecl = DBuilder.createReplaceableCompositeType( llvm::dwarf::DW_TAG_structure_type, MangledName, Scope, 0, 0, - llvm::dwarf::DW_LANG_Swift, 0, 0, llvm::DINode::FlagFwdDecl, - MangledName); - ReplaceMap.emplace_back( - std::piecewise_construct, std::make_tuple(DbgTy.getType()), + llvm::dwarf::DW_LANG_Swift, 0, 0, llvm::DINode::FlagFwdDecl); + FwdDeclTypes.emplace_back( + std::piecewise_construct, std::make_tuple(MangledName), std::make_tuple(static_cast(FwdDecl))); return FwdDecl; } llvm::DIType *DITy = createType(DbgTy, MangledName, Scope, getFile(Scope)); // Don't cache a type alias to a forward declaration either. - if (DbgTy.isForwardDecl()) + if (DbgTy.isForwardDecl() || DbgTy.isFixedBuffer() || + DITy->isForwardDecl()) return DITy; + if (auto Ty = DbgTy.getType()) + switch (Ty->getKind()) { + case TypeKind::PrimaryArchetype: + // FIXME: Primary archetypes carry all sorts of auxiliary information + // that isn't contained in their mangled name. See also + // getMangledName(). + return DITy; + case TypeKind::BoundGenericEnum: + case TypeKind::BoundGenericStruct: + // FIXME: These are emitted in sized anonymous containers, and their + // size is not consistent when resilient. + return DITy; + default: + break; + } + // Incrementally build the DIRefMap. if (auto *CTy = dyn_cast(DITy)) { #ifndef NDEBUG @@ -2020,16 +2035,24 @@ void IRGenDebugInfoImpl::finalize() { createImportedModule(MainFile, M, MainFile, 0); // Finalize all replaceable forward declarations. - for (auto &Ty : ReplaceMap) { - llvm::TempMDNode FwdDecl(cast(Ty.second)); - llvm::Metadata *Replacement; - if (auto *FullType = getTypeOrNull(Ty.first)) - Replacement = FullType; - else - Replacement = Ty.second; - DBuilder.replaceTemporary(std::move(FwdDecl), - cast(Replacement)); + auto finalize = [&](llvm::MDNode *FwdDeclType, llvm::MDNode *FullType, + llvm::MDString *UID = nullptr) { + llvm::TempMDNode FwdDecl(cast(FwdDeclType)); + llvm::Metadata *Replacement = FullType ? FullType : FwdDeclType; + llvm::Metadata *Replaced = DBuilder.replaceTemporary( + std::move(FwdDecl), cast(Replacement)); + + // Unique all identical forward declarations. + if (UID && !FullType) + DIRefMap[UID] = llvm::TrackingMDNodeRef(cast(Replaced)); + }; + + for (auto &Ty : FwdDeclTypes) { + auto *UID = llvm::MDString::get(IGM.getLLVMContext(), Ty.first); + finalize(cast(Ty.second), + llvm::cast_or_null(DIRefMap.lookup(UID)), UID); } + // Finalize the DIBuilder. DBuilder.finalize(); } @@ -2393,7 +2416,7 @@ IRGenDebugInfoImpl::emitFunction(const SILDebugScope *DS, llvm::Function *Fn, auto DTI = DebugTypeInfo::getFromTypeInfo( ErrorInfo->getReturnValueType(IGM.getSILModule(), FnTy, IGM.getMaximalTypeExpansionContext()), - IGM.getTypeInfo(SILTy), false); + IGM.getTypeInfo(SILTy), IGM, false); Error = DBuilder.getOrCreateArray({getOrCreateType(DTI)}).get(); } @@ -2541,9 +2564,6 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( if (DbgTy.getType()->hasOpenedExistential()) return; - if (!DbgTy.getTypeSizeInBits()) - DbgTy.setSizeInBits(getStorageSizeInBits(IGM.DataLayout, Storage)); - auto *Scope = dyn_cast_or_null(getOrCreateScope(DS)); assert(Scope && "variable has no local scope"); auto DInstLoc = getStartLocation(DbgInstLoc); @@ -2639,8 +2659,8 @@ void IRGenDebugInfoImpl::emitVariableDeclaration( Operands.push_back(llvm::dwarf::DW_OP_deref); if (IsPiece) { - // Advance the offset and align it for the next piece. - OffsetInBits += llvm::alignTo(SizeInBits, AlignInBits); + // Advance the offset for the next piece. + OffsetInBits += SizeInBits; SizeInBits = IGM.DataLayout.getTypeSizeInBits(Piece->getType()); AlignInBits = IGM.DataLayout.getABITypeAlignment(Piece->getType()); if (!AlignInBits) diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 6c8ba4ad35b3a..af00a89297c13 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1115,9 +1115,9 @@ class IRGenSILFunction : if (VarInfo.ArgNo) { PrologueLocation AutoRestore(IGM.DebugInfo.get(), Builder); - IGM.DebugInfo->emitVariableDeclaration(Builder, Storage, Ty, DS, VarLoc, - VarInfo, Indirection, ArtificialKind::RealValue, - DbgInstrKind); + IGM.DebugInfo->emitVariableDeclaration( + Builder, Storage, Ty, DS, VarLoc, VarInfo, Indirection, + ArtificialKind::RealValue, DbgInstrKind); return; } @@ -5078,10 +5078,10 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) { // Figure out the debug variable type if (VarDecl *Decl = i->getDecl()) { DbgTy = DebugTypeInfo::getLocalVariable(Decl, RealTy, getTypeInfo(SILTy), - IsFragmentType); + IGM, IsFragmentType); } else if (!SILTy.hasArchetype() && !VarInfo->Name.empty()) { // Handle the cases that read from a SIL file - DbgTy = DebugTypeInfo::getFromTypeInfo(RealTy, getTypeInfo(SILTy), + DbgTy = DebugTypeInfo::getFromTypeInfo(RealTy, getTypeInfo(SILTy), IGM, IsFragmentType); } else return; @@ -5453,11 +5453,11 @@ void IRGenSILFunction::emitDebugInfoForAllocStack(AllocStackInst *i, auto RealType = SILTy.getASTType(); DebugTypeInfo DbgTy; if (Decl) { - DbgTy = - DebugTypeInfo::getLocalVariable(Decl, RealType, type, IsFragmentType); + DbgTy = DebugTypeInfo::getLocalVariable(Decl, RealType, type, IGM, + IsFragmentType); } else if (i->getFunction()->isBare() && !SILTy.hasArchetype() && !VarInfo->Name.empty()) { - DbgTy = DebugTypeInfo::getFromTypeInfo(RealType, getTypeInfo(SILTy), + DbgTy = DebugTypeInfo::getFromTypeInfo(RealType, getTypeInfo(SILTy), IGM, IsFragmentType); } else return; @@ -5691,7 +5691,8 @@ void IRGenSILFunction::visitAllocBoxInst(swift::AllocBoxInst *i) { IGM.getMaximalTypeExpansionContext(), i->getBoxType(), IGM.getSILModule().Types, 0); auto RealType = SILTy.getASTType(); - auto DbgTy = DebugTypeInfo::getLocalVariable(Decl, RealType, type, false); + auto DbgTy = + DebugTypeInfo::getLocalVariable(Decl, RealType, type, IGM, false); auto VarInfo = i->getVarInfo(); assert(VarInfo && "debug_value without debug info"); diff --git a/test/DebugInfo/BoundGenericEnum.swift b/test/DebugInfo/BoundGenericEnum.swift index c06685689a6c7..ea475ef354176 100644 --- a/test/DebugInfo/BoundGenericEnum.swift +++ b/test/DebugInfo/BoundGenericEnum.swift @@ -48,7 +48,7 @@ y.g() // Here we have three types, all named $s1a6ResultOyxGD (---> a.Result), // but with different storage sizes: // -// 0. Unsized from the subroutine tupe of map. +// 0. Unsized from the subroutine type of map. // 1. Enum wrapping a pointer-sized object [map() and f()]. // 2. Enum wrapping a 4x64-bit tuple. // @@ -59,16 +59,10 @@ y.g() // CASE_0-DAG: !DISubprogram(name: "map", {{.*}}line: 11, type: ![[SBTY:[0-9]+]] // CASE_0-DAG: ![[SBTY]] = !DISubroutineType(types: ![[SBTYS:[0-9]+]]) // CASE_0-DAG: ![[SBTYS]] = !{!{{[0-9]+}}, !{{[0-9]+}}, ![[SELFTY:[0-9]+]]} -// CASE_0-DAG: ![[SELFTY]] = !DICompositeType(tag: DW_TAG_structure_type, {{.*}}elements: ![[UNSIZED_ELTS:[0-9]+]] -// CASE_0-DAG: ![[UNSIZED_ELTS]] = !{![[UNSIZED_MEM:[0-9]+]]} -// CASE_0-DAG: ![[UNSIZED_MEM]] = !DIDerivedType(tag: DW_TAG_member, {{.*}} baseType: ![[UNIQ:[0-9]+]] +// CASE_0-DAG: ![[SELFTY]] = !DICompositeType(tag: DW_TAG_structure_type, {{.*}}DIFlagFwdDecl // The unique unsized type. -// CASE_0: ![[UNIQ]] = !DICompositeType( -// CASE_0-SAME: tag: DW_TAG_structure_type, name: "Result", -// CASE_0-NOT: size: -// CASE_0-SAME: runtimeLang: DW_LANG_Swift, -// CASE_0-SAME: identifier: "$s1a6ResultOyxGD") +// CASE_0-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "$s1a6ResultOyxGD", {{.*}}DIFlagFwdDecl // (1) // CASE_1-DAG: ![[F:[0-9]+]] = distinct !DISubprogram(name: "f", diff --git a/test/DebugInfo/BoundGenericStruct.swift b/test/DebugInfo/BoundGenericStruct.swift index 3fc57672b5c2c..c64c998e664e6 100644 --- a/test/DebugInfo/BoundGenericStruct.swift +++ b/test/DebugInfo/BoundGenericStruct.swift @@ -5,8 +5,8 @@ public struct S { public let s = S(t: 0) -// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "S", -// CHECK-SAME: templateParams: ![[PARAMS:[0-9]+]], identifier: +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "$s18BoundGenericStruct1SVySiGD", +// CHECK-SAME: templateParams: ![[PARAMS:[0-9]+]] // CHECK: ![[PARAMS]] = !{![[INTPARAM:[0-9]+]]} // CHECK: ![[INTPARAM]] = !DITemplateTypeParameter(type: ![[INT:[0-9]+]]) -// CHECK: ![[INT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Int", +// CHECK: ![[INT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$sSiD", diff --git a/test/DebugInfo/Imports.swift b/test/DebugInfo/Imports.swift index 0beaaf35e429f..ce98458c03040 100644 --- a/test/DebugInfo/Imports.swift +++ b/test/DebugInfo/Imports.swift @@ -8,7 +8,6 @@ // 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: ![[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/ResilientSize.swift b/test/DebugInfo/ResilientSize.swift index 561d29b2f4e5f..bf505b63d2e0c 100644 --- a/test/DebugInfo/ResilientSize.swift +++ b/test/DebugInfo/ResilientSize.swift @@ -1,4 +1,3 @@ - // RUN: %empty-directory(%t) // // Compile the external swift module. @@ -15,4 +14,4 @@ public struct S : OtherResilientProtocol { } // Test that this type has no size (instead of an incorrect size of 0). -// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "S", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, flags: DIFlagFwdDecl, runtimeLang: DW_LANG_Swift, templateParams: !{{[0-9]+}}, identifier: "$s13ResilientSize1SVyxGD") +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "$s13ResilientSize1SVyxGD", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, flags: DIFlagFwdDecl, runtimeLang: DW_LANG_Swift, templateParams: !{{[0-9]+}}) diff --git a/test/DebugInfo/apple-types-accel.swift b/test/DebugInfo/apple-types-accel.swift index 6490fcdbccd97..94a37cce25f67 100644 --- a/test/DebugInfo/apple-types-accel.swift +++ b/test/DebugInfo/apple-types-accel.swift @@ -22,12 +22,13 @@ // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "foo" // CHECK-SAME: identifier: "$s4main3fooCD" class foo { - var x : Int64 = 1 + var x : Int64 = 1 } func main() -> Int64 { - var thefoo = foo(); - return thefoo.x + var y : Int64 = 2 + var thefoo = foo(); + return thefoo.x } main() diff --git a/test/DebugInfo/archetypes2.swift b/test/DebugInfo/archetypes2.swift index 124f157cb0da1..1ee1288b016ac 100644 --- a/test/DebugInfo/archetypes2.swift +++ b/test/DebugInfo/archetypes2.swift @@ -3,16 +3,12 @@ func markUsed(_ t: T) {} class C { - // CHECK: ![[A:.*]] = !DICompositeType(tag: DW_TAG_structure_type,{{.*}}identifier: "$sxD" - // CHECK: ![[LET_A:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, - // CHECK-SAME: baseType: ![[A]]) - // CHECK: ![[B:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type,{{.*}}identifier: "$sqd__D") - // CHECK: !DILocalVariable(name: "x", arg: 1,{{.*}}line: [[@LINE+6]], - // CHECK-SAME: type: ![[LET_A]] - // CHECK: !DILocalVariable(name: "y", arg: 2,{{.*}}line: [[@LINE+4]], - // CHECK-SAME: type: ![[LET_B:[0-9]+]] - // CHECK: ![[LET_B]] = !DIDerivedType(tag: DW_TAG_const_type, - // CHECK-SAME: baseType: ![[B]]) + // CHECK-DAG: !DILocalVariable(name: "x", arg: 1,{{.*}}line: [[@LINE+6]], {{.*}}type: ![[LET_A:[0-9]+]] + // CHECK-DAG: ![[LET_A]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[A:[0-9]+]]) + // CHECK-DAG: ![[A]] = !DICompositeType(tag: DW_TAG_structure_type,{{.*}}name: "$sxD" + // CHECK: !DILocalVariable(name: "y", arg: 2,{{.*}}line: [[@LINE+3]],{{.*}}type: ![[LET_B:[0-9]+]] + // CHECK-DAG: ![[LET_B]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[B:[0-9]+]]) + // CHECK-DAG: ![[B]] = !DICompositeType(tag: DW_TAG_structure_type,{{.*}}name: "$sqd__D" func foo(_ x: A, y :B) { markUsed("hello world") } diff --git a/test/DebugInfo/async-boxed-arg.swift b/test/DebugInfo/async-boxed-arg.swift index 4991645c2a3c7..8df4fa4074fe4 100644 --- a/test/DebugInfo/async-boxed-arg.swift +++ b/test/DebugInfo/async-boxed-arg.swift @@ -1,5 +1,5 @@ // RUN: %target-swift-frontend %s -emit-ir -g -o - -parse-as-library \ -// RUN: -module-name M -disable-availability-checking | %FileCheck %s --dump-input always +// RUN: -module-name M -disable-availability-checking | %FileCheck %s // REQUIRES: concurrency @available(SwiftStdlib 5.1, *) diff --git a/test/DebugInfo/bound-namealiastype.swift b/test/DebugInfo/bound-namealiastype.swift index 37bdb22e4edf8..a4f92462e7a19 100644 --- a/test/DebugInfo/bound-namealiastype.swift +++ b/test/DebugInfo/bound-namealiastype.swift @@ -14,5 +14,5 @@ func dispatch_queue_create() -> dispatch_queue_t! { // CHECK: ![[TY_ELTS]] = !{![[TY_MEMBER:[0-9]+]]} // CHECK: ![[TY_MEMBER]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[TY:[0-9]+]] // CHECK: ![[TY]] = !DICompositeType( -// CHECK-SAME: identifier: "$s4main16dispatch_queue_taSgD" +// CHECK-SAME: name: "$s4main16dispatch_queue_taSgD" public var queue = dispatch_queue_create() diff --git a/test/DebugInfo/enum.swift b/test/DebugInfo/enum.swift index c4de4a290e92c..71c0d13b27f3a 100644 --- a/test/DebugInfo/enum.swift +++ b/test/DebugInfo/enum.swift @@ -21,7 +21,7 @@ enum Color : UInt64 { // This is effectively a 2-bit bitfield: // DWARF: !DIDerivedType(tag: DW_TAG_member, name: "Red" // DWARF-SAME: baseType: ![[UINT64:[0-9]+]] -// DWARF-SAME: size: 8{{[,)]}} +// DWARF-SAME: size: 64{{[,)]}} // DWARF: ![[UINT64]] = !DICompositeType({{.*}}identifier: "$ss6UInt64VD" case Red, Green, Blue } @@ -47,42 +47,38 @@ enum Maybe { let r = Color.Red let c = MaybeIntPair.just(74, 75) -// CHECK: !DICompositeType({{.*}}name: "Maybe", -// CHECK-SAME: identifier: "$s4enum5MaybeOyAA5ColorOGD" +// CHECK: !DICompositeType({{.*}}name: "$s4enum5MaybeOyAA5ColorOGD" let movie : Maybe = .none public enum Nothing { } public func foo(_ empty : Nothing) { } // CHECK: !DICompositeType({{.*}}name: "Nothing", {{.*}}elements: ![[EMPTY]] -// CHECK: !DICompositeType({{.*}}name: "Rose", -// CHECK-SAME: {{.*}}identifier: "$s4enum4RoseOyxG{{z?}}D") +// CHECK: !DICompositeType({{.*}}name: "$s4enum4RoseOyxG{{z?}}D" enum Rose { case MkRose(() -> A, () -> [Rose]) - // DWARF: !DICompositeType({{.*}}name: "Rose",{{.*}}flags: DIFlagFwdDecl{{.*}}identifier: "$s4enum4RoseOyxGD") + // DWARF: !DICompositeType({{.*}}name: "$s4enum4RoseOyxGD",{{.*}}flags: DIFlagFwdDecl{{.*}} case IORose(() -> Rose) } func foo(_ x : Rose) -> Rose { return x } -// CHECK: !DICompositeType({{.*}}name: "Tuple", {{.*}}identifier: "$s4enum5TupleOyxGD") -// DWARF: !DICompositeType({{.*}}name: "Tuple", -// DWARF-SAME: {{.*}}identifier: "$s4enum5TupleOyxG{{z?}}D") +// CHECK: !DICompositeType({{.*}}name: "$s4enum5TupleOyxGD" +// DWARF: !DICompositeType({{.*}}name: "$s4enum5TupleOyxG{{z?}}D" public enum Tuple

{ case C(P, () -> Tuple) } func bar(_ x : Tuple) -> Tuple { return x } -// CHECK-DAG: ![[LIST:.*]] = !DICompositeType({{.*}}identifier: "$s4enum4ListOyxGD" -// CHECK-DAG: ![[LIST_MEMBER:.*]] = !DIDerivedType(tag: DW_TAG_member, {{.*}} baseType: ![[LIST]] -// CHECK-DAG: ![[LIST_ELTS:.*]] = !{![[LIST_MEMBER]]} -// CHECK-DAG: ![[LIST_CONTAINER:.*]] = !DICompositeType({{.*}}elements: ![[LIST_ELTS]] - -// CHECK-DAG: ![[LET_LIST:.*]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[LIST_CONTAINER]]) -// CHECK-DAG: !DILocalVariable(name: "self", arg: 1, {{.*}} line: [[@LINE+4]], type: ![[LET_LIST]], flags: DIFlagArtificial) public enum List { indirect case Tail(List, T) case End func fooMyList() {} +// CHECK-DAG: !DILocalVariable(name: "self", arg: 1, {{.*}} line: [[@LINE-1]], type: ![[LET_LIST:[0-9]+]], flags: DIFlagArtificial) +// CHECK-DAG: ![[LET_LIST]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[LIST_CONTAINER:[0-9]+]]) +// CHECK-DAG: ![[LIST_CONTAINER]] = !DICompositeType({{.*}}elements: ![[LIST_ELTS:[0-9]+]] +// CHECK-DAG: ![[LIST_ELTS]] = !{![[LIST_MEMBER:[0-9]+]]} +// CHECK-DAG: ![[LIST_MEMBER]] = !DIDerivedType(tag: DW_TAG_member, {{.*}} baseType: ![[LIST:[0-9]+]] +// CHECK-DAG: ![[LIST]] = !DICompositeType({{.*}}name: "$s4enum4ListOyxGD",{{.*}}DIFlagFwdDecl } diff --git a/test/DebugInfo/fnptr.swift b/test/DebugInfo/fnptr.swift index 109b4f85c7d8d..6501ad53afcd5 100644 --- a/test/DebugInfo/fnptr.swift +++ b/test/DebugInfo/fnptr.swift @@ -1,9 +1,6 @@ // RUN: %target-swift-frontend %s -emit-ir -gdwarf-types -o - | %FileCheck %s -allow-deprecated-dag-overlap // RUN: %target-swift-frontend %s -emit-ir -g -o - | %FileCheck %s --check-prefix=AST -// CHECK-DAG: ![[SINODE:.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Int64",{{.*}} identifier: [[SI:.*]]) -// CHECK-DAG: ![[SFNODE:.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Float",{{.*}} identifier: [[SF:.*]]) -// CHECK-DAG: ![[VOIDNODE:.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$sytD",{{.*}} identifier: [[VOID:.*]]) func bar() {} func baz(_ i: Float) -> Int64 { return 0; } func barz(_ i: Float, _ j: Float) -> Int64 { return 0; } @@ -18,7 +15,7 @@ func main() -> Int64 { // CHECK-DAG: ![[BARPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type,{{.*}} baseType: ![[BART:[0-9]+]] // CHECK-DAG: ![[BART]] = !DISubroutineType(types: ![[BARARGS:[0-9]+]]) // CHECK-DAG: ![[BARARGS]] = !{![[VOID:.*]]} - // CHECK-DAG: ![[VOID]] = {{.*}}identifier: "$sytD" + // CHECK-DAG: ![[VOID]] = {{.*}}name: "$sytD" bar_fnptr(); // CHECK-DAG: !DILocalVariable(name: "baz_fnptr",{{.*}} type: ![[BAZPT:[0-9]+]] @@ -30,7 +27,7 @@ func main() -> Int64 { // CHECK-DAG: ![[BAZPTR]] = !DIDerivedType(tag: DW_TAG_pointer_type,{{.*}} baseType: ![[BAZT:[0-9]+]] // CHECK-DAG: ![[BAZT]] = !DISubroutineType(types: ![[BAZARGS:.*]]) // CHECK-DAG: ![[BAZARGS]] = !{![[INT:.*]], ![[FLOAT:.*]]} - // CHECK-DAG: ![[INT]] = {{.*}}identifier: "$ss5Int64VD" + // CHECK-DAG: ![[INT]] = {{.*}}name: "$ss5Int64VD" // CHECK-DAG: ![[FLOAT]] = {{.*}}identifier: "$sSfD" var baz_fnptr = baz baz_fnptr(2.89) diff --git a/test/DebugInfo/generic_arg.swift b/test/DebugInfo/generic_arg.swift index c2e7ffeb06e36..3f7dec6c730e9 100644 --- a/test/DebugInfo/generic_arg.swift +++ b/test/DebugInfo/generic_arg.swift @@ -10,13 +10,10 @@ func foo(_ x: T) -> () { // CHECK-SAME: metadata ![[X1:.*]], metadata !DIExpression(DW_OP_deref)) // CHECK: store %swift.type* %T, %swift.type** %[[T]], // CHECK: store %swift.opaque* %0, %swift.opaque** %[[X]], - // CHECK: ![[TY2:[0-9]+]] = !DICompositeType({{.*}}identifier: "$sxD") - // CHECK: ![[T1]] = !DILocalVariable(name: "$\CF\84_0_0", - // CHECK-SAME: flags: DIFlagArtificial) - // CHECK: ![[X1]] = !DILocalVariable(name: "x", arg: 1, - // CHECK-SAME: line: 3, type: ![[LET_TY2:[0-9]+]]) - // CHECK: ![[LET_TY2]] = !DIDerivedType(tag: DW_TAG_const_type, - // CHECK-SAME: baseType: ![[TY2]]) + // CHECK-DAG: ![[T1]] = !DILocalVariable(name: "$\CF\84_0_0",{{.*}}flags: DIFlagArtificial) + // CHECK-DAG: ![[X1]] = !DILocalVariable(name: "x", arg: 1,{{.*}}line: 3, type: ![[LET_TY2:[0-9]+]]) + // CHECK-DAG: ![[LET_TY2]] = !DIDerivedType(tag: DW_TAG_const_type,{{.*}}baseType: ![[TY2:[0-9]+]]) + // CHECK-DAG: ![[TY2]] = !DICompositeType({{.*}}name: "$sxD" _blackHole(x) } diff --git a/test/DebugInfo/generic_arg3.swift b/test/DebugInfo/generic_arg3.swift index 4e7a90cc16202..2ef63ae4b8c3f 100644 --- a/test/DebugInfo/generic_arg3.swift +++ b/test/DebugInfo/generic_arg3.swift @@ -9,9 +9,8 @@ public func f(_ value : Type) // CHECK-SAME: metadata ![[ARG:.*]], metadata !DIExpression(DW_OP_deref)) // CHECK: store %swift.opaque* %1, %swift.opaque** %[[ALLOCA]], align // No deref here. - // CHECK: ![[TY:.*]] = !DICompositeType({{.*}}identifier: "$sxD" - // CHECK: ![[LET_TY:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, - // CHECK-SAME: baseType: ![[TY]]) + // CHECK-DAG: ![[TY:.*]] = !DICompositeType({{.*}}name: "$sxD", file + // CHECK-DAG: ![[LET_TY:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type,{{.*}}baseType: ![[TY]]) // CHECK: ![[ARG]] = !DILocalVariable(name: "arg", arg: 1, // CHECK-SAME: line: [[@LINE+1]], type: ![[LET_TY]]) apply(value) { arg in return arg } diff --git a/test/DebugInfo/generic_arg4.swift b/test/DebugInfo/generic_arg4.swift index 21180316c1827..bae202a8dd020 100644 --- a/test/DebugInfo/generic_arg4.swift +++ b/test/DebugInfo/generic_arg4.swift @@ -9,7 +9,7 @@ public struct Q { // CHECK-SAME: metadata ![[ARG:.*]], metadata !DIExpression()) // CHECK: store %[[TY]]* %0, %[[TY]]** %[[ALLOCA]], align // No deref here: the array argument is passed by value. -// CHECK: ![[DITY:.*]] = !DICompositeType({{.*}}identifier: "$sSay12generic_arg41QVyxGGD") +// CHECK: ![[DITY:.*]] = !DICompositeType({{.*}}name: "$sSay12generic_arg41QVyxGGD" public func foo(_ arg: [Q]) { // CHECK: ![[ARG]] = !DILocalVariable(name: "arg", arg: 1, // CHECK-SAME: line: [[@LINE-2]], type: ![[DITY:.*]]) diff --git a/test/DebugInfo/generic_arg5.swift b/test/DebugInfo/generic_arg5.swift index f1585921a00a4..86e2d058078eb 100644 --- a/test/DebugInfo/generic_arg5.swift +++ b/test/DebugInfo/generic_arg5.swift @@ -12,16 +12,17 @@ public func foo(_ values : [S]) // CHECK-SAME: metadata ![[ARG:[0-9]+]], // CHECK-SAME: metadata !DIExpression(DW_OP_deref)) // CHECK: store %[[TY]]* %1, %[[TY]]** %[[ALLOCA]], align - // CHECK: ![[TYP:[0-9]+]] = !DICompositeType({{.*}}, identifier: "$s12generic_arg51SVyxGD") + // CHECK: ![[TYP:[0-9]+]] = !DICompositeType({{.*}}, name: "$s12generic_arg51SVyxGD" // The argument is a by-ref struct and thus needs to be dereferenced. // CHECK: ![[ARG]] = !DILocalVariable(name: "arg", arg: 1, - // CHECK-SAME: line: [[@LINE+7]], + // CHECK-SAME: line: [[@LINE+8]], // CHECK-SAME: type: ![[LET_TYP:[0-9]+]]) // CHECK: ![[LET_TYP]] = !DIDerivedType(tag: DW_TAG_const_type, // CHECK-SAME: baseType: ![[TYP_CONTAINER:[0-9]+]]) // CHECK: ![[TYP_CONTAINER]] = !DICompositeType({{.*}}elements: ![[TYP_ELTS:[0-9]+]] // CHECK: ![[TYP_ELTS]] = !{![[TYP_MEMBER:[0-9]+]]} - // CHECK: ![[TYP_MEMBER]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[TYP]] + // CHECK: ![[TYP_MEMBER]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[TYP_:[0-9]+]] + // CHECK: ![[TYP_]] = !DICompositeType({{.*}}, name: "$s12generic_arg51SVyxGD" let _ = values.flatMap { arg in return .some(arg) } diff --git a/test/DebugInfo/generic_args.swift b/test/DebugInfo/generic_args.swift index 293e20b93a30f..6a493e4f254e3 100644 --- a/test/DebugInfo/generic_args.swift +++ b/test/DebugInfo/generic_args.swift @@ -15,7 +15,7 @@ class AnotherClass : AProtocol { // CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "$sq_D", // CHECK-DAG: !DILocalVariable(name: "x", arg: 1,{{.*}} type: ![[LET_T:.*]]) // CHECK-DAG: ![[LET_T]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[T:.*]]) -// CHECK-DAG: ![[T]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$sxD" +// CHECK-DAG: ![[T]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$sxD", // CHECK-DAG: !DILocalVariable(name: "y", arg: 2,{{.*}} type: ![[LET_Q:.*]]) // CHECK-DAG: ![[LET_Q]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[Q:.*]]) // CHECK-DAG: ![[Q]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$sq_D" @@ -28,15 +28,16 @@ aFunction(AClass(),AnotherClass(),"aFunction") struct Wrapper { init(from : Wrapper) { - // CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "Wrapper",{{.*}} identifier: "$s12generic_args7WrapperVyqd__GD") + // CHECK-DAG: !DICompositeType(tag: DW_TAG_structure_type, name: "$s12generic_args7WrapperVyqd__GD" var wrapped = from wrapped = from _ = wrapped } func passthrough(_ t: T) -> T { - // CHECK-DAG: ![[WRAPPER:.*]] = !DICompositeType({{.*}}identifier: "$s12generic_args7WrapperVyxGD") - // CHECK-DAG: !DILocalVariable(name: "local",{{.*}} line: [[@LINE+1]],{{.*}} type: ![[T]] + // CHECK-DAG: ![[WRAPPER:.*]] = !DICompositeType({{.*}}name: "$s12generic_args7WrapperVyxGD" + // CHECK-DAG: !DILocalVariable(name: "local",{{.*}} line: [[@LINE+2]],{{.*}} type: ![[T1:[0-9]+]] + // CHECK-DAG: ![[T1]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$sxD", var local = t local = t return local diff --git a/test/DebugInfo/generic_enum.swift b/test/DebugInfo/generic_enum.swift index e50117ccefa50..ed425b8b53699 100644 --- a/test/DebugInfo/generic_enum.swift +++ b/test/DebugInfo/generic_enum.swift @@ -16,12 +16,12 @@ func unwrapTrivialGeneric(_ tg: TrivialGeneric) -> (T, U) { func wrapTrivialGeneric(_ t: T, u: U) -> TrivialGeneric { return .x(t, u) } -// CHECK-DAG: ![[T1:.*]] = !DICompositeType({{.*}}identifier: "$s12generic_enum14TrivialGenericOys5Int64VSSGD" +// CHECK-DAG: ![[T1:.*]] = !DICompositeType({{.*}}name: "$s12generic_enum14TrivialGenericOys5Int64VSSGD" // CHECK-DAG: ![[T1_MEMBER:.*]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[T1]] // CHECK-DAG: ![[T1_ELTS:.*]] = !{![[T1_MEMBER]]} // CHECK-DAG: ![[T1_CONTAINER:.*]] = !DICompositeType({{.*}}elements: ![[T1_ELTS]] // CHECK-DAG: !DIGlobalVariable(name: "tg",{{.*}} line: [[@LINE+2]],{{.*}} type: ![[T1_CONTAINER]],{{.*}} isLocal: false, isDefinition: true -// CHECK-DAG: !DICompositeType({{.*}}, name: "TrivialGeneric", {{.*}}identifier: "$s12generic_enum14TrivialGenericOys5Int64VSSGD" +// CHECK-DAG: !DICompositeType({{.*}}, name: "$s12generic_enum14TrivialGenericOys5Int64VSSGD" var tg : TrivialGeneric = .x(23, "skidoo") switch tg { case .x(var t, var u): diff --git a/test/DebugInfo/generic_enum_closure.swift b/test/DebugInfo/generic_enum_closure.swift index 0e8063b942e78..4e5478f73b76a 100644 --- a/test/DebugInfo/generic_enum_closure.swift +++ b/test/DebugInfo/generic_enum_closure.swift @@ -19,7 +19,7 @@ struct CErrorOr // CHECK-DAG: ![[TY_CONTAINER]] = !DICompositeType({{.*}}elements: ![[TY_ELTS:[0-9]+]] // CHECK-DAG: ![[TY_ELTS]] = !{![[TY_MEMBER:[0-9]+]]} // CHECK-DAG: ![[TY_MEMBER]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[TY:[0-9]+]] - // CHECK-DAG: ![[TY]] = !DICompositeType({{.*}}, identifier: "$s20generic_enum_closure8CErrorOrVyxGD") + // CHECK-DAG: ![[TY]] = !DICompositeType({{.*}}, name: "$s20generic_enum_closure8CErrorOrVyxGD" value = .none } } diff --git a/test/DebugInfo/inlined-generics-basic.swift b/test/DebugInfo/inlined-generics-basic.swift index caaa4b055c03b..4049fc4447f39 100644 --- a/test/DebugInfo/inlined-generics-basic.swift +++ b/test/DebugInfo/inlined-generics-basic.swift @@ -95,13 +95,13 @@ public class C { // IR: ![[LET_BOOL:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[BOOL]]) // IR: ![[INT:[0-9]+]] = !DICompositeType({{.*}}name: "Int" // IR: ![[LET_INT:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[INT]]) -// IR: ![[TAU_0_0:[0-9]+]] = {{.*}}DW_TAG_structure_type, name: "$sxD", -// IR: ![[LET_TAU_0_0:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[TAU_0_0]]) -// IR: ![[TAU_1_0:[0-9]+]] = {{.*}}DW_TAG_structure_type, name: "$sqd__D", -// IR: ![[MD_1_0]] = !DILocalVariable(name: "$\CF\84_1_0" -// IR: ![[S]] = !DILocalVariable(name: "s", {{.*}} type: ![[LET_TAU_1_0:[0-9]+]] -// IR: ![[LET_TAU_1_0]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[TAU_1_0]]) -// IR: ![[GS_T]] = !DILocalVariable(name: "t", {{.*}} scope: ![[SP_GS_T:[0-9]+]], {{.*}} type: ![[LET_TAU_1_0]]) +// IR-DAG: ![[TAU_0_0:[0-9]+]] = {{.*}}DW_TAG_structure_type, name: "$sxD", file +// IR-DAG: ![[LET_TAU_0_0:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[TAU_0_0]]) +// IR-DAG: ![[TAU_1_0:[0-9]+]] = {{.*}}DW_TAG_structure_type, name: "$sqd__D", file +// IR-DAG: ![[MD_1_0]] = !DILocalVariable(name: "$\CF\84_1_0" +// IR-DAG: ![[S]] = !DILocalVariable(name: "s", {{.*}} type: ![[LET_TAU_1_0:[0-9]+]] +// IR-DAG: ![[LET_TAU_1_0]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[TAU_1_0]]) +// IR-DAG: ![[GS_T]] = !DILocalVariable(name: "t", {{.*}} scope: ![[SP_GS_T:[0-9]+]], {{.*}} type: ![[LET_TAU_1_0]]) // IR: ![[SP_GS_T]] = {{.*}}linkageName: "$s1A1gyyxlFqd___Ti5" // IR: ![[GS_U]] = !DILocalVariable(name: "u", {{.*}} scope: ![[SP_GS_U:[0-9]+]], {{.*}} type: ![[LET_TAU_1_0]]) // IR: ![[SP_GS_U]] = {{.*}}linkageName: "$s1A1hyyxlFqd___Ti5" diff --git a/test/DebugInfo/mangling.swift b/test/DebugInfo/mangling.swift index 859bec5506f1d..38eabb0f6bbd4 100644 --- a/test/DebugInfo/mangling.swift +++ b/test/DebugInfo/mangling.swift @@ -14,7 +14,7 @@ func markUsed(_ t: T) {} // CHECK: ![[DT_CONTAINER]] = !DICompositeType({{.*}}elements: ![[DT_ELTS:[0-9]+]] // CHECK: ![[DT_ELTS]] = !{![[DT_MEMBER:[0-9]+]]} // CHECK: ![[DT_MEMBER]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[DT:[0-9]+]] -// CHECK: ![[DT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Dictionary" +// CHECK: ![[DT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$sSDys5Int64VSSGD" var myDict = Dictionary() myDict[12] = "Hello!" diff --git a/test/DebugInfo/protocol-sugar.swift b/test/DebugInfo/protocol-sugar.swift index 5b602eec058de..1f491404714e1 100644 --- a/test/DebugInfo/protocol-sugar.swift +++ b/test/DebugInfo/protocol-sugar.swift @@ -8,4 +8,4 @@ var p: (C & D)? // CHECK-DAG: ![[TY_CONTAINER]] = !DICompositeType({{.*}}elements: ![[TY_ELTS:[0-9]+]] // CHECK-DAG: ![[TY_ELTS]] = !{![[TY_MEMBER:[0-9]+]]} // CHECK-DAG: ![[TY_MEMBER]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[TY:[0-9]+]] -// CHECK-DAG: ![[TY]] = {{.*}}identifier: "$s4main1A_AA1BAA1DpXSpSgD" +// CHECK-DAG: ![[TY]] = {{.*}}name: "$s4main1A_AA1BAA1DpXSpSgD" diff --git a/test/DebugInfo/struct_resilience.swift b/test/DebugInfo/struct_resilience.swift index c0cef08662558..237d962a21883 100644 --- a/test/DebugInfo/struct_resilience.swift +++ b/test/DebugInfo/struct_resilience.swift @@ -30,7 +30,7 @@ func f() { } f() -// CHECK: ![[TY:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Size", +// CHECK: ![[TY:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$s16resilient_struct4SizeVD", // CHECK: ![[LET_TY:[0-9]+]] = !DIDerivedType(tag: DW_TAG_const_type, // CHECK-SAME: baseType: ![[TY:[0-9]+]]) // CHECK: ![[V1]] = !DILocalVariable(name: "s1", {{.*}}type: ![[LET_TY]]) diff --git a/test/DebugInfo/structs.swift b/test/DebugInfo/structs.swift index 31b9ad142038a..f280172c856b1 100644 --- a/test/DebugInfo/structs.swift +++ b/test/DebugInfo/structs.swift @@ -13,6 +13,10 @@ func test(_ x : A) { // CHECK: define hidden {{.*}}void @"$s7structs4test{{[_0-9a-zA-Z]*}}F" // CHECK: [[X_DBG:%.*]] = alloca // CHECK: call void @llvm.dbg.declare(metadata {{.*}}* [[X_DBG]], metadata [[X_MD:!.*]], metadata + +// A class is represented by a pointer, so B's total size should be PTRSIZE. +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "B",{{.*}}size: [[PTRSIZE]] + // CHECK: ![[A:.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "A", // CHECK-SAME: identifier @@ -25,8 +29,8 @@ class C { // CHECK-SAME: type: ![[LET_A:[0-9]+]] // CHECK: ![[LET_A]] = !DIDerivedType(tag: DW_TAG_const_type, baseType: ![[A]]) -// A class is represented by a pointer, so B's total size should be PTRSIZE. -// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "B",{{.*}}size: [[PTRSIZE]] struct B { var c : C } + +let b = B(c: C()) diff --git a/test/DebugInfo/variables.swift b/test/DebugInfo/variables.swift index 502c91e04f18a..fe23ee2970d4a 100644 --- a/test/DebugInfo/variables.swift +++ b/test/DebugInfo/variables.swift @@ -71,7 +71,7 @@ func myprint(_ p: (i: Int, b: Bool)) { myprint(tuple) // Arrays are represented as an instantiation of Array. -// CHECK-DAG: ![[ARRAYTY:.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Array", +// CHECK-DAG: ![[ARRAYTY:.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$sSaySi1a_Si1btGD", // CHECK-DAG: ![[ARRAY_MEMBER:.*]] = !DIDerivedType(tag: DW_TAG_member, {{.*}}baseType: ![[ARRAYTY]] // CHECK-DAG: ![[ARRAY_ELTS:.*]] = !{![[ARRAY_MEMBER]]} // CHECK-DAG: ![[ARRAY_CONTAINER:.*]] = !DICompositeType({{.*}}elements: ![[ARRAY_ELTS]] diff --git a/test/DebugInfo/vector.swift b/test/DebugInfo/vector.swift index f50164ce1f54f..3901aec65fd38 100644 --- a/test/DebugInfo/vector.swift +++ b/test/DebugInfo/vector.swift @@ -8,13 +8,7 @@ import Swift -public struct float2 { - public var _vector: Builtin.Vec2xFPIEEE32 - public subscript(index: Int) -> Float { - get { - let elt = Builtin.extractelement_Vec2xFPIEEE32_Int32(_vector, - Int32(index)._value) - return Float(elt) - } - } +public func float2(_ _vector: Builtin.Vec2xFPIEEE32, _ index: UInt) { + let elt = Builtin.extractelement_Vec2xFPIEEE32_Int32(_vector, + Int32(index)._value) } From 2ebff4d29e766c07e0281ec40b10982cafd61cb3 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 8 Feb 2023 15:16:40 -0800 Subject: [PATCH 50/98] [GenTuple] Extracted dynamic tuple offset load. Refactored the preexisting function loadTupleOffsetFromMetadata--which took an unsigned index and constructed an llvm::Value from it--to instead take a llvm::Value. Preserved the old overload to just pass through to the refactored function. --- lib/IRGen/GenTuple.cpp | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/IRGen/GenTuple.cpp b/lib/IRGen/GenTuple.cpp index 69cc23d1f156b..378377dac4cb3 100644 --- a/lib/IRGen/GenTuple.cpp +++ b/lib/IRGen/GenTuple.cpp @@ -73,22 +73,37 @@ namespace { /// Project a tuple offset from a tuple metadata structure. static llvm::Value *loadTupleOffsetFromMetadata(IRGenFunction &IGF, llvm::Value *metadata, - unsigned index) { + llvm::Value *index) { auto asTuple = IGF.Builder.CreateBitCast(metadata, IGF.IGM.TupleTypeMetadataPtrTy); llvm::Value *indices[] = { - IGF.IGM.getSize(Size(0)), // (*tupleType) - llvm::ConstantInt::get(IGF.IGM.Int32Ty, 3), // .Elements - IGF.IGM.getSize(Size(index)), // [index] - llvm::ConstantInt::get(IGF.IGM.Int32Ty, 1) // .Offset + IGF.IGM.getSize(Size(0)), // (*tupleType) + llvm::ConstantInt::get(IGF.IGM.Int32Ty, 3), // .Elements + index, // [index] + llvm::ConstantInt::get(IGF.IGM.Int32Ty, 1) // .Offset }; auto slot = IGF.Builder.CreateInBoundsGEP(IGF.IGM.TupleTypeMetadataTy, asTuple, indices); - return IGF.Builder.CreateLoad( - slot, IGF.IGM.Int32Ty, IGF.IGM.getPointerAlignment(), - metadata->getName() + "." + Twine(index) + ".offset"); + Twine name = [&]() -> Twine { + if (auto *constantIndex = dyn_cast(index)) { + return metadata->getName() + "." + + Twine(constantIndex->getValue().getLimitedValue()) + ".offset"; + } else { + return metadata->getName() + ".dynamic.offset"; + } + }(); + + return IGF.Builder.CreateLoad(slot, IGF.IGM.Int32Ty, + IGF.IGM.getPointerAlignment(), name); + } + + static llvm::Value *loadTupleOffsetFromMetadata(IRGenFunction &IGF, + llvm::Value *metadata, + unsigned index) { + return loadTupleOffsetFromMetadata(IGF, metadata, + IGF.IGM.getSize(Size(index))); } /// Adapter for tuple types. From 74de34c1c2fd112e609076603e689a45b106e814 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 8 Feb 2023 14:21:57 -0500 Subject: [PATCH 51/98] Round the initial context size of tasks up to 32 on 64-bit <=5.6 runtimes Fixes issue 63420 --- .../Compatibility56/Concurrency/Task.cpp | 56 +++++++++++++++++++ .../toolchain/Compatibility56/Overrides.cpp | 3 + stdlib/toolchain/Compatibility56/Overrides.h | 20 +++++++ 3 files changed, 79 insertions(+) diff --git a/stdlib/toolchain/Compatibility56/Concurrency/Task.cpp b/stdlib/toolchain/Compatibility56/Concurrency/Task.cpp index 714092987bdd5..d7d73c1dcc691 100644 --- a/stdlib/toolchain/Compatibility56/Concurrency/Task.cpp +++ b/stdlib/toolchain/Compatibility56/Concurrency/Task.cpp @@ -244,3 +244,59 @@ void SWIFT_CC(swiftasync) swift::swift56override_swift_task_future_wait_throwing } } } + +//===--- swift_task_create_common -----------------------------------------===// + +// NOTE: this function is currently only installed as an override on +// 64-bit targets. The fix in it has been written to work correctly +// on any target, though, so if you need to use it for a more general +// fix, you should be able to just define and install it unconditionally. +#if __POINTER_WIDTH__ == 64 + +AsyncTaskAndContext SWIFT_CC(swift) +swift::swift56override_swift_task_create_common( + size_t rawTaskCreateFlags, + TaskOptionRecord *options, + const Metadata *futureResultType, + TaskContinuationFunction *function, void *closureContext, + size_t initialContextSize, + TaskCreateCommon_t *original) { + + // The <=5.6 versions of this function pointlessly initialize the + // defunct Flags field in the initial context. This initialization + // is mostly harmless because the initial function has no expectations + // about the non-header contents of the initial context on entry. + // However, if the initial context doesn't include space for the Flags + // field, and it ends up at the end of an allocation, this write can + // go past the end of the allocation. + // + // The initial context is always at the end of the allocation for + // Tasks that lack a preallocated buffer, i.e. any Task that is not + // an async let. + // + // On 32-bit targets, the Flags field was at offset 8. Since context + // sizes are always rounded up to a multiple of MaximumAlignment, + // initialContextSize is guaranteed to be >= 16, so the store to + // Flags will always fall within it. On 64-bit targets, however, + // Flags was at offset 16. We therefore need to ensure the initial + // context is large enough for the unnecessary write to Flags. + // + // We could handle this in the compiler by ensuring that all + // functions request at least 32 bytes of context, but that would + // introduce a permanent overhead on thunks and other functions that + // don't need any temporary scratch space. We really only need to work + // around this one store when creating tasks, and fortunately, that + // always flows through this one function. Since this hook receives + // the initial function and context size directly instead of as an + // async function pointer, it's painless for us to just change the + // requested initial context size. +#if __POINTER_WIDTH__ == 64 + if (initialContextSize < 32) initialContextSize = 32; +#endif + + return original(rawTaskCreateFlags, options, + futureResultType, function, closureContext, + initialContextSize); +} + +#endif diff --git a/stdlib/toolchain/Compatibility56/Overrides.cpp b/stdlib/toolchain/Compatibility56/Overrides.cpp index 4341e08bd0f43..f416dc46f1969 100644 --- a/stdlib/toolchain/Compatibility56/Overrides.cpp +++ b/stdlib/toolchain/Compatibility56/Overrides.cpp @@ -43,6 +43,9 @@ __attribute__((visibility("hidden"))) ConcurrencyOverrideSection Swift56ConcurrencyOverrides __attribute__((used, section("__DATA,__s_async_hook"))) = { .version = 0, +#if __POINTER_WIDTH__ == 64 + .task_create_common = swift56override_swift_task_create_common, +#endif .task_future_wait = swift56override_swift_task_future_wait, .task_future_wait_throwing = swift56override_swift_task_future_wait_throwing, }; diff --git a/stdlib/toolchain/Compatibility56/Overrides.h b/stdlib/toolchain/Compatibility56/Overrides.h index 38260a4f2eb91..1cbef1e83b960 100644 --- a/stdlib/toolchain/Compatibility56/Overrides.h +++ b/stdlib/toolchain/Compatibility56/Overrides.h @@ -23,6 +23,13 @@ struct OpaqueValue; class AsyncContext; class AsyncTask; +using TaskCreateCommon_t = SWIFT_CC(swift) AsyncTaskAndContext( + size_t rawTaskCreateFlags, + TaskOptionRecord *options, + const Metadata *futureResultType, + TaskContinuationFunction *function, void *closureContext, + size_t initialContextSize); + using TaskFutureWait_t = SWIFT_CC(swiftasync) void( OpaqueValue *result, SWIFT_ASYNC_CONTEXT AsyncContext *callerContext, @@ -54,4 +61,17 @@ void SWIFT_CC(swiftasync) swift56override_swift_task_future_wait_throwing( ThrowingTaskFutureWaitContinuationFunction *, AsyncContext *, TaskFutureWaitThrowing_t *original); + +#if __POINTER_WIDTH__ == 64 +__attribute__((weak, visibility("hidden"))) +AsyncTaskAndContext SWIFT_CC(swift) +swift56override_swift_task_create_common( + size_t rawTaskCreateFlags, + TaskOptionRecord *options, + const Metadata *futureResultType, + TaskContinuationFunction *function, void *closureContext, + size_t initialContextSize, + TaskCreateCommon_t *original); +#endif + } // namespace swift From 956e81c14e1dccfa01581de7d6cd25cf31b4847f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 8 Feb 2023 15:36:19 -0800 Subject: [PATCH 52/98] Add API to get the "outermost" parent source file. --- include/swift/AST/DeclContext.h | 6 ++++++ lib/AST/DeclContext.cpp | 12 ++++++++++++ lib/IRGen/IRGenModule.cpp | 3 ++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index c31daf43d9dbd..7e6085b0fb312 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -519,6 +519,12 @@ class alignas(1 << DeclContextAlignInBits) DeclContext LLVM_READONLY SourceFile *getParentSourceFile() const; + /// Returns the "outermost" source file that contains this context, + /// looking through any source files for generated code, such as + /// macro expansions. + LLVM_READONLY + SourceFile *getOutermostParentSourceFile() const; + /// Determine whether this declaration context is generic, meaning that it or /// any of its parents have generic parameters. bool isGenericContext() const; diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index fa839b1b8abab..15aabe274a27a 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -323,6 +323,18 @@ SourceFile *DeclContext::getParentSourceFile() const { return fallbackSF; } +SourceFile *DeclContext::getOutermostParentSourceFile() const { + auto sf = getParentSourceFile(); + if (!sf) + return nullptr; + + // Find the originating source file. + while (auto enclosingSF = sf->getEnclosingSourceFile()) + sf = enclosingSF; + + return sf; +} + DeclContext *DeclContext::getModuleScopeContext() const { auto DC = const_cast(this); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index a89c22e44c50c..efc6778c51d43 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -1919,10 +1919,11 @@ IRGenModule *IRGenerator::getGenModule(DeclContext *ctxt) { if (GenModules.size() == 1 || !ctxt) { return getPrimaryIGM(); } - SourceFile *SF = ctxt->getParentSourceFile(); + SourceFile *SF = ctxt->getOutermostParentSourceFile(); if (!SF) { return getPrimaryIGM(); } + IRGenModule *IGM = GenModules[SF]; assert(IGM); return IGM; From 31d8cadcd7398151f997faf1f0445c36fe1ad8b9 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Wed, 8 Feb 2023 15:58:17 -0800 Subject: [PATCH 53/98] Make objcImpl classes derive inherited inits A class that uses @objcImplementation but does not explicitly declare any designated initializers previously did not override the superclass initializers, so its stored properties would not be initialized. Opt these classes into that logic and adjust it to add the initializers to the @objcImplementation extension instead of the ClassDecl itself. Fixes rdar://105008242. --- lib/SILGen/SILGenConstructor.cpp | 5 ++-- lib/Sema/CodeSynthesis.cpp | 40 +++++++++++++++++-------- lib/Sema/TypeCheckDecl.cpp | 2 +- test/IRGen/Inputs/objc_implementation.h | 9 ++++++ test/IRGen/objc_implementation.swift | 30 +++++++++++++++++++ 5 files changed, 70 insertions(+), 16 deletions(-) diff --git a/lib/SILGen/SILGenConstructor.cpp b/lib/SILGen/SILGenConstructor.cpp index 6c5b2fbad3ca7..2003f7f929c3d 100644 --- a/lib/SILGen/SILGenConstructor.cpp +++ b/lib/SILGen/SILGenConstructor.cpp @@ -1057,7 +1057,8 @@ emitMemberInit(SILGenFunction &SGF, VarDecl *selfDecl, Pattern *pattern) { slot = SGF.B.createStructElementAddr(pattern, self.forward(SGF), field, fieldTy.getAddressType()); } else { - assert(isa(field->getDeclContext())); + assert(isa(field->getDeclContext()-> + getImplementedObjCContext())); slot = SGF.B.createRefElementAddr(pattern, self.forward(SGF), field, fieldTy.getAddressType()); } @@ -1154,7 +1155,7 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc, NominalTypeDecl *nominal) { auto subs = getSubstitutionsForPropertyInitializer(dc, nominal); - for (auto member : nominal->getMembers()) { + for (auto member : nominal->getImplementationContext()->getMembers()) { // Find instance pattern binding declarations that have initializers. if (auto pbd = dyn_cast(member)) { if (pbd->isStatic()) continue; diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 55bfc480795d8..77682668ec6cc 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -619,12 +619,26 @@ createDesignatedInitOverrideGenericParams(ASTContext &ctx, ArrayRef(), SourceLoc()); } +/// True if the type has an opaque clang implementation, meaning it is imported +/// and doesn't have an \c \@objcImplementation extension. +static bool hasClangImplementation(const NominalTypeDecl *decl) { + return decl->hasClangNode() && !decl->getObjCImplementationDecl(); +} + +/// True if \p member is in the main body of \p ty, where the "main body" is +/// either the type itself (the usual case) or its \c \@objcImplementation +/// extension (if one is present). +static bool isInMainBody(ValueDecl *member, NominalTypeDecl *ty) { + return member->getDeclContext() == + ty->getImplementationContext()->getAsGenericContext(); +} + static void configureInheritedDesignatedInitAttributes(ClassDecl *classDecl, ConstructorDecl *ctor, ConstructorDecl *superclassCtor, ASTContext &ctx) { - assert(ctor->getDeclContext() == classDecl); + assert(isInMainBody(ctor, classDecl)); AccessLevel access = classDecl->getFormalAccess(); access = std::max(access, AccessLevel::Internal); @@ -856,6 +870,7 @@ createDesignatedInitOverride(ClassDecl *classDecl, // Create the initializer declaration, inheriting the name, // failability, and throws from the superclass initializer. + auto implCtx = classDecl->getImplementationContext()->getAsGenericContext(); auto ctor = new (ctx) ConstructorDecl(superclassCtor->getName(), classDecl->getBraces().Start, @@ -865,8 +880,7 @@ createDesignatedInitOverride(ClassDecl *classDecl, /*AsyncLoc=*/SourceLoc(), /*Throws=*/superclassCtor->hasThrows(), /*ThrowsLoc=*/SourceLoc(), - bodyParams, genericParams, - classDecl); + bodyParams, genericParams, implCtx); ctor->setImplicit(); @@ -988,9 +1002,9 @@ static void diagnoseMissingRequiredInitializer( bool AreAllStoredPropertiesDefaultInitableRequest::evaluate( Evaluator &evaluator, NominalTypeDecl *decl) const { - assert(!decl->hasClangNode()); + assert(!hasClangImplementation(decl)); - for (auto member : decl->getMembers()) { + for (auto member : decl->getImplementationContext()->getMembers()) { // If a stored property lacks an initial value and if there is no way to // synthesize an initial value (e.g. for an optional) then we suppress // generation of the default initializer. @@ -1031,7 +1045,7 @@ bool AreAllStoredPropertiesDefaultInitableRequest::evaluate( static bool areAllStoredPropertiesDefaultInitializable(Evaluator &eval, NominalTypeDecl *decl) { - if (decl->hasClangNode()) + if (hasClangImplementation(decl)) return true; return evaluateOrDefault( @@ -1041,11 +1055,11 @@ static bool areAllStoredPropertiesDefaultInitializable(Evaluator &eval, bool HasUserDefinedDesignatedInitRequest::evaluate(Evaluator &evaluator, NominalTypeDecl *decl) const { - assert(!decl->hasClangNode()); + assert(!hasClangImplementation(decl)); auto results = decl->lookupDirect(DeclBaseName::createConstructor()); for (auto *member : results) { - if (isa(member->getDeclContext())) + if (!isInMainBody(member, decl)) continue; auto *ctor = cast(member); @@ -1059,7 +1073,7 @@ HasUserDefinedDesignatedInitRequest::evaluate(Evaluator &evaluator, static bool hasUserDefinedDesignatedInit(Evaluator &eval, NominalTypeDecl *decl) { // Imported decls don't have a designated initializer defined by the user. - if (decl->hasClangNode()) + if (hasClangImplementation(decl)) return false; return evaluateOrDefault(eval, HasUserDefinedDesignatedInitRequest{decl}, @@ -1086,7 +1100,7 @@ static void collectNonOveriddenSuperclassInits( auto ctors = subclass->lookupDirect(DeclBaseName::createConstructor()); for (auto *member : ctors) { - if (isa(member->getDeclContext())) + if (!isInMainBody(member, subclass)) continue; auto *ctor = cast(member); @@ -1189,7 +1203,7 @@ static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) { auto results = decl->lookupDirect(DeclBaseName::createConstructor()); for (auto *member : results) { - if (isa(member->getDeclContext())) + if (!isInMainBody(member, decl)) continue; auto *ctor = cast(member); @@ -1217,7 +1231,7 @@ static void addImplicitInheritedConstructorsToClass(ClassDecl *decl) { if (auto ctor = createDesignatedInitOverride( decl, superclassCtor, kind, ctx)) { - decl->addMember(ctor); + decl->getImplementationContext()->addMember(ctor); } } } @@ -1258,7 +1272,7 @@ InheritsSuperclassInitializersRequest::evaluate(Evaluator &eval, static bool shouldAttemptInitializerSynthesis(const NominalTypeDecl *decl) { // Don't synthesize initializers for imported decls. - if (decl->hasClangNode()) + if (hasClangImplementation(decl)) return false; // Don't add implicit constructors in module interfaces. diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index ce59679490891..bfdea84e03ef3 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2737,7 +2737,7 @@ static ArrayRef evaluateMembersRequest( return ctx.AllocateCopy(result); } - auto nominal = dyn_cast(idc); + auto nominal = dyn_cast(dc->getImplementedObjCContext()); if (nominal) { // We need to add implicit initializers because they diff --git a/test/IRGen/Inputs/objc_implementation.h b/test/IRGen/Inputs/objc_implementation.h index c131a3ab7271e..aff13897e623f 100644 --- a/test/IRGen/Inputs/objc_implementation.h +++ b/test/IRGen/Inputs/objc_implementation.h @@ -30,3 +30,12 @@ - (void)noImplMethod:(int)param; @end + +@interface NoInitImplClass: NSObject + +@property (readonly, strong, nonnull) NSString *s1; +@property (strong, nonnull) NSString *s2; +@property (readonly, strong, nonnull) NSString *s3; +@property (strong, nonnull) NSString *s4; + +@end diff --git a/test/IRGen/objc_implementation.swift b/test/IRGen/objc_implementation.swift index 6ecb6a448a06b..157ca04f97da8 100644 --- a/test/IRGen/objc_implementation.swift +++ b/test/IRGen/objc_implementation.swift @@ -66,6 +66,24 @@ } } +// +// Second @_objcImplementation class, inherited initializer +// + +// CHECK: @"OBJC_METACLASS_$_NoInitImplClass" = global %objc_class { %objc_class* @"OBJC_METACLASS_$_NSObject", %objc_class* @"OBJC_METACLASS_$_NSObject", %swift.opaque* @_objc_empty_cache, %swift.opaque* null, i64 ptrtoint ({ i32, i32, i32, i32, i8*, i8*, i8*, i8*, i8*, i8*, i8* }* @_METACLASS_DATA_NoInitImplClass to i64) }, align 8 +// CHECK: @_METACLASS_DATA_NoInitImplClass = internal constant { i32, i32, i32, i32, i8*, i8*, i8*, i8*, i8*, i8*, i8* } { i32 129, i32 40, i32 40, i32 0, i8* null, i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str.15.NoInitImplClass, i64 0, i64 0), i8* null, i8* null, i8* null, i8* null, i8* null }, section "__DATA, __objc_const", align 8 +// CHECK: @_INSTANCE_METHODS_NoInitImplClass = internal constant { i32, i32, [8 x { i8*, i8*, i8* }] } { i32 24, i32 8, [8 x { i8*, i8*, i8* }] [{ i8*, i8*, i8* } { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"\01L_selector_data(s1)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.@16@0:8", i64 0, i64 0), i8* bitcast (%2* (%3*, i8*)* @"$sSo15NoInitImplClassC19objc_implementationE2s1SSvgTo" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"\01L_selector_data(s2)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.@16@0:8", i64 0, i64 0), i8* bitcast (%2* (%3*, i8*)* @"$sSo15NoInitImplClassC19objc_implementationE2s2SSvgTo" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"\01L_selector_data(setS2:)", i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v24@0:8@16", i64 0, i64 0), i8* bitcast (void (%3*, i8*, %2*)* @"$sSo15NoInitImplClassC19objc_implementationE2s2SSvsTo" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"\01L_selector_data(s3)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.@16@0:8", i64 0, i64 0), i8* bitcast (%2* (%3*, i8*)* @"$sSo15NoInitImplClassC19objc_implementationE2s3SSvgTo" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @"\01L_selector_data(s4)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.@16@0:8", i64 0, i64 0), i8* bitcast (%2* (%3*, i8*)* @"$sSo15NoInitImplClassC19objc_implementationE2s4SSvgTo" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"\01L_selector_data(setS4:)", i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v24@0:8@16", i64 0, i64 0), i8* bitcast (void (%3*, i8*, %2*)* @"$sSo15NoInitImplClassC19objc_implementationE2s4SSvsTo" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([5 x i8], [5 x i8]* @"\01L_selector_data(init)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.@16@0:8", i64 0, i64 0), i8* bitcast (%3* (%3*, i8*)* @"$sSo15NoInitImplClassC19objc_implementationEABycfcTo" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([14 x i8], [14 x i8]* @"\01L_selector_data(.cxx_destruct)", i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.v16@0:8", i64 0, i64 0), i8* bitcast (void (%3*, i8*)* @"$sSo15NoInitImplClassCfETo" to i8*) }] }, section "__DATA, __objc_data", align 8 +// CHECK: @_IVARS_NoInitImplClass = internal constant { i32, i32, [4 x { i64*, i8*, i8*, i32, i32 }] } { i32 32, i32 4, [4 x { i64*, i8*, i8*, i32, i32 }] [{ i64*, i8*, i8*, i32, i32 } { i64* @"$sSo15NoInitImplClassC19objc_implementationE2s1SSvpWvd", i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.2.s1, i64 0, i64 0), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.0., i64 0, i64 0), i32 3, i32 16 }, { i64*, i8*, i8*, i32, i32 } { i64* @"$sSo15NoInitImplClassC19objc_implementationE2s2SSvpWvd", i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.2.s2, i64 0, i64 0), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.0., i64 0, i64 0), i32 3, i32 16 }, { i64*, i8*, i8*, i32, i32 } { i64* @"$sSo15NoInitImplClassC19objc_implementationE2s3SSvpWvd", i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.2.s3, i64 0, i64 0), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.0., i64 0, i64 0), i32 3, i32 16 }, { i64*, i8*, i8*, i32, i32 } { i64* @"$sSo15NoInitImplClassC19objc_implementationE2s4SSvpWvd", i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.2.s4, i64 0, i64 0), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.0., i64 0, i64 0), i32 3, i32 16 }] }, section "__DATA, __objc_const", align 8 +// CHECK: @_PROPERTIES_NoInitImplClass = internal constant { i32, i32, [4 x { i8*, i8* }] } { i32 16, i32 4, [4 x { i8*, i8* }] [{ i8*, i8* } { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.2.s1, i64 0, i64 0), i8* getelementptr inbounds ([17 x i8], [17 x i8]* @".str.16.T@\22NSString\22,N,R", i64 0, i64 0) }, { i8*, i8* } { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.2.s2, i64 0, i64 0), i8* getelementptr inbounds ([17 x i8], [17 x i8]* @".str.16.T@\22NSString\22,N,C", i64 0, i64 0) }, { i8*, i8* } { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.2.s3, i64 0, i64 0), i8* getelementptr inbounds ([17 x i8], [17 x i8]* @".str.16.T@\22NSString\22,N,R", i64 0, i64 0) }, { i8*, i8* } { i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str.2.s4, i64 0, i64 0), i8* getelementptr inbounds ([17 x i8], [17 x i8]* @".str.16.T@\22NSString\22,N,C", i64 0, i64 0) }] }, section "__DATA, __objc_const", align 8 +// CHECK: @_DATA_NoInitImplClass = internal constant { i32, i32, i32, i32, i8*, i8*, { i32, i32, [8 x { i8*, i8*, i8* }] }*, i8*, { i32, i32, [4 x { i64*, i8*, i8*, i32, i32 }] }*, i8*, { i32, i32, [4 x { i8*, i8* }] }* } { i32 388, i32 8, i32 72, i32 0, i8* null, i8* getelementptr inbounds ([16 x i8], [16 x i8]* @.str.15.NoInitImplClass, i64 0, i64 0), { i32, i32, [8 x { i8*, i8*, i8* }] }* @_INSTANCE_METHODS_NoInitImplClass, i8* null, { i32, i32, [4 x { i64*, i8*, i8*, i32, i32 }] }* @_IVARS_NoInitImplClass, i8* null, { i32, i32, [4 x { i8*, i8* }] }* @_PROPERTIES_NoInitImplClass }, section "__DATA, __objc_data", align 8 +// CHECK: @"OBJC_CLASS_$_NoInitImplClass" = global <{ i64, %objc_class*, %swift.opaque*, %swift.opaque*, { i32, i32, i32, i32, i8*, i8*, { i32, i32, [8 x { i8*, i8*, i8* }] }*, i8*, { i32, i32, [4 x { i64*, i8*, i8*, i32, i32 }] }*, i8*, { i32, i32, [4 x { i8*, i8* }] }* }* }> <{ i64 ptrtoint (%objc_class* @"OBJC_METACLASS_$_NoInitImplClass" to i64), %objc_class* @"OBJC_CLASS_$_NSObject", %swift.opaque* @_objc_empty_cache, %swift.opaque* null, { i32, i32, i32, i32, i8*, i8*, { i32, i32, [8 x { i8*, i8*, i8* }] }*, i8*, { i32, i32, [4 x { i64*, i8*, i8*, i32, i32 }] }*, i8*, { i32, i32, [4 x { i8*, i8* }] }* }* @_DATA_NoInitImplClass }>, section "__DATA,__objc_data, regular", align 8 +@_objcImplementation extension NoInitImplClass { + @objc let s1 = "s1v" + @objc var s2 = "s2v" + @objc(s3) let s3 = "s3v" + @objc(s4) var s4 = "s4v" +} + // // @objc subclass of @_objcImplementation class // @@ -172,6 +190,18 @@ public func fn(impl: ImplClass, swiftSub: SwiftSubclass) { // ObjC calling convention -[ImplClass(Category1) category1Method:] // CHECK-LABEL: define internal void @"$sSo9ImplClassC19objc_implementationE15category1Methodyys5Int32VFTo" +// Swift calling convention -[NoInitImplClass init] +// CHECK-LABEL: define swiftcc %TSo15NoInitImplClassC* @"$sSo15NoInitImplClassC19objc_implementationEABycfc" + // CHECK-DAG: load i64, i64* @"$sSo15NoInitImplClassC19objc_implementationE2s1SSvpWvd", align 8 + // CHECK-DAG: load i64, i64* @"$sSo15NoInitImplClassC19objc_implementationE2s2SSvpWvd", align 8 + // CHECK-DAG: load i64, i64* @"$sSo15NoInitImplClassC19objc_implementationE2s3SSvpWvd", align 8 + // CHECK-DAG: load i64, i64* @"$sSo15NoInitImplClassC19objc_implementationE2s4SSvpWvd", align 8 + // CHECK: [[SEL_init:%[^ ]+]] = load i8*, i8** @"\01L_selector(init)", align 8 + // CHECK: call %0* bitcast (void ()* @objc_msgSendSuper{{.*}} [[SEL_init]]) + +// ObjC calling convention -[NoInitImplClass init] +// CHECK-LABEL: define internal %3* @"$sSo15NoInitImplClassC19objc_implementationEABycfcTo" + // Swift calling convention SwiftSubclass.mainMethod(_:) // CHECK-LABEL: define swiftcc void @"$s19objc_implementation13SwiftSubclassC10mainMethodyys5Int32VF" From aa6ef504fa654abcb2b52da0dab55ce56d191983 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 8 Feb 2023 16:06:21 -0800 Subject: [PATCH 54/98] Swap to an unprotected load to get better codegen --- .../Sources/Reflection/GenericArguments.swift | 2 +- .../Sources/Reflection/KeyPath.swift | 2 +- .../ContextDescriptor/ClassDescriptor.swift | 2 +- .../ContextDescriptor/GenericSignature.swift | 6 +-- .../_Runtime/ExistentialContainer.swift | 7 ++- .../_Runtime/Metadata/TupleMetadata.swift | 2 +- .../_Runtime/Metadata/TypeMetadata.swift | 20 +++---- .../Sources/_Runtime/Utils/BufferView.swift | 2 +- .../Sources/_Runtime/Utils/Layouts.swift | 4 +- .../_Runtime/Utils/MangledTypeReference.swift | 6 +-- .../Sources/_Runtime/Utils/PointerStuff.swift | 53 ++++++++++--------- .../Sources/_Runtime/Utils/PtrAuth.swift | 16 +++--- .../Utils/RelativeDirectPointer.swift | 2 +- .../Utils/RelativeIndirectPointer.swift | 4 +- .../Utils/RelativeIndirectablePointer.swift | 2 +- .../RelativeIndirectablePointerIntPair.swift | 4 +- .../_Runtime/Utils/RelativePointer.swift | 2 +- .../Sources/_Runtime/Utils/TypeCache.swift | 10 ++-- 18 files changed, 75 insertions(+), 71 deletions(-) diff --git a/stdlib/public/Reflection/Sources/Reflection/GenericArguments.swift b/stdlib/public/Reflection/Sources/Reflection/GenericArguments.swift index f3f5b5f1bfdc3..4315940518b45 100644 --- a/stdlib/public/Reflection/Sources/Reflection/GenericArguments.swift +++ b/stdlib/public/Reflection/Sources/Reflection/GenericArguments.swift @@ -51,7 +51,7 @@ extension GenericArguments: RandomAccessCollection { let start = argumentPointer.unsafelyUnwrapped let address = start + position * MemoryLayout.size - return address.loadUnaligned(as: Type.self) + return address.unprotectedLoad(as: Type.self) } } diff --git a/stdlib/public/Reflection/Sources/Reflection/KeyPath.swift b/stdlib/public/Reflection/Sources/Reflection/KeyPath.swift index ba124dff1de7b..1e21cc8fc99d1 100644 --- a/stdlib/public/Reflection/Sources/Reflection/KeyPath.swift +++ b/stdlib/public/Reflection/Sources/Reflection/KeyPath.swift @@ -96,7 +96,7 @@ extension Case { var value = pair.buffer if isIndirect { - let owner = value.loadUnaligned(as: HeapObject.self) + let owner = value.unprotectedLoad(as: HeapObject.self) value = swift_projectBox(owner) } diff --git a/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/ClassDescriptor.swift b/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/ClassDescriptor.swift index 074d791ef3b13..ac07cd7465c34 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/ClassDescriptor.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/ClassDescriptor.swift @@ -80,7 +80,7 @@ extension ClassDescriptor { // 'TargetStoredClassMetadataBounds', by the time we access this it will // have already been initialized way before for us. Thus, it is safe to // access this value non-atomically. - return storedBounds.load(as: Int.self) + return storedBounds.unprotectedLoad(as: Int.self) } @inlinable diff --git a/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/GenericSignature.swift b/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/GenericSignature.swift index 9626905195de4..0c6ce8ab863ae 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/GenericSignature.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/ContextDescriptor/GenericSignature.swift @@ -119,7 +119,7 @@ extension GenericSignature.RequirementDescriptor { @available(SwiftStdlib 5.9, *) @inlinable public var layoutKind: GenericSignature.LayoutKind { - address(for: \.requirement).loadUnaligned( + address(for: \.requirement).unprotectedLoad( as: GenericSignature.LayoutKind.self ) } @@ -140,7 +140,7 @@ extension GenericSignature.RequirementDescriptor { ) -> Any.Type? { _getTypeByMangledNameInContext( UnsafePointer(parameter.ptr._rawValue), - UInt(parameter.length), + UInt(truncatingIfNeeded: parameter.length), genericContext: context.ptr, genericArguments: argPtr ) @@ -269,7 +269,7 @@ extension GenericSignature.RequirementDescriptor { func getGenericSignature(at address: UnsafeRawPointer) -> GenericSignature { var address = address - let header = address.loadUnaligned(as: GenericSignature.Header.self) + let header = address.unprotectedLoad(as: GenericSignature.Header.self) address += MemoryLayout.size let parameters = BufferView( diff --git a/stdlib/public/Reflection/Sources/_Runtime/ExistentialContainer.swift b/stdlib/public/Reflection/Sources/_Runtime/ExistentialContainer.swift index 753859fe504de..a39545f082e32 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/ExistentialContainer.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/ExistentialContainer.swift @@ -67,15 +67,14 @@ extension AnyExistentialContainer { } } - let alignMask = UInt(metadata.vwt.flags.alignmentMask) + let alignMask = UInt(truncatingIfNeeded:metadata.vwt.flags.alignmentMask) let heapObjSize = UInt(MemoryLayout.size * 2) let byteOffset = (heapObjSize + alignMask) & ~alignMask return try withUnsafeMutablePointer(to: &self) { - let raw = UnsafeMutableRawPointer($0) - let heap = raw.loadUnaligned(as: UnsafeMutableRawPointer.self) + let heap = $0.raw.unprotectedLoad(as: UnsafeMutableRawPointer.self) - return try body(heap + Int(byteOffset)) + return try body(heap + Int(truncatingIfNeeded: byteOffset)) } } } diff --git a/stdlib/public/Reflection/Sources/_Runtime/Metadata/TupleMetadata.swift b/stdlib/public/Reflection/Sources/_Runtime/Metadata/TupleMetadata.swift index 0b78c09ec94d9..8dd76bb7cea04 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Metadata/TupleMetadata.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Metadata/TupleMetadata.swift @@ -85,7 +85,7 @@ extension TupleMetadata.Elements.Element { // string address -= MemoryLayout.size - guard let cString = address.loadUnaligned( + guard let cString = address.unprotectedLoad( as: UnsafePointer?.self ) else { return "" diff --git a/stdlib/public/Reflection/Sources/_Runtime/Metadata/TypeMetadata.swift b/stdlib/public/Reflection/Sources/_Runtime/Metadata/TypeMetadata.swift index 3492a508fbc57..3b3c3f9ac9763 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Metadata/TypeMetadata.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Metadata/TypeMetadata.swift @@ -29,7 +29,7 @@ extension TypeMetadata { @inlinable public var descriptor: TypeDescriptor { var address: UnsafeRawPointer - + switch metadata.kind { case .struct: address = ptr + MemoryLayout.offset(of: \.descriptor)! @@ -39,12 +39,12 @@ extension TypeMetadata { default: address = ptr + MemoryLayout.offset(of: \.descriptor)! } - - address = address.loadUnaligned(as: UnsafeRawPointer.self) - + + address = address.unprotectedLoad(as: UnsafeRawPointer.self) + return TypeDescriptor(address) } - + @inlinable public var genericArguments: UnsafeRawPointer { switch metadata.kind { @@ -68,17 +68,17 @@ extension TypeMetadata { public var metadata: Metadata { Metadata(ptr) } - + @inlinable public var `class`: ClassMetadata { ClassMetadata(ptr) } - + @inlinable public var `enum`: EnumMetadata { EnumMetadata(ptr) } - + @inlinable public var `struct`: StructMetadata { StructMetadata(ptr) @@ -93,10 +93,10 @@ extension TypeMetadata { if let ss = typeRef.standardSubstitution { return ss } - + return _resolve(typeRef) } - + @usableFromInline func _resolve(_ typeRef: MangledTypeReference) -> Any.Type? { typeCache.getOrInsert(typeRef, from: self) diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/BufferView.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/BufferView.swift index fb76cc55486c3..3303eb1484f24 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/BufferView.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/BufferView.swift @@ -54,7 +54,7 @@ extension BufferView: RandomAccessCollection { @inlinable public subscript(position: Int) -> Element { - start.loadUnaligned( + start.unprotectedLoad( fromByteOffset: position * MemoryLayout.size, as: Element.self ) diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/Layouts.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/Layouts.swift index 0b62a3b70d422..a0722dd1e9d57 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/Layouts.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/Layouts.swift @@ -25,7 +25,7 @@ extension PublicLayout { @inline(__always) @inlinable public var layout: Layout { - ptr.loadUnaligned(as: Layout.self) + ptr.unprotectedLoad(as: Layout.self) } @inline(__always) @@ -74,7 +74,7 @@ protocol PrivateLayout { @available(SwiftStdlib 5.9, *) extension PrivateLayout { var layout: Layout { - ptr.loadUnaligned(as: Layout.self) + ptr.unprotectedLoad(as: Layout.self) } var trailing: UnsafeRawPointer { diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/MangledTypeReference.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/MangledTypeReference.swift index 2a9ef02330e52..41410f1195ec6 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/MangledTypeReference.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/MangledTypeReference.swift @@ -34,19 +34,19 @@ extension MangledTypeReference { @available(SwiftStdlib 5.9, *) @inlinable var standardSubstitution: Any.Type? { - let byte1 = ptr.loadUnaligned(fromByteOffset: 1, as: UInt8.self) + let byte1 = ptr.unprotectedLoad(fromByteOffset: 1, as: UInt8.self) guard byte1 != 0 else { return nil } - let byte2 = ptr.loadUnaligned(fromByteOffset: 2, as: UInt8.self) + let byte2 = ptr.unprotectedLoad(fromByteOffset: 2, as: UInt8.self) guard byte2 == 0 else { return nil } - let byte0 = ptr.loadUnaligned(as: UInt8.self) + let byte0 = ptr.unprotectedLoad(as: UInt8.self) switch (byte0, byte1) { // Stdlib types diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift index 436fffe898b84..3f1a2bdf501ba 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift @@ -39,46 +39,51 @@ extension UnsafeBufferPointer { extension UnsafeRawPointer { @_alwaysEmitIntoClient - var bitPattern: UInt64 { - UInt64(truncatingIfNeeded: UInt(bitPattern: self)) + var binaryString: String { + // FIXME: Use 'StaticString's description when it makes an immortal string. + String(cString: UnsafePointer(_rawValue)) } -} -extension UInt64 { @_alwaysEmitIntoClient - var rawPointer: UnsafeRawPointer { - let pointer = UnsafeRawPointer(bitPattern: UInt(truncatingIfNeeded: self)) - - return pointer.unsafelyUnwrapped + var bitPattern: UInt64 { + UInt64(truncatingIfNeeded: UInt(bitPattern: self)) } -} -extension UnsafeRawPointer { @_alwaysEmitIntoClient var mutable: UnsafeMutableRawPointer { UnsafeMutableRawPointer(mutating: self) } + + @_alwaysEmitIntoClient + func offset(of count: Int) -> UnsafeRawPointer { + advanced(by: count * MemoryLayout.size) + } + + @_alwaysEmitIntoClient + public func unprotectedLoad( + fromByteOffset offset: Int = 0, + as type: T.Type + ) -> T { + UnsafePointer((self + offset)._rawValue).pointee + } } -extension UnsafeRawPointer { +extension UnsafeMutableRawPointer { @_alwaysEmitIntoClient - var binaryString: String { -// let length = strlen(UnsafePointer(_rawValue)) -// -// // This is a hack to make an immortal string. -// return String( -// _builtinStringLiteral: _rawValue, -// utf8CodeUnitCount: length._builtinWordValue, -// isASCII: Builtin.trunc_Word_Int1(0._builtinWordValue) -// ) - String(cString: UnsafePointer(_rawValue)) + public func unprotectedLoad( + fromByteOffset offset: Int = 0, + as type: T.Type + ) -> T { + UnsafePointer((self + offset)._rawValue).pointee } } -extension UnsafeRawPointer { +extension UInt64 { @_alwaysEmitIntoClient - func offset(of count: Int) -> UnsafeRawPointer { - advanced(by: count * MemoryLayout.size) + var rawPointer: UnsafeRawPointer { + let pointer = UnsafeRawPointer(bitPattern: UInt(truncatingIfNeeded: self)) + + return pointer.unsafelyUnwrapped } } diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/PtrAuth.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/PtrAuth.swift index 08f26a4baad09..0f9a7faff1556 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/PtrAuth.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/PtrAuth.swift @@ -336,49 +336,49 @@ extension UnsafeRawPointer { @available(SwiftStdlib 5.9, *) @inlinable var signedVWTInitializeBufferWithCopyOfBuffer: ValueWitnessTable.InitializeBufferWithCopyOfBuffer { - unsafeBitCast(loadUnaligned(as: UnsafeRawPointer.self)) + unsafeBitCast(unprotectedLoad(as: UnsafeRawPointer.self)) } @available(SwiftStdlib 5.9, *) @inlinable var signedVWTDestroy: ValueWitnessTable.Destroy { - unsafeBitCast(loadUnaligned(as: UnsafeRawPointer.self)) + unsafeBitCast(unprotectedLoad(as: UnsafeRawPointer.self)) } @available(SwiftStdlib 5.9, *) @inlinable var signedVWTInitializeWithCopy: ValueWitnessTable.InitializeWithCopy { - unsafeBitCast(loadUnaligned(as: UnsafeRawPointer.self)) + unsafeBitCast(unprotectedLoad(as: UnsafeRawPointer.self)) } @available(SwiftStdlib 5.9, *) @inlinable var signedVWTAssignWithCopy: ValueWitnessTable.AssignWithCopy { - unsafeBitCast(loadUnaligned(as: UnsafeRawPointer.self)) + unsafeBitCast(unprotectedLoad(as: UnsafeRawPointer.self)) } @available(SwiftStdlib 5.9, *) @inlinable var signedVWTInitializeWithTake: ValueWitnessTable.InitializeWithTake { - unsafeBitCast(loadUnaligned(as: UnsafeRawPointer.self)) + unsafeBitCast(unprotectedLoad(as: UnsafeRawPointer.self)) } @available(SwiftStdlib 5.9, *) @inlinable var signedVWTAssignWithTake: ValueWitnessTable.AssignWithTake { - unsafeBitCast(loadUnaligned(as: UnsafeRawPointer.self)) + unsafeBitCast(unprotectedLoad(as: UnsafeRawPointer.self)) } @available(SwiftStdlib 5.9, *) @inlinable var signedVWTGetEnumTagSinglePayload: ValueWitnessTable.GetEnumTagSinglePayload { - unsafeBitCast(loadUnaligned(as: UnsafeRawPointer.self)) + unsafeBitCast(unprotectedLoad(as: UnsafeRawPointer.self)) } @available(SwiftStdlib 5.9, *) @inlinable var signedVWTStoreEnumTagSinglePayload: ValueWitnessTable.StoreEnumTagSinglePayload { - unsafeBitCast(loadUnaligned(as: UnsafeRawPointer.self)) + unsafeBitCast(unprotectedLoad(as: UnsafeRawPointer.self)) } #endif } diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeDirectPointer.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeDirectPointer.swift index fd817a391d722..87456b1adf569 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeDirectPointer.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeDirectPointer.swift @@ -32,7 +32,7 @@ extension UnsafeRawPointer { @inlinable public func relativeDirectAddress(as type: T.Type) -> UnsafeRawPointer { let relativePointer = RelativeDirectPointer( - offset: loadUnaligned(as: Int32.self) + offset: unprotectedLoad(as: Int32.self) ) return relativePointer.address(from: self) diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectPointer.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectPointer.swift index aa85658403cdb..c3b2049f8f806 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectPointer.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectPointer.swift @@ -23,7 +23,7 @@ public struct RelativeIndirectPointer: RelativePointer { @inlinable public func address(from ptr: UnsafeRawPointer) -> UnsafeRawPointer { - (ptr + Int(offset)).loadUnaligned(as: UnsafeRawPointer.self) + (ptr + Int(offset)).unprotectedLoad(as: UnsafeRawPointer.self) } } @@ -32,7 +32,7 @@ extension UnsafeRawPointer { @inlinable public func relativeIndirectAddress(as type: T.Type) -> UnsafeRawPointer { let relativePointer = RelativeIndirectPointer( - offset: loadUnaligned(as: Int32.self) + offset: unprotectedLoad(as: Int32.self) ) return relativePointer.address(from: self) diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointer.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointer.swift index 56b61f1a7ac26..e4e062355fe19 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointer.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointer.swift @@ -26,7 +26,7 @@ public struct RelativeIndirectablePointer: RelativePointer { let address = ptr + Int(truncatingIfNeeded: offset & ~1) if offset & 1 != 0 { - return address.loadUnaligned(as: UnsafeRawPointer.self) + return address.unprotectedLoad(as: UnsafeRawPointer.self) } else { return address } diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointerIntPair.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointerIntPair.swift index c6cebeecca055..029bb0ed82bf6 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointerIntPair.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativeIndirectablePointerIntPair.swift @@ -49,7 +49,7 @@ public struct RelativeIndirectablePointerIntPair< let address = ptr + (unresolved & ~1) if unresolved & 1 != 0 { - return address.loadUnaligned(as: UnsafeRawPointer.self) + return address.unprotectedLoad(as: UnsafeRawPointer.self) } else { return address } @@ -64,7 +64,7 @@ extension UnsafeRawPointer { and type2: U.Type ) -> UnsafeRawPointer { let relativePointer = RelativeIndirectablePointerIntPair( - offset: loadUnaligned(as: Int32.self) + offset: unprotectedLoad(as: Int32.self) ) return relativePointer.address(from: self) diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativePointer.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativePointer.swift index be9c1db59ccfb..4b7464e4af90b 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativePointer.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/RelativePointer.swift @@ -34,6 +34,6 @@ extension RelativePointer { return nil } - return address(from: ptr).loadUnaligned(as: Pointee.self) + return address(from: ptr).unprotectedLoad(as: Pointee.self) } } diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/TypeCache.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/TypeCache.swift index 32a61fdfcfa7f..77809bd4d2b77 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/TypeCache.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/TypeCache.swift @@ -42,8 +42,8 @@ extension TypeCache.Key: Equatable { var isGeneric = false - loop: while let lhsCurrent = Optional(lhsEnd.loadUnaligned(as: UInt8.self)), - let rhsCurrent = Optional(rhsEnd.loadUnaligned(as: UInt8.self)) { + loop: while let lhsCurrent = Optional(lhsEnd.unprotectedLoad(as: UInt8.self)), + let rhsCurrent = Optional(rhsEnd.unprotectedLoad(as: UInt8.self)) { lhsEnd += 1 rhsEnd += 1 @@ -113,7 +113,7 @@ extension TypeCache.Key: Hashable { var end = typeRef.ptr - while let current = Optional(end.loadUnaligned(as: UInt8.self)), current != 0 { + while let current = Optional(end.unprotectedLoad(as: UInt8.self)), current != 0 { end += 1 switch current { @@ -190,10 +190,10 @@ extension TypeCache { @available(SwiftStdlib 5.9, *) var typeCache: TypeCache = { var result = TypeCache() - + result.cache.withLock { $0.reserveCapacity(50) } - + return result }() From 38e9af48e4b5833ae7cffdb8c4f52f873f9981ad Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 8 Feb 2023 16:07:39 -0800 Subject: [PATCH 55/98] Move some tests into our testsuite and various other fixes --- stdlib/public/Reflection/Package.swift | 77 --------- stdlib/public/Reflection/README.md | 3 - .../Reflection/Sources/Reflection/Case.swift | 8 +- .../Reflection/Sources/Reflection/Field.swift | 3 +- .../_Runtime/Metadata/ValueWitnessTable.swift | 53 +----- .../Sources/_Runtime/Utils/Lock.swift | 2 + .../_SwiftRuntimeShims/module.modulemap | 4 - .../ReflectionTests/ReflectionTests.swift | 3 - .../ContextDescriptor/StructDescriptor.swift | 31 ---- .../RuntimeTests/Metadata/EnumMetadata.swift | 29 ---- .../RuntimeTests/Metadata/Metadata.swift | 42 ----- .../Metadata/MetatypeMetadata.swift | 11 -- .../Metadata/StructMetadata.swift | 29 ---- .../RuntimeTests/Metadata/TypeMetadata.swift | 87 ---------- .../Tests/RuntimeTests/RuntimeTests.swift | 3 - .../ContextDescriptor/StructDescriptor.swift | 36 ++++ .../_Runtime}/Metadata/FunctionMetadata.swift | 143 ++++++++-------- test/stdlib/_Runtime/Metadata/Metadata.swift | 47 ++++++ .../_Runtime/Metadata/MetatypeMetadata.swift | 18 ++ .../_Runtime/Metadata/StructMetadata.swift | 34 ++++ .../_Runtime}/Metadata/TupleMetadata.swift | 55 ++++--- .../_Runtime/Metadata/TypeMetadata.swift | 85 ++++++++++ .../Metadata/ValueWitnessTable.swift | 155 +++++++++--------- 23 files changed, 420 insertions(+), 538 deletions(-) delete mode 100644 stdlib/public/Reflection/Package.swift delete mode 100644 stdlib/public/Reflection/README.md delete mode 100644 stdlib/public/Reflection/Sources/_SwiftRuntimeShims/module.modulemap delete mode 100644 stdlib/public/Reflection/Tests/ReflectionTests/ReflectionTests.swift delete mode 100644 stdlib/public/Reflection/Tests/RuntimeTests/ContextDescriptor/StructDescriptor.swift delete mode 100644 stdlib/public/Reflection/Tests/RuntimeTests/Metadata/EnumMetadata.swift delete mode 100644 stdlib/public/Reflection/Tests/RuntimeTests/Metadata/Metadata.swift delete mode 100644 stdlib/public/Reflection/Tests/RuntimeTests/Metadata/MetatypeMetadata.swift delete mode 100644 stdlib/public/Reflection/Tests/RuntimeTests/Metadata/StructMetadata.swift delete mode 100644 stdlib/public/Reflection/Tests/RuntimeTests/Metadata/TypeMetadata.swift delete mode 100644 stdlib/public/Reflection/Tests/RuntimeTests/RuntimeTests.swift create mode 100644 test/stdlib/_Runtime/ContextDescriptor/StructDescriptor.swift rename {stdlib/public/Reflection/Tests/RuntimeTests => test/stdlib/_Runtime}/Metadata/FunctionMetadata.swift (68%) create mode 100644 test/stdlib/_Runtime/Metadata/Metadata.swift create mode 100644 test/stdlib/_Runtime/Metadata/MetatypeMetadata.swift create mode 100644 test/stdlib/_Runtime/Metadata/StructMetadata.swift rename {stdlib/public/Reflection/Tests/RuntimeTests => test/stdlib/_Runtime}/Metadata/TupleMetadata.swift (61%) create mode 100644 test/stdlib/_Runtime/Metadata/TypeMetadata.swift rename {stdlib/public/Reflection/Tests/RuntimeTests => test/stdlib/_Runtime}/Metadata/ValueWitnessTable.swift (60%) diff --git a/stdlib/public/Reflection/Package.swift b/stdlib/public/Reflection/Package.swift deleted file mode 100644 index bea5e295f71a2..0000000000000 --- a/stdlib/public/Reflection/Package.swift +++ /dev/null @@ -1,77 +0,0 @@ -// swift-tools-version: 5.7 - -import PackageDescription - -let commonFlags = [ - "-I", "\(Context.packageDirectory)/Sources/_SwiftRuntimeShims", - - "-Xfrontend" ,"-define-availability", - "-Xfrontend", "SwiftStdlib 9999:macOS 9999, iOS 9999, watchOS 9999, tvOS 9999" -] - -let libraryFlags = [ - "-parse-stdlib", - "-enable-library-evolution", - "-Xfrontend", "-disable-implicit-concurrency-module-import", - "-Xfrontend", "-disable-implicit-string-processing-module-import", - "-assert-config", "Release", -] - -let testFlags = [ - "-Xfrontend", "-disable-availability-checking" -] - -let package = Package( - name: "Reflection", - platforms: [ - .macOS(.v13) - ], - products: [ - .library( - name: "_Runtime", - type: .dynamic, - targets: ["_Runtime"] - ), - .library( - name: "Reflection", - type: .dynamic, - targets: ["Reflection"] - ), - ], - targets: [ - .target( - name: "_Runtime", - exclude: [ - "CMakeLists.txt" - ], - swiftSettings: [ - .unsafeFlags(commonFlags + libraryFlags) - ] - ), - .target( - name: "Reflection", - dependencies: ["_Runtime"], - exclude: [ - "CMakeLists.txt" - ], - swiftSettings: [ - .unsafeFlags(commonFlags + libraryFlags) - ] - ), - - .testTarget( - name: "RuntimeTests", - dependencies: ["_Runtime"], - swiftSettings: [ - .unsafeFlags(commonFlags + testFlags) - ] - ), - .testTarget( - name: "ReflectionTests", - dependencies: ["_Runtime", "Reflection"], - swiftSettings: [ - .unsafeFlags(commonFlags + testFlags) - ] - ), - ] -) diff --git a/stdlib/public/Reflection/README.md b/stdlib/public/Reflection/README.md deleted file mode 100644 index 65fed9b2f9bba..0000000000000 --- a/stdlib/public/Reflection/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Reflection - -A description of this package. diff --git a/stdlib/public/Reflection/Sources/Reflection/Case.swift b/stdlib/public/Reflection/Sources/Reflection/Case.swift index b9d08250bbbf2..9f5f77faa7893 100644 --- a/stdlib/public/Reflection/Sources/Reflection/Case.swift +++ b/stdlib/public/Reflection/Sources/Reflection/Case.swift @@ -29,17 +29,19 @@ public struct Case { @inlinable public init?(from instance: Any) { - guard Type(instance).isEnum else { + let instanceTy = type(of: instance) + + guard Type(instanceTy).isEnum else { return nil } var container = unsafeBitCast(instance, to: AnyExistentialContainer.self) let tag = container.projectValue { - Metadata(type(of: instance)).enum.enumVWT.getEnumTag($0) + Metadata(instanceTy).enum.enumVWT.getEnumTag($0) } - self.parent = Metadata(type(of: instance)).enum + self.parent = Metadata(instanceTy).enum self.tag = Int(truncatingIfNeeded: tag) } } diff --git a/stdlib/public/Reflection/Sources/Reflection/Field.swift b/stdlib/public/Reflection/Sources/Reflection/Field.swift index 19652c06d08c4..5f55ca20cd916 100644 --- a/stdlib/public/Reflection/Sources/Reflection/Field.swift +++ b/stdlib/public/Reflection/Sources/Reflection/Field.swift @@ -42,8 +42,7 @@ extension Field { @inlinable public var name: String { guard parent.kind != .tuple else { - //return TupleMetadata(parent.ptr).elements[index]. - return "hello" + return parent.tuple.elements[index].label } return parent.type.descriptor.fields[index].name diff --git a/stdlib/public/Reflection/Sources/_Runtime/Metadata/ValueWitnessTable.swift b/stdlib/public/Reflection/Sources/_Runtime/Metadata/ValueWitnessTable.swift index 7f41e4f7112ba..cfe7b3e7d417d 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Metadata/ValueWitnessTable.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Metadata/ValueWitnessTable.swift @@ -128,7 +128,7 @@ extension ValueWitnessTable { _ dest: UnsafeMutableRawPointer, _ src: UnsafeRawPointer ) -> UnsafeMutableRawPointer { - let address = layout.raw + let address = address(for: \.initializeBufferWithCopyOfBuffer) return address.signedVWTInitializeBufferWithCopyOfBuffer( dest, @@ -139,17 +139,7 @@ extension ValueWitnessTable { @inlinable public func destroy(_ src: UnsafeMutableRawPointer) { - // rdar://103834325 - // FIXME: There's currently a compiler bug preventing me from doing: - // 'address(of: \.destroy)' - // or even - // 'MemoryLayout.offset(of: \.destroy)' - // - // The same goes for everything else in this file - let address = layout.raw - + MemoryLayout.size - - address.signedVWTDestroy(src, trailing) + address(for: \.destroy).signedVWTDestroy(src, trailing) } @inlinable @@ -158,9 +148,7 @@ extension ValueWitnessTable { _ dest: UnsafeMutableRawPointer, _ src: UnsafeRawPointer ) -> UnsafeMutableRawPointer { - let address = layout.raw - + MemoryLayout.size - + MemoryLayout.size + let address = address(for: \.initializeWithCopy) return address.signedVWTInitializeWithCopy(dest, src, trailing) } @@ -171,10 +159,7 @@ extension ValueWitnessTable { _ dest: UnsafeMutableRawPointer, _ src: UnsafeRawPointer ) -> UnsafeMutableRawPointer { - let address = layout.raw - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size + let address = address(for: \.assignWithCopy) return address.signedVWTAssignWithCopy(dest, src, trailing) } @@ -185,11 +170,7 @@ extension ValueWitnessTable { _ dest: UnsafeMutableRawPointer, _ src: UnsafeMutableRawPointer ) -> UnsafeMutableRawPointer { - let address = layout.raw - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size + let address = address(for: \.initializeWithTake) return address.signedVWTInitializeWithTake(dest, src, trailing) } @@ -200,12 +181,7 @@ extension ValueWitnessTable { _ dest: UnsafeMutableRawPointer, _ src: UnsafeMutableRawPointer ) -> UnsafeMutableRawPointer { - let address = layout.raw - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size + let address = address(for: \.assignWithTake) return address.signedVWTAssignWithTake(dest, src, trailing) } @@ -216,13 +192,7 @@ extension ValueWitnessTable { _ src: UnsafeRawPointer, _ numberOfEmptyCases: UInt32 ) -> UInt32 { - let address = layout.raw - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size + let address = address(for: \.getEnumTagSinglePayload) return address.signedVWTGetEnumTagSinglePayload( src, @@ -237,14 +207,7 @@ extension ValueWitnessTable { _ tag: UInt32, _ numberOfEmptyCases: UInt32 ) -> () { - let address = layout.raw - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size - + MemoryLayout.size + let address = address(for: \.storeEnumTagSinglePayload) return address.signedVWTStoreEnumTagSinglePayload( src, diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/Lock.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/Lock.swift index 3094995133698..12294705ef8f0 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/Lock.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/Lock.swift @@ -43,6 +43,8 @@ class Lock { UnsafeRawPointer.self ) + lock.value = initialValue + _lockInit(lock.mutex) return lock diff --git a/stdlib/public/Reflection/Sources/_SwiftRuntimeShims/module.modulemap b/stdlib/public/Reflection/Sources/_SwiftRuntimeShims/module.modulemap deleted file mode 100644 index 71f744b3be421..0000000000000 --- a/stdlib/public/Reflection/Sources/_SwiftRuntimeShims/module.modulemap +++ /dev/null @@ -1,4 +0,0 @@ -module _SwiftRuntimeShims { - umbrella "../../../SwiftShims/swift/shims/_SwiftRuntimeShims" - export * -} diff --git a/stdlib/public/Reflection/Tests/ReflectionTests/ReflectionTests.swift b/stdlib/public/Reflection/Tests/ReflectionTests/ReflectionTests.swift deleted file mode 100644 index 2cecb9498998e..0000000000000 --- a/stdlib/public/Reflection/Tests/ReflectionTests/ReflectionTests.swift +++ /dev/null @@ -1,3 +0,0 @@ -import XCTest - -final class ReflectionTests: XCTestCase {} diff --git a/stdlib/public/Reflection/Tests/RuntimeTests/ContextDescriptor/StructDescriptor.swift b/stdlib/public/Reflection/Tests/RuntimeTests/ContextDescriptor/StructDescriptor.swift deleted file mode 100644 index d3fdbace00b6d..0000000000000 --- a/stdlib/public/Reflection/Tests/RuntimeTests/ContextDescriptor/StructDescriptor.swift +++ /dev/null @@ -1,31 +0,0 @@ -import XCTest -import _Runtime - -enum StructDescriptorTests { - struct Dog { - let name: String - let age: Int - } - - struct GenericDog { - let one: T - let age: Int - let two: T - } -} - -extension RuntimeTests { - func testStructDescriptor() throws { - let dog = Metadata(StructDescriptorTests.Dog.self).struct - let descriptor = dog.descriptor - - XCTAssertEqual(descriptor.base.name, "Dog") - } - - func testGenericStructDescriptor() throws { - let dog = Metadata(StructDescriptorTests.GenericDog.self).struct - let descriptor = dog.descriptor - - XCTAssertEqual(descriptor.base.name, "GenericDog") - } -} diff --git a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/EnumMetadata.swift b/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/EnumMetadata.swift deleted file mode 100644 index f9629e1f7bace..0000000000000 --- a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/EnumMetadata.swift +++ /dev/null @@ -1,29 +0,0 @@ -import XCTest -import _Runtime - -enum EnumMetadataTests { - enum Color { - case red - case green - case blue - case generic(Int) - } - - enum GenericColor { - case generic(T) - case generic2(T, T) - case weird(T, Int, T) - } -} - -extension RuntimeTests { - func testEnumMetadata() throws { - let metadata = Metadata(EnumMetadataTests.Color.self).enum - - - } - - func testGenericEnumMetadata() throws { - - } -} diff --git a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/Metadata.swift b/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/Metadata.swift deleted file mode 100644 index e1acce745570e..0000000000000 --- a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/Metadata.swift +++ /dev/null @@ -1,42 +0,0 @@ -import XCTest -import _Runtime - -enum MetadataTests { - struct Dog {} - - enum Cat {} - - class Fish {} -} - -extension RuntimeTests { - func testMetadata() throws { - let structMetadata = Metadata(MetadataTests.Dog.self) - let enumMetadata = Metadata(MetadataTests.Cat.self) - let classMetadata = Metadata(MetadataTests.Fish.self) - let existentialMetadata = Metadata((any Equatable).self) - let functionMetadata = Metadata((() -> ()).self) - let metatypeMetadata = Metadata(Int.Type.self) - let tupleMetadata = Metadata(Void.self) - -//===----------------------------------------------------------------------===// -// Kinds -//===----------------------------------------------------------------------===// - - XCTAssertEqual(structMetadata.kind, .struct) - XCTAssertEqual(enumMetadata.kind, .enum) - XCTAssertEqual(classMetadata.kind, .class) - XCTAssertEqual(existentialMetadata.kind, .existential) - XCTAssertEqual(functionMetadata.kind, .function) - XCTAssertEqual(metatypeMetadata.kind, .metatype) - XCTAssertEqual(tupleMetadata.kind, .tuple) - -//===----------------------------------------------------------------------===// -// Metadata Equality -//===----------------------------------------------------------------------===// - - XCTAssertEqual(structMetadata, structMetadata) - XCTAssertNotEqual(structMetadata, Metadata(Int.self)) - XCTAssertNotEqual(structMetadata, classMetadata) - } -} diff --git a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/MetatypeMetadata.swift b/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/MetatypeMetadata.swift deleted file mode 100644 index e124d5e38376f..0000000000000 --- a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/MetatypeMetadata.swift +++ /dev/null @@ -1,11 +0,0 @@ -import XCTest -import _Runtime - -extension RuntimeTests { - func testMetatypeMetadata() throws { - let metatype = Metadata(Int.Type.self).metatype - let int = Metadata(Int.self) - - XCTAssertEqual(metatype.instanceMetadata, int) - } -} diff --git a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/StructMetadata.swift b/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/StructMetadata.swift deleted file mode 100644 index b0936d5cd82bd..0000000000000 --- a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/StructMetadata.swift +++ /dev/null @@ -1,29 +0,0 @@ -import XCTest -import _Runtime - -enum StructMetadataTests { - struct Dog { - let name: String - let age: Int - } - - struct GenericDog { - let one: T - let age: Int - let two: T - } -} - -extension RuntimeTests { - func testStructMetadata() throws { - let dog = Metadata(StructMetadataTests.Dog.self).struct - - XCTAssert(dog.fieldOffsets.elementsEqual([0, 16])) - } - - func testGenericStructMetadata() throws { - let genericDog = Metadata(StructMetadataTests.GenericDog.self).struct - - XCTAssert(genericDog.fieldOffsets.elementsEqual([0, 16, 24])) - } -} diff --git a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/TypeMetadata.swift b/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/TypeMetadata.swift deleted file mode 100644 index 457710c949bda..0000000000000 --- a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/TypeMetadata.swift +++ /dev/null @@ -1,87 +0,0 @@ -import XCTest -import _Runtime - -enum TypeMetadataTests { - struct Struct { - let xyz: T - } - - enum Enum { - case generic(T) - } - - class Class { - let xyz: T - - init(xyz: T) { - self.xyz = xyz - } - } - - class WeirdClass: JSONDecoder {} -} - -extension RuntimeTests { -//===----------------------------------------------------------------------===// -// Struct case -//===----------------------------------------------------------------------===// - - func testStructTypeMetadata() throws { - let meta = Metadata(TypeMetadataTests.Struct.self).type - - XCTAssertEqual(meta.descriptor.name, "Struct") - - let genericArg = meta.genericArguments.load(as: Any.Type.self) - - XCTAssert(genericArg == Int.self) - - let fieldType = meta.descriptor.fields[0].typeRef - XCTAssert(meta.resolve(fieldType) == Int.self) - } - -//===----------------------------------------------------------------------===// -// Enum case -//===----------------------------------------------------------------------===// - - func testEnumTypeMetadata() throws { - let meta = Metadata(TypeMetadataTests.Enum.self).type - - XCTAssertEqual(meta.descriptor.name, "Enum") - - let genericArg = meta.genericArguments.load(as: Any.Type.self) - - XCTAssert(genericArg == String.self) - - let caseType = meta.descriptor.fields[0].typeRef - XCTAssert(meta.resolve(caseType) == String.self) - } - -//===----------------------------------------------------------------------===// -// Class case -//===----------------------------------------------------------------------===// - - func testClassTypeMetadata() throws { - // Non resilient superclass - - let meta = Metadata(TypeMetadataTests.Class.self).type - - XCTAssertEqual(meta.descriptor.name, "Class") - - let genericArg = meta.genericArguments.load(as: Any.Type.self) - - XCTAssert(genericArg == Double.self) - - let fieldTy = meta.descriptor.fields[0].typeRef - XCTAssert(meta.resolve(fieldTy) == Double.self) - - // Resilient superclass - - let weirdMeta = Metadata(TypeMetadataTests.WeirdClass.self).type - - XCTAssertEqual(weirdMeta.descriptor.name, "WeirdClass") - - let weirdGenericArg = weirdMeta.genericArguments.load(as: Any.Type.self) - - XCTAssert(weirdGenericArg == Float.self) - } -} diff --git a/stdlib/public/Reflection/Tests/RuntimeTests/RuntimeTests.swift b/stdlib/public/Reflection/Tests/RuntimeTests/RuntimeTests.swift deleted file mode 100644 index 111cd69797096..0000000000000 --- a/stdlib/public/Reflection/Tests/RuntimeTests/RuntimeTests.swift +++ /dev/null @@ -1,3 +0,0 @@ -import XCTest - -final class RuntimeTests: XCTestCase {} diff --git a/test/stdlib/_Runtime/ContextDescriptor/StructDescriptor.swift b/test/stdlib/_Runtime/ContextDescriptor/StructDescriptor.swift new file mode 100644 index 0000000000000..18fc668ab83de --- /dev/null +++ b/test/stdlib/_Runtime/ContextDescriptor/StructDescriptor.swift @@ -0,0 +1,36 @@ +// RUN: %target-run-simple-swift +// REQUIRES: executable_test + +import StdlibUnittest +import _Runtime + +let suite = TestSuite("StructDescriptor") + +struct Dog { + let name: String + let age: Int +} + +struct GenericDog { + let one: T + let age: Int + let two: T +} + +if #available(SwiftStdlib 5.9, *) { + suite.test("Non-generic struct") { + let dog = Metadata(Dog.self).struct + let descriptor = dog.descriptor + + expectEqual(descriptor.base.name, "Dog") + } + + suite.test("Generic struct") { + let dog = Metadata(GenericDog.self).struct + let descriptor = dog.descriptor + + expectEqual(descriptor.base.name, "GenericDog") + } +} + +runAllTests() diff --git a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/FunctionMetadata.swift b/test/stdlib/_Runtime/Metadata/FunctionMetadata.swift similarity index 68% rename from stdlib/public/Reflection/Tests/RuntimeTests/Metadata/FunctionMetadata.swift rename to test/stdlib/_Runtime/Metadata/FunctionMetadata.swift index bf9cc1bf0acb9..f0fcfda70fe83 100644 --- a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/FunctionMetadata.swift +++ b/test/stdlib/_Runtime/Metadata/FunctionMetadata.swift @@ -1,120 +1,127 @@ -import XCTest +// RUN: %target-run-simple-swift +// REQUIRES: executable_test + +import StdlibUnittest import _Runtime -extension RuntimeTests { - func testFunctionMetadata() throws { +let suite = TestSuite("FunctionMetadata") + +if #available(SwiftStdlib 5.9, *) { + suite.test("Basic") { let int = Metadata(Int.self) let string = Metadata(String.self) let bool = Metadata(Bool.self) - + //===----------------------------------------------------------------------===// // General Queries //===----------------------------------------------------------------------===// - + let fn0 = Metadata(((Int, String) -> Bool).self).function - - XCTAssertFalse(fn0.throws) - XCTAssert(fn0.isEscaping) - XCTAssertFalse(fn0.isDifferential) - XCTAssertFalse(fn0.hasGlobalActor) - XCTAssertFalse(fn0.isAsync) - XCTAssertFalse(fn0.isSendable) - XCTAssertEqual(fn0.convention, .swift) - XCTAssertEqual(fn0.resultMetadata, bool) - XCTAssert(fn0.parameterMetadata.elementsEqual([int, string])) - XCTAssertEqual(fn0.differentiableKind, .nonDifferentiable) - + + expectFalse(fn0.throws) + expectTrue(fn0.isEscaping) + expectFalse(fn0.isDifferential) + expectFalse(fn0.hasGlobalActor) + expectFalse(fn0.isAsync) + expectFalse(fn0.isSendable) + expectEqual(fn0.convention, .swift) + expectEqual(fn0.resultMetadata, bool) + expectTrue(fn0.parameterMetadata.elementsEqual([int, string])) + expectEqual(fn0.differentiableKind, .nonDifferentiable) + //===----------------------------------------------------------------------===// // Throws //===----------------------------------------------------------------------===// - + let fn1 = Metadata((() throws -> ()).self).function - - XCTAssert(fn1.throws) - + + expectTrue(fn1.throws) + //===----------------------------------------------------------------------===// // Escaping //===----------------------------------------------------------------------===// - + // By default, closure arguments to functions are '@nonescaping', however // as a type by itself the function is implicitly '@escaping'. I'm unsure // of a way to spell a @nonescaping function type in Swift without just // using the mangling for it. This mangling is '@nonescaping () -> Int'. - + let fn2 = Metadata(_typeByName("SiyXE")!).function - - XCTAssertFalse(fn2.isEscaping) - + + expectFalse(fn2.isEscaping) + //===----------------------------------------------------------------------===// // Differentiable Kind //===----------------------------------------------------------------------===// - + // Mangling = '@differential(reverse) () -> Int' - + let fn3 = Metadata(_typeByName("SiyYjrc")!).function - - XCTAssert(fn3.isDifferential) - XCTAssertEqual(fn3.differentiableKind, .reverse) - + + expectTrue(fn3.isDifferential) + expectEqual(fn3.differentiableKind, .reverse) + // Mangling = '@differentiable(_forward) (Int) -> Int' - + let fn4 = Metadata(_typeByName("S2iYjfXE")!).function - - XCTAssert(fn4.isDifferential) - XCTAssertEqual(fn4.differentiableKind, .forward) - + + expectTrue(fn4.isDifferential) + expectEqual(fn4.differentiableKind, .forward) + // Mangling = '@differentiable (Int) -> Int' - + let fn5 = Metadata(_typeByName("S2iYjdXE")!).function - - XCTAssert(fn5.isDifferential) - XCTAssertEqual(fn5.differentiableKind, .normal) - + + expectTrue(fn5.isDifferential) + expectEqual(fn5.differentiableKind, .normal) + // Mangling = '@differentiable(_linear) (Int) -> Int' - + let fn6 = Metadata(_typeByName("S2iYjlXE")!).function - - XCTAssert(fn6.isDifferential) - XCTAssertEqual(fn6.differentiableKind, .linear) - + + expectTrue(fn6.isDifferential) + expectEqual(fn6.differentiableKind, .linear) + //===----------------------------------------------------------------------===// // Global Actor //===----------------------------------------------------------------------===// - + let fn7 = Metadata((@MainActor () -> Int).self).function - - XCTAssert(fn7.hasGlobalActor) - + + expectTrue(fn7.hasGlobalActor) + //===----------------------------------------------------------------------===// // Async //===----------------------------------------------------------------------===// - + let fn8 = Metadata((() async -> Int).self).function - - XCTAssert(fn8.isAsync) - + + expectTrue(fn8.isAsync) + //===----------------------------------------------------------------------===// // Sendable //===----------------------------------------------------------------------===// - + let fn9 = Metadata((@Sendable () -> Int).self).function - - XCTAssert(fn9.isSendable) - + + expectTrue(fn9.isSendable) + //===----------------------------------------------------------------------===// // Calling Convention //===----------------------------------------------------------------------===// - + let fn10 = Metadata((@convention(thin) () -> Int).self).function - - XCTAssertEqual(fn10.convention, .thin) - + + expectEqual(fn10.convention, .thin) + let fn11 = Metadata((@convention(c) () -> Int).self).function - - XCTAssertEqual(fn11.convention, .c) - + + expectEqual(fn11.convention, .c) + let fn12 = Metadata((@convention(block) () -> Int).self).function - - XCTAssertEqual(fn12.convention, .block) + + expectEqual(fn12.convention, .block) } } + +runAllTests() diff --git a/test/stdlib/_Runtime/Metadata/Metadata.swift b/test/stdlib/_Runtime/Metadata/Metadata.swift new file mode 100644 index 0000000000000..c40405d5730a2 --- /dev/null +++ b/test/stdlib/_Runtime/Metadata/Metadata.swift @@ -0,0 +1,47 @@ +// RUN: %target-run-simple-swift +// REQUIRES: executable_test + +import StdlibUnittest +import _Runtime + +let suite = TestSuite("Metadata") + +struct Dog {} + +enum Cat {} + +class Fish {} + +if #available(SwiftStdlib 5.9, *) { + suite.test("Basic") { + let structMetadata = Metadata(Dog.self) + let enumMetadata = Metadata(Cat.self) + let classMetadata = Metadata(Fish.self) + let existentialMetadata = Metadata((any Equatable).self) + let functionMetadata = Metadata((() -> ()).self) + let metatypeMetadata = Metadata(Int.Type.self) + let tupleMetadata = Metadata(Void.self) + +//===----------------------------------------------------------------------===// +// Kinds +//===----------------------------------------------------------------------===// + + expectEqual(structMetadata.kind, .struct) + expectEqual(enumMetadata.kind, .enum) + expectEqual(classMetadata.kind, .class) + expectEqual(existentialMetadata.kind, .existential) + expectEqual(functionMetadata.kind, .function) + expectEqual(metatypeMetadata.kind, .metatype) + expectEqual(tupleMetadata.kind, .tuple) + +//===----------------------------------------------------------------------===// +// Metadata Equality +//===----------------------------------------------------------------------===// + + expectEqual(structMetadata, structMetadata) + expectNotEqual(structMetadata, Metadata(Int.self)) + expectNotEqual(structMetadata, classMetadata) + } +} + +runAllTests() diff --git a/test/stdlib/_Runtime/Metadata/MetatypeMetadata.swift b/test/stdlib/_Runtime/Metadata/MetatypeMetadata.swift new file mode 100644 index 0000000000000..c19efbbabedb6 --- /dev/null +++ b/test/stdlib/_Runtime/Metadata/MetatypeMetadata.swift @@ -0,0 +1,18 @@ +// RUN: %target-run-simple-swift +// REQUIRES: executable_test + +import StdlibUnittest +import _Runtime + +let suite = TestSuite("MetatypeMetadata") + +if #available(SwiftStdlib 5.9, *) { + suite.test("Basic") { + let metatype = Metadata(Int.Type.self).metatype + let int = Metadata(Int.self) + + expectEqual(metatype.instanceMetadata, int) + } +} + +runAllTests() diff --git a/test/stdlib/_Runtime/Metadata/StructMetadata.swift b/test/stdlib/_Runtime/Metadata/StructMetadata.swift new file mode 100644 index 0000000000000..6666e4431d75e --- /dev/null +++ b/test/stdlib/_Runtime/Metadata/StructMetadata.swift @@ -0,0 +1,34 @@ +// RUN: %target-run-simple-swift +// REQUIRES: executable_test + +import StdlibUnittest +import _Runtime + +let suite = TestSuite("StructMetadata") + +struct Dog { + let name: String + let age: Int +} + +struct GenericDog { + let one: T + let age: Int + let two: T +} + +if #available(SwiftStdlib 5.9, *) { + suite.test("Non-generic struct") { + let dog = Metadata(Dog.self).struct + + expectTrue(dog.fieldOffsets.elementsEqual([0, 16])) + } + + suite.test("Generic struct") { + let genericDog = Metadata(GenericDog.self).struct + + expectTrue(genericDog.fieldOffsets.elementsEqual([0, 16, 24])) + } +} + +runAllTests() diff --git a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/TupleMetadata.swift b/test/stdlib/_Runtime/Metadata/TupleMetadata.swift similarity index 61% rename from stdlib/public/Reflection/Tests/RuntimeTests/Metadata/TupleMetadata.swift rename to test/stdlib/_Runtime/Metadata/TupleMetadata.swift index 2bd87a1306af1..ba289d79698c3 100644 --- a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/TupleMetadata.swift +++ b/test/stdlib/_Runtime/Metadata/TupleMetadata.swift @@ -1,47 +1,54 @@ -import XCTest +// RUN: %target-run-simple-swift +// REQUIRES: executable_test + +import StdlibUnittest import _Runtime -extension RuntimeTests { - func testTupleMetadata() throws { +let suite = TestSuite("TupleMetadata") + +if #available(SwiftStdlib 5.9, *) { + suite.test("Basic") { // Tuple - + let tuple = Metadata((name: String, age: Int, [Double], lastThing: UInt64).self).tuple - + let offsets = tuple.elements.map { $0.offset } - - XCTAssertEqual(offsets, [0, 16, 24, 32]) - + + expectEqual(offsets, [0, 16, 24, 32]) + let metadatas = tuple.elements.map { $0.metadata } - - XCTAssertEqual(metadatas, [ + + expectEqual(metadatas, [ Metadata(String.self), Metadata(Int.self), Metadata([Double].self), Metadata(UInt64.self) ]) - + let labels = tuple.elements.map { $0.label } - - XCTAssertEqual(labels, ["name", "age", "", "lastThing"]) - + + expectEqual(labels, ["name", "age", "", "lastThing"]) + // Tuple2 - + let tuple2 = Metadata((Int, String, Double).self).tuple - + let offsets2 = tuple2.elements.map { $0.offset } - - XCTAssertEqual(offsets2, [0, 8, 24]) - + + expectEqual(offsets2, [0, 8, 24]) + let metadatas2 = tuple2.elements.map { $0.metadata } - - XCTAssertEqual(metadatas2, [ + + expectEqual(metadatas2, [ Metadata(Int.self), Metadata(String.self), Metadata(Double.self) ]) - + let labels2 = tuple2.elements.map { $0.label } - - XCTAssertEqual(labels2, ["", "", ""]) + + expectEqual(labels2, ["", "", ""]) } } + +runAllTests() diff --git a/test/stdlib/_Runtime/Metadata/TypeMetadata.swift b/test/stdlib/_Runtime/Metadata/TypeMetadata.swift new file mode 100644 index 0000000000000..c6f298db72a92 --- /dev/null +++ b/test/stdlib/_Runtime/Metadata/TypeMetadata.swift @@ -0,0 +1,85 @@ +// RUN: %target-run-simple-swift +// REQUIRES: executable_test + +import StdlibUnittest +import _Runtime + +let suite = TestSuite("TypeMetadata") + +struct Struct { + let xyz: T +} + +enum Enum { + case generic(T) +} + +class Class { + let xyz: T + + init(xyz: T) { + self.xyz = xyz + } +} + +#if canImport(Foundation) +import Foundation + +class WeirdClass: JSONDecoder {} +#endif + +if #available(SwiftStdlib 5.9, *) { + suite.test("Struct") { + let meta = Metadata(Struct.self).type + + expectEqual(meta.descriptor.name, "Struct") + + let genericArg = meta.genericArguments.load(as: Any.Type.self) + + expectTrue(genericArg == Int.self) + + let fieldType = meta.descriptor.fields[0].typeRef + expectTrue(meta.resolve(fieldType) == Int.self) + } + + suite.test("Enum") { + let meta = Metadata(Enum.self).type + + expectEqual(meta.descriptor.name, "Enum") + + let genericArg = meta.genericArguments.load(as: Any.Type.self) + + expectTrue(genericArg == String.self) + + let caseType = meta.descriptor.fields[0].typeRef + expectTrue(meta.resolve(caseType) == String.self) + } + + suite.test("Class") { + // Non resilient superclass + + let meta = Metadata(Class.self).type + + expectEqual(meta.descriptor.name, "Class") + + let genericArg = meta.genericArguments.load(as: Any.Type.self) + + expectTrue(genericArg == Double.self) + + let fieldTy = meta.descriptor.fields[0].typeRef + expectTrue(meta.resolve(fieldTy) == Double.self) + + // Resilient superclass +#if canImport(Foundation) + let weirdMeta = Metadata(WeirdClass.self).type + + expectEqual(weirdMeta.descriptor.name, "WeirdClass") + + let weirdGenericArg = weirdMeta.genericArguments.load(as: Any.Type.self) + + expectTrue(weirdGenericArg == Float.self) +#endif + } +} + +runAllTests() diff --git a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/ValueWitnessTable.swift b/test/stdlib/_Runtime/Metadata/ValueWitnessTable.swift similarity index 60% rename from stdlib/public/Reflection/Tests/RuntimeTests/Metadata/ValueWitnessTable.swift rename to test/stdlib/_Runtime/Metadata/ValueWitnessTable.swift index dc8e555881928..de64660fb4c4a 100644 --- a/stdlib/public/Reflection/Tests/RuntimeTests/Metadata/ValueWitnessTable.swift +++ b/test/stdlib/_Runtime/Metadata/ValueWitnessTable.swift @@ -1,154 +1,155 @@ -import XCTest +// RUN: %target-run-simple-swift +// REQUIRES: executable_test + +import StdlibUnittest import _Runtime -enum ValueWitnessTableTests { - struct Dog: Equatable { - let name: String - let age: Int - } +let suite = TestSuite("ValueWitnessTable") + +struct Dog: Equatable { + let name: String + let age: Int } -extension RuntimeTests { - func testValueWitnessTable() throws { - let meta = Metadata(ValueWitnessTableTests.Dog.self).struct +if #available(SwiftStdlib 5.9, *) { + suite.test("Basic") { + let meta = Metadata(Dog.self).struct let vwt = meta.vwt - + //===----------------------------------------------------------------------===// // Property API //===----------------------------------------------------------------------===// - - XCTAssertEqual(vwt.size, 24) - XCTAssertEqual(vwt.stride, 24) - XCTAssertEqual(vwt.flags.alignment, 8) - XCTAssertEqual(vwt.flags.alignmentMask, 7) - XCTAssert(vwt.flags.isValueInline) - XCTAssertFalse(vwt.flags.isPOD) - XCTAssertFalse(vwt.flags.hasSpareBits) - XCTAssert(vwt.flags.isBitwiseTakable) - XCTAssertFalse(vwt.flags.hasEnumWitnesses) - XCTAssertFalse(vwt.flags.isIncomplete) - + + expectEqual(vwt.size, 24) + expectEqual(vwt.stride, 24) + expectEqual(vwt.flags.alignment, 8) + expectEqual(vwt.flags.alignmentMask, 7) + expectTrue(vwt.flags.isValueInline) + expectFalse(vwt.flags.isPOD) + expectFalse(vwt.flags.hasSpareBits) + expectTrue(vwt.flags.isBitwiseTakable) + expectFalse(vwt.flags.hasEnumWitnesses) + expectFalse(vwt.flags.isIncomplete) + //===----------------------------------------------------------------------===// // InitializeBufferWithCopyOfBuffer //===----------------------------------------------------------------------===// - - let anySopie: Any = ValueWitnessTableTests.Dog(name: "Sophie", age: 11) + + let anySopie: Any = Dog(name: "Sophie", age: 11) var anySophiev2 = AnyExistentialContainer( - type: ValueWitnessTableTests.Dog.self + type: Dog.self ) - + withUnsafePointer(to: anySopie) { let src = $0 - + anySophiev2.projectValue { let dest = $0 - + vwt.initializeBufferWithCopyOfBuffer(dest, src) } } - - let concreteSophie = anySopie as! ValueWitnessTableTests.Dog - let concreteSophiev2 = unsafeBitCast(anySophiev2, to: Any.self) as! ValueWitnessTableTests.Dog - - XCTAssertEqual(concreteSophie, concreteSophiev2) - + + let concreteSophie = anySopie as! Dog + let concreteSophiev2 = unsafeBitCast(anySophiev2, to: Any.self) as! Dog + + expectEqual(concreteSophie, concreteSophiev2) + //===----------------------------------------------------------------------===// // Destroy //===----------------------------------------------------------------------===// - + withUnsafeTemporaryAllocation( - of: ValueWitnessTableTests.Dog.self, + of: Dog.self, capacity: 1 ) { $0.initializeElement(at: 0, to: .init(name: "Sparky", age: 25)) - + vwt.destroy(UnsafeMutableRawPointer($0.baseAddress!)) } - + //===----------------------------------------------------------------------===// // InitializeWithCopy //===----------------------------------------------------------------------===// - - let sophie = ValueWitnessTableTests.Dog(name: "Sophie", age: 11) + + let sophie = Dog(name: "Sophie", age: 11) let sophiev2 = withUnsafeTemporaryAllocation( - of: ValueWitnessTableTests.Dog.self, + of: Dog.self, capacity: 1 ) { let destPtr = UnsafeMutableRawPointer($0.baseAddress!) - + withUnsafePointer(to: sophie) { let srcPtr = UnsafeRawPointer($0) - + vwt.initializeWithCopy(destPtr, srcPtr) } - + return $0[0] } - - XCTAssertEqual(sophie, sophiev2) - + + expectEqual(sophie, sophiev2) + //===----------------------------------------------------------------------===// // AssignWithCopy //===----------------------------------------------------------------------===// - - var assignSparky = ValueWitnessTableTests.Dog(name: "Sparky", age: 25) - + + var assignSparky = Dog(name: "Sparky", age: 25) + withUnsafePointer(to: sophie) { let src = UnsafeRawPointer($0) - + withUnsafeMutablePointer(to: &assignSparky) { let dest = UnsafeMutableRawPointer($0) - + vwt.assignWithCopy(dest, src) } } - - XCTAssertEqual(sophie, assignSparky) - + + expectEqual(sophie, assignSparky) + //===----------------------------------------------------------------------===// // InitializeWithTake //===----------------------------------------------------------------------===// - - var takenSophie = ValueWitnessTableTests.Dog(name: "Sophie", age: 11) - + + var takenSophie = Dog(name: "Sophie", age: 11) + let newSophie = withUnsafeTemporaryAllocation( - of: ValueWitnessTableTests.Dog.self, + of: Dog.self, capacity: 1 ) { let dest = UnsafeMutableRawPointer($0.baseAddress!) - + withUnsafeMutablePointer(to: &takenSophie) { let src = UnsafeMutableRawPointer($0) - + vwt.initializeWithTake(dest, src) } - + return $0[0] } - - XCTAssertEqual(newSophie, sophie) - + + expectEqual(newSophie, sophie) + //===----------------------------------------------------------------------===// // AssignWithTake //===----------------------------------------------------------------------===// - - takenSophie = ValueWitnessTableTests.Dog(name: "Sophie", age: 11) - assignSparky = ValueWitnessTableTests.Dog(name: "Sparky", age: 25) - + + takenSophie = Dog(name: "Sophie", age: 11) + assignSparky = Dog(name: "Sparky", age: 25) + withUnsafeMutablePointer(to: &takenSophie) { let src = $0 - + withUnsafeMutablePointer(to: &assignSparky) { let dest = $0 - + vwt.assignWithTake(dest, src) } } - - XCTAssertEqual(assignSparky, sophie) - } - - func testEnumValueWitnessTable() throws { - + + expectEqual(assignSparky, sophie) } } + +runAllTests() From c085f5afd9257e92fbd14495d559e1786d1240e9 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 8 Feb 2023 18:08:33 -0800 Subject: [PATCH 56/98] Record original source range of generated source buffers into dumped files This helps us establish where the code from a generated source buffer would go, for external tools that don't understand serialized diagnostics. --- include/swift/Basic/SourceManager.h | 1 + lib/Basic/SourceLoc.cpp | 40 +++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/include/swift/Basic/SourceManager.h b/include/swift/Basic/SourceManager.h index a0468ba6947c4..6094baafdc27c 100644 --- a/include/swift/Basic/SourceManager.h +++ b/include/swift/Basic/SourceManager.h @@ -129,6 +129,7 @@ class SourceManager { SourceManager(llvm::IntrusiveRefCntPtr FS = llvm::vfs::getRealFileSystem()) : FileSystem(FS) {} + ~SourceManager(); llvm::SourceMgr &getLLVMSourceMgr() { return LLVMSourceMgr; diff --git a/lib/Basic/SourceLoc.cpp b/lib/Basic/SourceLoc.cpp index 2fb59857603f1..cf03131f56478 100644 --- a/lib/Basic/SourceLoc.cpp +++ b/lib/Basic/SourceLoc.cpp @@ -212,10 +212,18 @@ SourceManager::getIDForBufferIdentifier(StringRef BufIdentifier) const { return It->second; } +SourceManager::~SourceManager() { + for (auto &generated : GeneratedSourceInfos) { + free((void*)generated.second.onDiskBufferCopyFileName.data()); + } +} + /// Dump the contents of the given memory buffer to a file, returning the /// name of that file (when successful) and \c None otherwise. static Optional -dumpBufferToFile(const llvm::MemoryBuffer *buffer) { +dumpBufferToFile(const llvm::MemoryBuffer *buffer, + const SourceManager &sourceMgr, + SourceRange originalSourceRange) { // Create file in the system temporary directory. SmallString<128> outputFileName; llvm::sys::path::system_temp_directory(true, outputFileName); @@ -232,9 +240,30 @@ dumpBufferToFile(const llvm::MemoryBuffer *buffer) { auto contents = buffer->getBuffer(); out << contents; - // Make sure we have a trailing newline. - if (contents.empty() || contents.back() != '\n') - out << "\n"; + // Make sure we have a trailing newline. + if (contents.empty() || contents.back() != '\n') + out << "\n"; + + // If we know the source range this comes from, append it later in + // the file so one can trace. + if (originalSourceRange.isValid()) { + out << "\n"; + + auto originalFilename = + sourceMgr.getDisplayNameForLoc(originalSourceRange.Start, true); + unsigned startLine, startColumn, endLine, endColumn; + std::tie(startLine, startColumn) = + sourceMgr.getPresumedLineAndColumnForLoc( + originalSourceRange.Start); + std::tie(endLine, endColumn) = + sourceMgr.getPresumedLineAndColumnForLoc( + originalSourceRange.End); + out << "// original-source-range: " + << originalFilename + << ":" << startLine << ":" << startColumn + << "-" << endLine << ":" << endColumn + << "\n"; + } }); if (ec) return None; @@ -258,7 +287,8 @@ StringRef SourceManager::getIdentifierForBuffer( return buffer->getBufferIdentifier(); if (generatedInfo->onDiskBufferCopyFileName.empty()) { - if (auto newFileNameOpt = dumpBufferToFile(buffer)) { + if (auto newFileNameOpt = dumpBufferToFile( + buffer, *this, generatedInfo->originalSourceRange)) { generatedInfo->onDiskBufferCopyFileName = strdup(newFileNameOpt->c_str()); } From ae9e3a4976b59e6028b8d5cf47985c6945db6585 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 6 Feb 2023 10:38:57 +0100 Subject: [PATCH 57/98] Swift SIL: fix base class of MarkUninitializedInst --- SwiftCompilerSources/Sources/SIL/Instruction.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index 1aa4b99aa693c..fb1bf53f50b58 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -278,7 +278,7 @@ final public class DeallocStackRefInst : Instruction, UnaryInstruction { public var allocRef: AllocRefInstBase { operand as! AllocRefInstBase } } -final public class MarkUninitializedInst : Instruction, UnaryInstruction { +final public class MarkUninitializedInst : SingleValueInstruction, UnaryInstruction { } final public class CondFailInst : Instruction, UnaryInstruction { From 713f6c39462431ee76d1bf97e64d319d65315eec Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 3 Feb 2023 19:03:20 +0100 Subject: [PATCH 58/98] Swift SIL: add `Type.nominal` and `Type.isOrContainsObjectiveCClass` APIs --- SwiftCompilerSources/Sources/SIL/Type.swift | 14 ++++++++++++++ include/swift/SIL/SILBridging.h | 4 ++++ lib/SIL/Utils/SILBridging.cpp | 16 ++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/SwiftCompilerSources/Sources/SIL/Type.swift b/SwiftCompilerSources/Sources/SIL/Type.swift index da2de3602d4ed..3eb9dc26035b3 100644 --- a/SwiftCompilerSources/Sources/SIL/Type.swift +++ b/SwiftCompilerSources/Sources/SIL/Type.swift @@ -41,6 +41,11 @@ public struct Type : CustomStringConvertible, NoReflectionChildren { public var isEnum: Bool { SILType_isEnum(bridged) != 0 } public var isFunction: Bool { SILType_isFunction(bridged) } + /// Can only be used if the type is in fact a nominal type (`isNominal` is true). + public var nominal: Decl { Decl(bridged: SILType_getNominal(bridged)) } + + public var isOrContainsObjectiveCClass: Bool { SILType_isOrContainsObjectiveCClass(bridged) } + public var tupleElements: TupleElementArray { TupleElementArray(type: self) } public func getNominalFields(in function: Function) -> NominalFieldsArray { @@ -104,3 +109,12 @@ public struct TupleElementArray : RandomAccessCollection, FormattedLikeArray { extension BridgedType { var type: Type { Type(bridged: self) } } + +// TODO: use an AST type for this once we have it +public struct Decl : Equatable { + let bridged: BridgedDecl + + public static func ==(lhs: Decl, rhs: Decl) -> Bool { + lhs.bridged == rhs.bridged + } +} diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index b46fb36a0810e..09b57f34efbb5 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -197,6 +197,8 @@ typedef enum { // AST bridging +typedef void * _Nonnull BridgedDecl; + struct BridgedEffectInfo { SwiftInt argumentIndex; bool isDerived; @@ -325,6 +327,8 @@ SwiftInt SILType_isStruct(BridgedType type); SwiftInt SILType_isTuple(BridgedType type); SwiftInt SILType_isEnum(BridgedType type); bool SILType_isFunction(BridgedType type); +BridgedDecl SILType_getNominal(BridgedType type); +bool SILType_isOrContainsObjectiveCClass(BridgedType type); bool SILType_isCalleeConsumedFunction(BridgedType type); SwiftInt SILType_getNumTupleElements(BridgedType type); BridgedType SILType_getTupleElementType(BridgedType type, SwiftInt elementIdx); diff --git a/lib/SIL/Utils/SILBridging.cpp b/lib/SIL/Utils/SILBridging.cpp index 21fe00a004100..2460381b5dbc9 100644 --- a/lib/SIL/Utils/SILBridging.cpp +++ b/lib/SIL/Utils/SILBridging.cpp @@ -541,6 +541,22 @@ bool SILType_isFunction(BridgedType type) { return castToSILType(type).is(); } +BridgedDecl SILType_getNominal(BridgedType type) { + return castToSILType(type).getNominalOrBoundGenericNominal(); +} + +bool SILType_isOrContainsObjectiveCClass(BridgedType type) { + return castToSILType(type).getASTType().findIf([](Type ty) { + if (ClassDecl *cd = ty->getClassOrBoundGenericClass()) { + if (cd->isForeign() || cd->getObjectModel() == ReferenceCounting::ObjC) + return true; + } + if (ty->is()) + return true; + return false; + }); +} + bool SILType_isCalleeConsumedFunction(BridgedType type) { auto funcTy = castToSILType(type).castTo(); return funcTy->isCalleeConsumed() && !funcTy->isNoEscape(); From b1c6ae60cd6d0f03dab66a971e2254986062a074 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 7 Feb 2023 15:41:00 +0100 Subject: [PATCH 59/98] Swift SIL: add metatype APIs to `Type` * `var isMetatype: Bool` * `var instanceTypeOfMetatype: Type` --- SwiftCompilerSources/Sources/SIL/Type.swift | 3 +++ include/swift/SIL/SILBridging.h | 2 ++ lib/SIL/Utils/SILBridging.cpp | 10 ++++++++++ 3 files changed, 15 insertions(+) diff --git a/SwiftCompilerSources/Sources/SIL/Type.swift b/SwiftCompilerSources/Sources/SIL/Type.swift index 3eb9dc26035b3..f5b5fcfa9e97e 100644 --- a/SwiftCompilerSources/Sources/SIL/Type.swift +++ b/SwiftCompilerSources/Sources/SIL/Type.swift @@ -40,6 +40,7 @@ public struct Type : CustomStringConvertible, NoReflectionChildren { public var isTuple: Bool { SILType_isTuple(bridged) != 0 } public var isEnum: Bool { SILType_isEnum(bridged) != 0 } public var isFunction: Bool { SILType_isFunction(bridged) } + public var isMetatype: Bool { SILType_isMetatype(bridged) } /// Can only be used if the type is in fact a nominal type (`isNominal` is true). public var nominal: Decl { Decl(bridged: SILType_getNominal(bridged)) } @@ -52,6 +53,8 @@ public struct Type : CustomStringConvertible, NoReflectionChildren { NominalFieldsArray(type: self, function: function) } + public var instanceTypeOfMetatype: Type { SILType_instanceTypeOfMetatype(bridged).type } + public var isCalleeConsumedFunction: Bool { SILType_isCalleeConsumedFunction(bridged) } public func getIndexOfEnumCase(withName name: String) -> Int? { diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index 09b57f34efbb5..52db03298686b 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -327,6 +327,8 @@ SwiftInt SILType_isStruct(BridgedType type); SwiftInt SILType_isTuple(BridgedType type); SwiftInt SILType_isEnum(BridgedType type); bool SILType_isFunction(BridgedType type); +bool SILType_isMetatype(BridgedType type); +BridgedType SILType_instanceTypeOfMetatype(BridgedType type); BridgedDecl SILType_getNominal(BridgedType type); bool SILType_isOrContainsObjectiveCClass(BridgedType type); bool SILType_isCalleeConsumedFunction(BridgedType type); diff --git a/lib/SIL/Utils/SILBridging.cpp b/lib/SIL/Utils/SILBridging.cpp index 2460381b5dbc9..6af49dfc39b0d 100644 --- a/lib/SIL/Utils/SILBridging.cpp +++ b/lib/SIL/Utils/SILBridging.cpp @@ -541,6 +541,16 @@ bool SILType_isFunction(BridgedType type) { return castToSILType(type).is(); } +bool SILType_isMetatype(BridgedType type) { + return castToSILType(type).is(); +} + +BridgedType SILType_instanceTypeOfMetatype(BridgedType type) { + auto metaType = castToSILType(type).castTo(); + SILType instanceTy = SILType::getPrimitiveObjectType(metaType.getInstanceType()); + return {instanceTy.getOpaqueValue()}; +} + BridgedDecl SILType_getNominal(BridgedType type) { return castToSILType(type).getNominalOrBoundGenericNominal(); } From 1c1d17e18b1bb574450bad128ce774e9aef57a60 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 3 Feb 2023 20:51:50 +0100 Subject: [PATCH 60/98] Swift Optimizer: add some small optimization utilities * `Instruction.isTriviallyDead` and `Instruction.isTriviallyDeadIgnoringDebugUses` * `Instruction.hasSameDebugLocationAsPreviousOrNextInstruction` * `UseList.singleNonDebugUse` * `UseList.isEmptyIgnoringDebugUses` * `removeDeadBlocks` * `FunctionPassContext.removeTriviallyDeadInstructionsPreservingDebugInfo` and `FunctionPassContext.removeTriviallyDeadInstructionsIgnoringDebugUses` * `BasicBlock.dropAllReferences` * `SimplifyContext.tryReplaceRedundantInstructionPair` --- .../Optimizer/Utilities/OptUtils.swift | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift index ab53c58171ab9..b0a0cc20108d3 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift @@ -78,6 +78,138 @@ extension Value { } } +private extension Instruction { + var isTriviallyDead: Bool { + if results.contains(where: { !$0.uses.isEmpty }) { + return false + } + return self.canBeRemovedIfNotUsed + } + + var isTriviallyDeadIgnoringDebugUses: Bool { + if results.contains(where: { !$0.uses.isEmptyIgnoringDebugUses }) { + return false + } + return self.canBeRemovedIfNotUsed + } + + private var canBeRemovedIfNotUsed: Bool { + // TODO: it is horrible to hard-code exceptions here, but currently there is no Instruction API for this. + switch self { + case is TermInst, is MarkUninitializedInst, is DebugValueInst: + return false + case let bi as BuiltinInst: + if bi.id == .OnFastPath { + return false + } + default: + break + } + return !mayReadOrWriteMemory && !hasUnspecifiedSideEffects + } +} + +extension UseList { + var singleNonDebugUse: Operand? { + var singleUse: Operand? + for use in self { + if use.instruction is DebugValueInst { + continue + } + if singleUse != nil { + return nil + } + singleUse = use + } + return singleUse + } + + var isEmptyIgnoringDebugUses: Bool { + for use in self { + if !(use.instruction is DebugValueInst) { + return false + } + } + return true + } +} + +extension FunctionPassContext { + /// Returns true if any blocks were removed. + func removeDeadBlocks(in function: Function) -> Bool { + var reachableBlocks = ReachableBlocks(function: function, self) + defer { reachableBlocks.deinitialize() } + + var blocksRemoved = false + for block in function.blocks { + if !reachableBlocks.isReachable(block: block) { + block.dropAllReferences(self) + erase(block: block) + blocksRemoved = true + } + } + return blocksRemoved + } + + func removeTriviallyDeadInstructionsPreservingDebugInfo(in function: Function) { + for inst in function.reversedInstructions { + if inst.isTriviallyDead { + erase(instruction: inst) + } + } + } + + func removeTriviallyDeadInstructionsIgnoringDebugUses(in function: Function) { + for inst in function.reversedInstructions { + if inst.isTriviallyDeadIgnoringDebugUses { + erase(instructionIncludingDebugUses: inst) + } + } + } +} + +extension BasicBlock { + func dropAllReferences(_ context: FunctionPassContext) { + for arg in arguments { + arg.uses.replaceAll(with: Undef.get(type: arg.type, context), context) + } + for inst in instructions.reversed() { + for result in inst.results { + result.uses.replaceAll(with: Undef.get(type: result.type, context), context) + } + context.erase(instruction: inst) + } + } +} + +extension SimplifyContext { + + /// Replaces a pair of redudant instructions, like + /// ``` + /// %first = enum $E, #E.CaseA!enumelt, %replacement + /// %second = unchecked_enum_data %first : $E, #E.CaseA!enumelt + /// ``` + /// Replaces `%second` with `%replacement` and deletes the instructions if possible - or required. + /// The operation is not done if it would require to insert a copy due to keep ownership correct. + func tryReplaceRedundantInstructionPair(first: SingleValueInstruction, second: SingleValueInstruction, + with replacement: Value) { + let singleUse = preserveDebugInfo ? first.uses.singleUse : first.uses.singleNonDebugUse + let canEraseFirst = singleUse?.instruction == second + + if !canEraseFirst && first.parentFunction.hasOwnership && replacement.ownership == .owned { + // We cannot add more uses to `replacement` without inserting a copy. + return + } + + second.uses.replaceAll(with: replacement, self) + erase(instruction: second) + + if canEraseFirst { + erase(instructionIncludingDebugUses: first) + } + } +} + extension ProjectedValue { /// Returns true if the address can alias with `rhs`. /// From 67ed6cfff84c08aad99b654a5bc9c5e6322b1870 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 3 Feb 2023 20:53:38 +0100 Subject: [PATCH 61/98] Swift Optimizer: add Simplification passes Those passes are a framework for instruction simplifications (which are not yet included in this commit). Comparable to SILCombine --- .../Optimizer/FunctionPasses/CMakeLists.txt | 1 + .../FunctionPasses/SimplificationPasses.swift | 137 ++++++++++++++++++ .../PassManager/PassRegistration.swift | 3 + .../swift/SILOptimizer/PassManager/Passes.def | 8 +- 4 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 SwiftCompilerSources/Sources/Optimizer/FunctionPasses/SimplificationPasses.swift diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt index 8d5d67a98ea96..f60a5bf23d337 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt @@ -13,5 +13,6 @@ swift_compiler_sources(Optimizer ObjCBridgingOptimization.swift MergeCondFails.swift ReleaseDevirtualizer.swift + SimplificationPasses.swift StackPromotion.swift ) diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/SimplificationPasses.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/SimplificationPasses.swift new file mode 100644 index 0000000000000..0afcffa020d67 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/SimplificationPasses.swift @@ -0,0 +1,137 @@ +//===--- SimplificationPasses.swift ----------------------------------------==// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SIL + +//===--------------------------------------------------------------------===// +// Instruction protocols +//===--------------------------------------------------------------------===// + +/// Instructions which can be simplified at all optimization levels +protocol Simplifyable : Instruction { + func simplify(_ context: SimplifyContext) +} + +/// Instructions which can be simplified at -Onone +protocol OnoneSimplifyable : Simplifyable { +} + +/// Instructions which can only be simplified at the end of the -Onone pipeline +protocol LateOnoneSimplifyable : Instruction { + func simplifyLate(_ context: SimplifyContext) +} + +//===--------------------------------------------------------------------===// +// Simplification passes +//===--------------------------------------------------------------------===// + +let ononeSimplificationPass = FunctionPass(name: "onone-simplification") { + (function: Function, context: FunctionPassContext) in + + runSimplification(on: function, context, preserveDebugInfo: true) { + if let i = $0 as? OnoneSimplifyable { + i.simplify($1) + } + } +} + +let simplificationPass = FunctionPass(name: "simplification") { + (function: Function, context: FunctionPassContext) in + + runSimplification(on: function, context, preserveDebugInfo: false) { + if let i = $0 as? Simplifyable { + i.simplify($1) + } + } +} + +let lateOnoneSimplificationPass = FunctionPass(name: "late-onone-simplification") { + (function: Function, context: FunctionPassContext) in + + runSimplification(on: function, context, preserveDebugInfo: true) { + if let i = $0 as? LateOnoneSimplifyable { + i.simplifyLate($1) + } else if let i = $0 as? OnoneSimplifyable { + i.simplify($1) + } + } +} + +//===--------------------------------------------------------------------===// +// Pass implementation +//===--------------------------------------------------------------------===// + + +private func runSimplification(on function: Function, _ context: FunctionPassContext, + preserveDebugInfo: Bool, + _ simplify: (Instruction, SimplifyContext) -> ()) { + var worklist = InstructionWorklist(context) + defer { worklist.deinitialize() } + + let simplifyCtxt = context.createSimplifyContext(preserveDebugInfo: preserveDebugInfo, + notifyInstructionChanged: { + worklist.pushIfNotVisited($0) + }) + + // Push in reverse order so that popping from the tail of the worklist visits instruction in forward order again. + worklist.pushIfNotVisited(contentsOf: function.reversedInstructions) + + // Run multiple iterations because cleanupDeadCode can add new candidates to the worklist. + repeat { + + // The core worklist-loop. + while let instruction = worklist.popAndForget() { + if instruction.isDeleted { + continue + } + if !context.options.enableSimplification(for: instruction) { + continue + } + if !context.continueWithNextSubpassRun(for: instruction) { + return + } + simplify(instruction, simplifyCtxt) + } + + cleanupDeadInstructions(in: function, preserveDebugInfo, context) + cleanupDeadBlocks(in: function, pushNewCandidatesTo: &worklist, context) + + } while !worklist.isEmpty + + if context.needFixStackNesting { + function.fixStackNesting(context) + } +} + +private func cleanupDeadInstructions(in function: Function, + _ preserveDebugInfo: Bool, + _ context: FunctionPassContext) { + if preserveDebugInfo { + context.removeTriviallyDeadInstructionsPreservingDebugInfo(in: function) + } else { + context.removeTriviallyDeadInstructionsIgnoringDebugUses(in: function) + } +} + +private func cleanupDeadBlocks(in function: Function, + pushNewCandidatesTo worklist: inout InstructionWorklist, + _ context: FunctionPassContext) { + if context.removeDeadBlocks(in: function) { + // After deleting dead blocks their (still alive) successor blocks may become eligible for block merging. + // Therefore we re-run simplification for all branch instructions. + for block in function.blocks.reversed() { + if let bi = block.terminator as? BranchInst { + worklist.pushIfNotVisited(bi) + } + } + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift index c04540a1e9d3f..69cb6d5813c90 100644 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift @@ -72,6 +72,9 @@ private func registerSwiftPasses() { registerPass(functionStackProtection, { functionStackProtection.run($0) }) registerPass(assumeSingleThreadedPass, { assumeSingleThreadedPass.run($0) }) registerPass(releaseDevirtualizerPass, { releaseDevirtualizerPass.run($0) }) + registerPass(simplificationPass, { simplificationPass.run($0) }) + registerPass(ononeSimplificationPass, { ononeSimplificationPass.run($0) }) + registerPass(lateOnoneSimplificationPass, { lateOnoneSimplificationPass.run($0) }) // Instruction passes registerForSILCombine(BeginCOWMutationInst.self, { run(BeginCOWMutationInst.self, $0) }) diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index ba15a389a447a..f46d5f6b7d193 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -389,8 +389,12 @@ PASS(SROA, "sroa", "Scalar Replacement of Aggregate Stack Objects") PASS(SROABBArgs, "sroa-bb-args", "Scalar Replacement of Aggregate SIL Block Arguments") -SWIFT_FUNCTION_PASS(Simplify, "simplify", - "Simplify instructions via peephole optimizations") +SWIFT_FUNCTION_PASS(Simplification, "simplification", + "Peephole simplifications") +SWIFT_FUNCTION_PASS(OnoneSimplification, "onone-simplification", + "Peephole simplifications which runs at -Onone") +SWIFT_FUNCTION_PASS(LateOnoneSimplification, "late-onone-simplification", + "Peephole simplifications which can only run late in the -Onone pipeline") PASS(SimplifyBBArgs, "simplify-bb-args", "SIL Block Argument Simplification") PASS(SimplifyCFG, "simplify-cfg", From db103930a294db1e010b38e45837afda58317232 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 3 Feb 2023 20:53:55 +0100 Subject: [PATCH 62/98] Passes.def: fix an outdated comment --- include/swift/SILOptimizer/PassManager/Passes.def | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index f46d5f6b7d193..a2a1be0cdc4b6 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -65,7 +65,7 @@ #define SWIFT_FUNCTION_PASS(Id, Tag, Description) PASS(Id, Tag, Description) #endif -/// SWIFT_SILCOMBINE_PASS(Inst, Tag) +/// SWIFT_SILCOMBINE_PASS(Inst) /// Similar to SWIFT_FUNCTION_PASS, but defines an instruction pass which is /// implemented in swift and is run by the SILCombiner. /// The \p Inst argument specifies the instruction class. From fc6f1d862e91564b3779a708134ff0cbda68cffb Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Fri, 3 Feb 2023 20:55:06 +0100 Subject: [PATCH 63/98] stdlib: make type comparison functions transparent This is needed to be able to optimize them at Onone --- stdlib/public/core/Builtin.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/Builtin.swift b/stdlib/public/core/Builtin.swift index c651fb30f3f1f..009b578eccf2b 100644 --- a/stdlib/public/core/Builtin.swift +++ b/stdlib/public/core/Builtin.swift @@ -139,7 +139,7 @@ internal func != (lhs: Builtin.RawPointer, rhs: Builtin.RawPointer) -> Bool { /// - t1: Another type to compare. /// - Returns: `true` if both `t0` and `t1` are `nil` or if they represent the /// same type; otherwise, `false`. -@inlinable +@inlinable @_transparent public func == (t0: Any.Type?, t1: Any.Type?) -> Bool { switch (t0, t1) { case (.none, .none): return true @@ -156,7 +156,7 @@ public func == (t0: Any.Type?, t1: Any.Type?) -> Bool { /// - t1: Another type to compare. /// - Returns: `true` if one, but not both, of `t0` and `t1` are `nil`, or if /// they represent different types; otherwise, `false`. -@inlinable +@inlinable @_transparent public func != (t0: Any.Type?, t1: Any.Type?) -> Bool { return !(t0 == t1) } From 0ff8663f532516d8dd2a4caea3fcc849ea113e7d Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 7 Feb 2023 15:42:02 +0100 Subject: [PATCH 64/98] SILLocation: fix location comparison for "FilenameAndLocation" locations Those are locations which are parsed form SIL files --- include/swift/SIL/SILLocation.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/include/swift/SIL/SILLocation.h b/include/swift/SIL/SILLocation.h index 0796d5a4eb13e..40edbb171e945 100644 --- a/include/swift/SIL/SILLocation.h +++ b/include/swift/SIL/SILLocation.h @@ -434,8 +434,15 @@ class SILLocation { void print(raw_ostream &OS) const; inline bool operator==(const SILLocation& R) const { - return kindAndFlags.packedKindAndFlags == R.kindAndFlags.packedKindAndFlags - && storage.filePositionLoc == R.storage.filePositionLoc; + if (kindAndFlags.packedKindAndFlags != R.kindAndFlags.packedKindAndFlags) + return false; + + if (isFilenameAndLocation()) { + assert(R.isFilenameAndLocation()); + return *getFilenameAndLocation() == *R.getFilenameAndLocation(); + } + + return storage.filePositionLoc == R.storage.filePositionLoc; } inline bool operator!=(const SILLocation &R) const { return !(*this == R); } From e92f27c01a951743783b2ad502040ac24188b391 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 8 Feb 2023 18:06:23 +0100 Subject: [PATCH 65/98] Swift SIL: add some APIs to `Location` * `hasValidLineNumber` * `isAutoGenerated` * `isDebugSteppable` --- SwiftCompilerSources/Sources/SIL/Location.swift | 5 +++++ include/swift/SIL/SILLocation.h | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/SwiftCompilerSources/Sources/SIL/Location.swift b/SwiftCompilerSources/Sources/SIL/Location.swift index bc6e72058fbfe..f0d95d25216d9 100644 --- a/SwiftCompilerSources/Sources/SIL/Location.swift +++ b/SwiftCompilerSources/Sources/SIL/Location.swift @@ -25,6 +25,11 @@ public struct Location: Equatable, CustomStringConvertible { Location(bridged: SILLocation_getAutogeneratedLocation(bridged)) } + public var hasValidLineNumber: Bool { bridged.hasValidLineNumber() } + public var isAutoGenerated: Bool { bridged.isAutoGenerated() } + + public var isDebugSteppable: Bool { hasValidLineNumber && !isAutoGenerated } + public static func ==(lhs: Location, rhs: Location) -> Bool { SILLocation_equal(lhs.bridged, rhs.bridged) } diff --git a/include/swift/SIL/SILLocation.h b/include/swift/SIL/SILLocation.h index 40edbb171e945..fa6bfeade991e 100644 --- a/include/swift/SIL/SILLocation.h +++ b/include/swift/SIL/SILLocation.h @@ -307,6 +307,14 @@ class SILLocation { } explicit operator bool() const { return !isNull(); } + bool hasValidLineNumber() const { + if (isNull()) + return false; + if (isFilenameAndLocation() && getFilenameAndLocation()->line == 0) + return false; + return true; + } + /// Return true if this location is backed by an AST node. bool isASTNode() const { switch (getStorageKind()) { @@ -691,6 +699,8 @@ class SILDebugLocation { : debugScope(debugScope), location(location) {} SILLocation getLocation() const { return location; } const SILDebugScope *getScope() const { return debugScope; } + bool hasValidLineNumber() const { return location.hasValidLineNumber(); } + bool isAutoGenerated() const { return location.isAutoGenerated(); } operator bool() const { return bool(location) && debugScope; } }; From cef6ef9a84c826b2791622ab5a51b66f52eb2b63 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 8 Feb 2023 17:37:38 +0100 Subject: [PATCH 66/98] SIL: add a debug_step instruction This instruction can be inserted by Onone optimizations as a replacement for deleted instructions to ensure that it's possible to single step on its location. --- SwiftCompilerSources/Sources/SIL/Builder.swift | 5 +++++ .../Sources/SIL/Instruction.swift | 2 ++ .../Sources/SIL/Registration.swift | 1 + docs/SIL.rst | 14 ++++++++++++++ include/swift/SIL/SILBridging.h | 1 + include/swift/SIL/SILBuilder.h | 4 ++++ include/swift/SIL/SILCloner.h | 5 +++++ include/swift/SIL/SILInstruction.h | 13 +++++++++++++ include/swift/SIL/SILNodes.def | 2 ++ lib/IRGen/IRGenSIL.cpp | 12 ++++++++++++ lib/SIL/IR/OperandOwnership.cpp | 1 + lib/SIL/IR/SILPrinter.cpp | 4 ++++ lib/SIL/Parser/ParseSIL.cpp | 6 ++++++ lib/SIL/Utils/InstructionUtils.cpp | 1 + lib/SIL/Utils/SILBridging.cpp | 6 ++++++ .../UtilityPasses/SerializeSILPass.cpp | 1 + lib/SILOptimizer/Utils/SILInliner.cpp | 1 + lib/Serialization/DeserializeSIL.cpp | 1 + lib/Serialization/ModuleFormat.h | 2 +- lib/Serialization/SerializeSIL.cpp | 5 +++-- test/IRGen/debug_step.sil | 16 ++++++++++++++++ test/SIL/Parser/basic.sil | 10 ++++++++++ 22 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 test/IRGen/debug_step.sil diff --git a/SwiftCompilerSources/Sources/SIL/Builder.swift b/SwiftCompilerSources/Sources/SIL/Builder.swift index 3d290304d82f3..f43e830b03b10 100644 --- a/SwiftCompilerSources/Sources/SIL/Builder.swift +++ b/SwiftCompilerSources/Sources/SIL/Builder.swift @@ -134,6 +134,11 @@ public struct Builder { return notifyNew(SILBuilder_createDestroyValue(bridged, operand.bridged).getAs(DestroyValueInst.self)) } + @discardableResult + public func createDebugStep() -> DebugStepInst { + return notifyNew(SILBuilder_createDebugStep(bridged).getAs(DebugStepInst.self)) + } + @discardableResult public func createApply( function: Value, diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index fb1bf53f50b58..9004a97a0b1c0 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -291,6 +291,8 @@ final public class FixLifetimeInst : Instruction, UnaryInstruction {} final public class DebugValueInst : Instruction, UnaryInstruction {} +final public class DebugStepInst : Instruction {} + final public class UnconditionalCheckedCastAddrInst : Instruction { public override var mayTrap: Bool { true } } diff --git a/SwiftCompilerSources/Sources/SIL/Registration.swift b/SwiftCompilerSources/Sources/SIL/Registration.swift index 8902ee8c7139d..39193380a9a70 100644 --- a/SwiftCompilerSources/Sources/SIL/Registration.swift +++ b/SwiftCompilerSources/Sources/SIL/Registration.swift @@ -50,6 +50,7 @@ public func registerSILClasses() { register(MarkUninitializedInst.self) register(FixLifetimeInst.self) register(DebugValueInst.self) + register(DebugStepInst.self) register(UnconditionalCheckedCastAddrInst.self) register(SetDeallocatingInst.self) register(EndApplyInst.self) diff --git a/docs/SIL.rst b/docs/SIL.rst index e446761c3b10c..b124924f0d74e 100644 --- a/docs/SIL.rst +++ b/docs/SIL.rst @@ -3952,6 +3952,20 @@ SIL DIExpression can have elements with various types, like AST nodes or strings The ``[trace]`` flag is available for compiler unit testing. It is not produced during normal compilation. It is used combination with internal logging and optimization controls to select specific values to trace or to transform. For example, liveness analysis combines all "traced" values into a single live range with multiple definitions. This exposes corner cases that cannot be represented by passing valid SIL through the pipeline. +debug_step +`````````` + +:: + + sil-instruction ::= debug_step + + debug_step + +This instruction is inserted by Onone optimizations as a replacement for deleted instructions to +ensure that it's possible to set a breakpoint on its location. + +It is code-generated to a NOP instruction. + Testing ~~~~~~~ diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index 52db03298686b..e8bcf159681d2 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -465,6 +465,7 @@ BridgedInstruction SILBuilder_createCopyAddr(BridgedBuilder builder, SwiftInt takeSource, SwiftInt initializeDest); BridgedInstruction SILBuilder_createDestroyValue(BridgedBuilder builder, BridgedValue op); +BridgedInstruction SILBuilder_createDebugStep(BridgedBuilder builder); BridgedInstruction SILBuilder_createApply(BridgedBuilder builder, BridgedValue function, swift::SubstitutionMap subMap, BridgedValueArray arguments, bool isNonThrowing, bool isNonAsync, diff --git a/include/swift/SIL/SILBuilder.h b/include/swift/SIL/SILBuilder.h index 1e9b993bdad35..b59791eed6109 100644 --- a/include/swift/SIL/SILBuilder.h +++ b/include/swift/SIL/SILBuilder.h @@ -985,6 +985,10 @@ class SILBuilder { bool wasMoved = false, bool trace = false); + DebugStepInst *createDebugStep(SILLocation Loc) { + return insert(new (getModule()) DebugStepInst(getSILDebugLocation(Loc))); + } + /// Create a debug_value according to the type of \p src SILInstruction *emitDebugDescription(SILLocation Loc, SILValue src, SILDebugVariable Var) { diff --git a/include/swift/SIL/SILCloner.h b/include/swift/SIL/SILCloner.h index c3d72d709f51b..9c218c0c45de8 100644 --- a/include/swift/SIL/SILCloner.h +++ b/include/swift/SIL/SILCloner.h @@ -1315,6 +1315,11 @@ SILCloner::visitDebugValueInst(DebugValueInst *Inst) { remapDebugVarInfo(DebugVarCarryingInst(NewInst)); recordClonedInstruction(Inst, NewInst); } +template +void +SILCloner::visitDebugStepInst(DebugStepInst *Inst) { + recordClonedInstruction(Inst, getBuilder().createDebugStep(Inst->getLoc())); +} #define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ template \ diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 5901a6da056ac..aabb32b85b350 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -4929,6 +4929,19 @@ class MarkFunctionEscapeInst final } }; +/// This instruction is inserted by Onone optimizations as a replacement for deleted +/// instructions to ensure that it's possible to set a breakpoint on its location. +class DebugStepInst final + : public InstructionBase { + friend SILBuilder; + + DebugStepInst(SILDebugLocation debugLoc) : InstructionBase(debugLoc) {} + +public: + ArrayRef getAllOperands() const { return {}; } + MutableArrayRef getAllOperands() { return {}; } +}; + /// Define the start or update to a symbolic variable value (for loadable /// types). class DebugValueInst final diff --git a/include/swift/SIL/SILNodes.def b/include/swift/SIL/SILNodes.def index ce41fb331c1e9..9f763861c817b 100644 --- a/include/swift/SIL/SILNodes.def +++ b/include/swift/SIL/SILNodes.def @@ -789,6 +789,8 @@ NON_VALUE_INST(MarkFunctionEscapeInst, mark_function_escape, SILInstruction, None, DoesNotRelease) NON_VALUE_INST(DebugValueInst, debug_value, SILInstruction, None, DoesNotRelease) +NON_VALUE_INST(DebugStepInst, debug_step, + SILInstruction, MayHaveSideEffects, DoesNotRelease) NON_VALUE_INST(TestSpecificationInst, test_specification, SILInstruction, None, DoesNotRelease) #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \ diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 48cf4b64abfe8..e738ada882e91 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -1194,6 +1194,7 @@ class IRGenSILFunction : llvm_unreachable("unimplemented"); } void visitDebugValueInst(DebugValueInst *i); + void visitDebugStepInst(DebugStepInst *i); void visitRetainValueInst(RetainValueInst *i); void visitRetainValueAddrInst(RetainValueAddrInst *i); void visitCopyValueInst(CopyValueInst *i); @@ -5113,6 +5114,17 @@ void IRGenSILFunction::visitDebugValueInst(DebugValueInst *i) { AddrDbgInstrKind(i->getWasMoved())); } +void IRGenSILFunction::visitDebugStepInst(DebugStepInst *i) { + // Unfortunately there is no LLVM-equivalent of a debug_step instruction. + // Also LLVM doesn't provide a plain NOP instruction. + // Therefore we have to solve this with inline assembly. + // Strictly speaking, this is not architecture independent. But there are + // probably few assembly languages which don't use "nop" for nop instructions. + auto *AsmFnTy = llvm::FunctionType::get(IGM.VoidTy, {}, false); + auto *InlineAsm = llvm::InlineAsm::get(AsmFnTy, "nop", "", true); + Builder.CreateAsmCall(InlineAsm, {}); +} + void IRGenSILFunction::visitFixLifetimeInst(swift::FixLifetimeInst *i) { if (i->getOperand()->getType().isAddress()) { // Just pass in the address to fix lifetime if we have one. We will not do diff --git a/lib/SIL/IR/OperandOwnership.cpp b/lib/SIL/IR/OperandOwnership.cpp index 6cfb1698c3728..8741c1ecd08a4 100644 --- a/lib/SIL/IR/OperandOwnership.cpp +++ b/lib/SIL/IR/OperandOwnership.cpp @@ -102,6 +102,7 @@ SHOULD_NEVER_VISIT_INST(AllocPack) SHOULD_NEVER_VISIT_INST(DifferentiabilityWitnessFunction) SHOULD_NEVER_VISIT_INST(FloatLiteral) SHOULD_NEVER_VISIT_INST(FunctionRef) +SHOULD_NEVER_VISIT_INST(DebugStep) SHOULD_NEVER_VISIT_INST(DynamicFunctionRef) SHOULD_NEVER_VISIT_INST(PreviousDynamicFunctionRef) SHOULD_NEVER_VISIT_INST(GlobalAddr) diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index a6b3d17e0e1a4..c1d074174bf69 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -1789,6 +1789,10 @@ class SILPrinter : public SILInstructionVisitor { &DVI->getModule().getASTContext().SourceMgr); } + void visitDebugStepInst(DebugStepInst *dsi) { + // nothing to print other than the instruction name + } + #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ void visitLoad##Name##Inst(Load##Name##Inst *LI) { \ if (LI->isTake()) \ diff --git a/lib/SIL/Parser/ParseSIL.cpp b/lib/SIL/Parser/ParseSIL.cpp index d3e8c233c9141..5b89f928a64d4 100644 --- a/lib/SIL/Parser/ParseSIL.cpp +++ b/lib/SIL/Parser/ParseSIL.cpp @@ -3610,6 +3610,12 @@ bool SILParser::parseSpecificSILInstruction(SILBuilder &B, break; } + case SILInstructionKind::DebugStepInst: + if (parseSILDebugLocation(InstLoc, B)) + return true; + ResultVal = B.createDebugStep(InstLoc); + break; + case SILInstructionKind::TestSpecificationInst: { // Parse the specification string. if (P.Tok.getKind() != tok::string_literal) { diff --git a/lib/SIL/Utils/InstructionUtils.cpp b/lib/SIL/Utils/InstructionUtils.cpp index f042586b571e3..d0d502d371755 100644 --- a/lib/SIL/Utils/InstructionUtils.cpp +++ b/lib/SIL/Utils/InstructionUtils.cpp @@ -492,6 +492,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType) case SILInstructionKind::ScalarPackIndexInst: case SILInstructionKind::PackElementGetInst: case SILInstructionKind::PackElementSetInst: + case SILInstructionKind::DebugStepInst: return RuntimeEffect::NoEffect; case SILInstructionKind::DebugValueInst: diff --git a/lib/SIL/Utils/SILBridging.cpp b/lib/SIL/Utils/SILBridging.cpp index 6af49dfc39b0d..02410424e2894 100644 --- a/lib/SIL/Utils/SILBridging.cpp +++ b/lib/SIL/Utils/SILBridging.cpp @@ -1216,6 +1216,12 @@ BridgedInstruction SILBuilder_createDestroyValue(BridgedBuilder b, castToSILValue(op))}; } +BridgedInstruction SILBuilder_createDebugStep(BridgedBuilder b) { + SILBuilder builder(castToInst(b.insertBefore), castToBasicBlock(b.insertAtEnd), + b.loc.getScope()); + return {builder.createDebugStep(RegularLocation(b.loc.getLocation()))}; +} + BridgedInstruction SILBuilder_createApply(BridgedBuilder b, BridgedValue function, SubstitutionMap subMap, diff --git a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp index 2d859306f6f13..6bf57f1eed40b 100644 --- a/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp +++ b/lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp @@ -313,6 +313,7 @@ static bool hasOpaqueArchetype(TypeExpansionContext context, case SILInstructionKind::AssignByWrapperInst: case SILInstructionKind::MarkFunctionEscapeInst: case SILInstructionKind::DebugValueInst: + case SILInstructionKind::DebugStepInst: case SILInstructionKind::TestSpecificationInst: #define NEVER_OR_SOMETIMES_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \ case SILInstructionKind::Store##Name##Inst: diff --git a/lib/SILOptimizer/Utils/SILInliner.cpp b/lib/SILOptimizer/Utils/SILInliner.cpp index c968c9187c9fb..41022e61025b4 100644 --- a/lib/SILOptimizer/Utils/SILInliner.cpp +++ b/lib/SILOptimizer/Utils/SILInliner.cpp @@ -851,6 +851,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) { case SILInstructionKind::IntegerLiteralInst: case SILInstructionKind::FloatLiteralInst: case SILInstructionKind::DebugValueInst: + case SILInstructionKind::DebugStepInst: case SILInstructionKind::StringLiteralInst: case SILInstructionKind::FixLifetimeInst: case SILInstructionKind::EndBorrowInst: diff --git a/lib/Serialization/DeserializeSIL.cpp b/lib/Serialization/DeserializeSIL.cpp index 3cf0b57aa3dd3..c30245f888c2a 100644 --- a/lib/Serialization/DeserializeSIL.cpp +++ b/lib/Serialization/DeserializeSIL.cpp @@ -1302,6 +1302,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILInstruction *ResultInst; switch (OpCode) { case SILInstructionKind::DebugValueInst: + case SILInstructionKind::DebugStepInst: case SILInstructionKind::TestSpecificationInst: llvm_unreachable("not supported"); diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 757299e267e85..ad29225300d83 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0; /// describe what change you made. The content of this comment isn't important; /// it just ensures a conflict if two people change the module format. /// Don't worry about adhering to the 80-column limit for this line. -const uint16_t SWIFTMODULE_VERSION_MINOR = 742; // Constructor affects ABI +const uint16_t SWIFTMODULE_VERSION_MINOR = 743; // debug_step instruction /// A standard hash seed used for all string hashes in a serialized module. /// diff --git a/lib/Serialization/SerializeSIL.cpp b/lib/Serialization/SerializeSIL.cpp index b72711cb499d4..4130c7a470cca 100644 --- a/lib/Serialization/SerializeSIL.cpp +++ b/lib/Serialization/SerializeSIL.cpp @@ -915,8 +915,9 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) { } case SILInstructionKind::DebugValueInst: - // Currently we don't serialize debug variable infos, so it doesn't make - // sense to write the instruction at all. + case SILInstructionKind::DebugStepInst: + // Currently we don't serialize debug info, so it doesn't make + // sense to write those instructions at all. // TODO: decide if we want to serialize those instructions. return; case SILInstructionKind::TestSpecificationInst: diff --git a/test/IRGen/debug_step.sil b/test/IRGen/debug_step.sil new file mode 100644 index 0000000000000..7b5e39c2b765d --- /dev/null +++ b/test/IRGen/debug_step.sil @@ -0,0 +1,16 @@ +// RUN: %target-swift-frontend -g -emit-ir %s | %FileCheck %s + +// CHECK-LABEL: define {{.*}} @test_debug_step +sil @test_debug_step : $@convention(thin) () -> () { +bb0: + // CHECK: "nop"{{.*}}, !dbg [[L1:![0-9]+]] + debug_step, loc "a.swift":1:2 + // CHECK: "nop"{{.*}}, !dbg [[L2:![0-9]+]] + debug_step, loc "b.swift":3:4 + %r = tuple () + return %r : $() +} + +// CHECK-DAG: [[L1]] = !DILocation(line: 1, column: 2, {{.*}}) +// CHECK-DAG: [[L2]] = !DILocation(line: 3, column: 4, {{.*}}) + diff --git a/test/SIL/Parser/basic.sil b/test/SIL/Parser/basic.sil index 02d02cbc49129..6cacc004693b0 100644 --- a/test/SIL/Parser/basic.sil +++ b/test/SIL/Parser/basic.sil @@ -1314,6 +1314,16 @@ bb0(%0 : $Int, %1 : $*P, %2 : $AnyObject): unreachable } +// CHECK-LABEL: sil @debug_step +// CHECK: bb0: +// CHECK-NEXT: debug_step +// CHECK-NEXT: unreachable +sil @debug_step : $@convention(thin) () -> () { +bb0: + debug_step + unreachable +} + // CHECK-LABEL: sil @block_storage_type sil @block_storage_type : $@convention(thin) (Int) -> @convention(block) () -> () { entry(%0 : $Int): From 7eb2cb82e4613dd90d9859a63ae4d6fb6d7959f6 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Wed, 8 Feb 2023 19:11:32 +0100 Subject: [PATCH 67/98] Swift Optimizer: add a pass to cleanup debug_step instructions If a `debug_step` has the same debug location as a previous or succeeding instruction it is removed. It's just important that there is at least one instruction for a certain debug location so that single stepping on that location will work. --- .../Optimizer/FunctionPasses/CMakeLists.txt | 1 + .../FunctionPasses/CleanupDebugSteps.swift | 83 ++++++++++ .../PassManager/PassRegistration.swift | 1 + .../swift/SILOptimizer/PassManager/Passes.def | 2 + test/SILOptimizer/cleanup_debugstep.sil | 154 ++++++++++++++++++ 5 files changed, 241 insertions(+) create mode 100644 SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CleanupDebugSteps.swift create mode 100644 test/SILOptimizer/cleanup_debugstep.sil diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt index f60a5bf23d337..bf43f0302f56b 100644 --- a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt @@ -8,6 +8,7 @@ swift_compiler_sources(Optimizer AssumeSingleThreaded.swift + CleanupDebugSteps.swift ComputeEscapeEffects.swift ComputeSideEffects.swift ObjCBridgingOptimization.swift diff --git a/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CleanupDebugSteps.swift b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CleanupDebugSteps.swift new file mode 100644 index 0000000000000..469e7e9af61a9 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CleanupDebugSteps.swift @@ -0,0 +1,83 @@ +//===--- SimplificationPasses.swift ----------------------------------------==// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SIL + +/// Removes redundant `debug_step` instructions. +/// If a `debug_step` has the same debug location as a previous or succeeding instruction +/// it is removed. It's just important that there is at least one instruction for a +/// certain debug location so that single stepping on that location will work. +let cleanupDebugStepsPass = FunctionPass(name: "cleanup-debug-steps") { + (function: Function, context: FunctionPassContext) in + + for block in function.blocks { + cleanupDebugSteps(in: block, context) + } +} + +private func cleanupDebugSteps(in block: BasicBlock, _ context: FunctionPassContext) { + var lastInstWithSameLocation: Instruction? + + for inst in block.instructions { + if !inst.location.isDebugSteppable { + if inst is DebugStepInst && !inst.location.isDebugSteppable { + // First case: the instruction which is replaced by the debug_step didn't have a valid + // location itself. Then we don't need the debug_step either. + context.erase(instruction: inst) + } + continue + } + + if let li = lastInstWithSameLocation, + !inst.location.hasSameSourceLocation(as: li.location) { + lastInstWithSameLocation = nil + } + + // Only instructions which are really compiled down to some machine instructions can be + // single stepped on. + if !inst.producesMachineCode { + continue + } + + if let li = lastInstWithSameLocation { + if inst is DebugStepInst { + + // Second case: + // %li = some_instruction, loc "l" + // debug_step, loc "l" // current inst -> erase + context.erase(instruction: inst) + continue + } else if li is DebugStepInst { + + // Third case: + // debug_step, loc "l" // li -> erase + // %inst = some_instruction, loc "l" // current inst + context.erase(instruction: li) + } + } + lastInstWithSameLocation = inst + } +} + +private extension Instruction { + var producesMachineCode: Bool { + switch self { + // We could include more instructions here. + // In worst case a debug_step instruction remains in the code although it's not needed. + // This is harmless. + case is DebugStepInst, is ApplySite, is LoadInst, is StoreInst, is TermInst: + return location.isDebugSteppable + default: + return false + } + } +} diff --git a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift index 69cb6d5813c90..c7290e6a1b4f4 100644 --- a/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift +++ b/SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift @@ -75,6 +75,7 @@ private func registerSwiftPasses() { registerPass(simplificationPass, { simplificationPass.run($0) }) registerPass(ononeSimplificationPass, { ononeSimplificationPass.run($0) }) registerPass(lateOnoneSimplificationPass, { lateOnoneSimplificationPass.run($0) }) + registerPass(cleanupDebugStepsPass, { cleanupDebugStepsPass.run($0) }) // Instruction passes registerForSILCombine(BeginCOWMutationInst.self, { run(BeginCOWMutationInst.self, $0) }) diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index a2a1be0cdc4b6..f4fe549596160 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -395,6 +395,8 @@ SWIFT_FUNCTION_PASS(OnoneSimplification, "onone-simplification", "Peephole simplifications which runs at -Onone") SWIFT_FUNCTION_PASS(LateOnoneSimplification, "late-onone-simplification", "Peephole simplifications which can only run late in the -Onone pipeline") +SWIFT_FUNCTION_PASS(CleanupDebugSteps, "cleanup-debug-steps", + "Cleanup debug_step instructions for Onone") PASS(SimplifyBBArgs, "simplify-bb-args", "SIL Block Argument Simplification") PASS(SimplifyCFG, "simplify-cfg", diff --git a/test/SILOptimizer/cleanup_debugstep.sil b/test/SILOptimizer/cleanup_debugstep.sil new file mode 100644 index 0000000000000..f6f947065dd44 --- /dev/null +++ b/test/SILOptimizer/cleanup_debugstep.sil @@ -0,0 +1,154 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -cleanup-debug-steps -sil-print-debuginfo | %FileCheck %s + +// REQUIRES: swift_in_compiler + +import Swift +import Builtin + +sil @f : $@convention(thin) () -> () + +// CHECK-LABEL: sil @remove_after_previous_location +// CHECK-NOT: debug_step +// CHECK: } // end sil function 'remove_after_previous_location' +sil @remove_after_previous_location : $@convention(thin) () -> () { +bb0: + %f = function_ref @f : $@convention(thin) () -> () + %a = apply %f() : $@convention(thin) () -> (), loc "a.swift":1:2 + debug_step, loc "a.swift":1:2 + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil @dont_remove_after_previous_location +// CHECK: debug_step +// CHECK: } // end sil function 'dont_remove_after_previous_location' +sil @dont_remove_after_previous_location : $@convention(thin) () -> () { +bb0: + %f = function_ref @f : $@convention(thin) () -> () + %a = apply %f() : $@convention(thin) () -> (), loc "a.swift":1:2 + %r = tuple (), loc "a.swift":27:2 + debug_step, loc "a.swift":1:2 + return %r : $() +} + +// CHECK-LABEL: sil @remove_two_after_previous_location +// CHECK-NOT: debug_step +// CHECK: } // end sil function 'remove_two_after_previous_location' +sil @remove_two_after_previous_location : $@convention(thin) () -> () { +bb0: + %f = function_ref @f : $@convention(thin) () -> () + %a = apply %f() : $@convention(thin) () -> (), loc "a.swift":1:2 + debug_step, loc "a.swift":1:2 + debug_step, loc "a.swift":1:2 + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil @remove_before_next_location +// CHECK-NOT: debug_step +// CHECK: } // end sil function 'remove_before_next_location' +sil @remove_before_next_location : $@convention(thin) () -> () { +bb0: + %f = function_ref @f : $@convention(thin) () -> () + %a = apply %f() : $@convention(thin) () -> () + debug_step, loc "a.swift":1:2 + %r = tuple (), loc "":0:0 + return %r : $(), loc "a.swift":1:2 +} + +// CHECK-LABEL: sil @dont_remove_before_next_location +// CHECK: debug_step +// CHECK: } // end sil function 'dont_remove_before_next_location' +sil @dont_remove_before_next_location : $@convention(thin) () -> () { +bb0: + %f = function_ref @f : $@convention(thin) () -> () + %a = apply %f() : $@convention(thin) () -> () + debug_step, loc "a.swift":1:2 + %r = tuple (), loc "a.swift":27:2 + return %r : $(), loc "a.swift":1:2 +} + +// CHECK-LABEL: sil @remove_two_before_next_location +// CHECK-NOT: debug_step +// CHECK: } // end sil function 'remove_two_before_next_location' +sil @remove_two_before_next_location : $@convention(thin) () -> () { +bb0: + %f = function_ref @f : $@convention(thin) () -> () + %a = apply %f() : $@convention(thin) () -> () + debug_step, loc "a.swift":1:2 + debug_step, loc "a.swift":1:2 + %r = tuple (), loc "":0:0 + return %r : $(), loc "a.swift":1:2 +} + +// CHECK-LABEL: sil @remove_null_location +// CHECK-NOT: debug_step +// CHECK: } // end sil function 'remove_null_location' +sil @remove_null_location : $@convention(thin) () -> () { +bb0: + %f = function_ref @f : $@convention(thin) () -> () + %a = apply %f() : $@convention(thin) () -> () + debug_step, loc "":0:0 + %r = tuple () + return %r : $(), loc "a.swift":1:2 +} +// CHECK-LABEL: sil @dont_remove_one +// CHECK: debug_step +// CHECK: } // end sil function 'dont_remove_one' +sil @dont_remove_one : $@convention(thin) () -> () { +bb0: + %f = function_ref @f : $@convention(thin) () -> () + %a = apply %f() : $@convention(thin) () -> () + debug_step, loc "a.swift":1:2 + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil @replace_three_with_one +// CHECK: apply +// CHECK-NEXT: debug_step , loc "a.swift":1:2 +// CHECK-NEXT: tuple +// CHECK: } // end sil function 'replace_three_with_one' +sil @replace_three_with_one : $@convention(thin) () -> () { +bb0: + %f = function_ref @f : $@convention(thin) () -> () + %a = apply %f() : $@convention(thin) () -> () + debug_step, loc "a.swift":1:2 + debug_step, loc "a.swift":1:2 + debug_step, loc "a.swift":1:2 + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil @replace_three_with_two +// CHECK: apply +// CHECK-NEXT: debug_step , loc "a.swift":1:2 +// CHECK-NEXT: debug_step , loc "a.swift":2:3 +// CHECK-NEXT: tuple +// CHECK: } // end sil function 'replace_three_with_two' +sil @replace_three_with_two : $@convention(thin) () -> () { +bb0: + %f = function_ref @f : $@convention(thin) () -> () + %a = apply %f() : $@convention(thin) () -> () + debug_step, loc "a.swift":1:2 + debug_step, loc "a.swift":2:3 + debug_step, loc "a.swift":2:3 + %r = tuple () + return %r : $() +} + + +// CHECK-LABEL: sil @dont_remove_two +// CHECK: debug_step +// CHECK-NEXT: debug_step +// CHECK: } // end sil function 'dont_remove_two' +sil @dont_remove_two : $@convention(thin) () -> () { +bb0: + %f = function_ref @f : $@convention(thin) () -> () + %a = apply %f() : $@convention(thin) () -> () + debug_step, loc "a.swift":1:2 + debug_step, loc "a.swift":2:3 + %r = tuple () + return %r : $() +} + From f1c6ed681d5e7b675efcd216bfb2d077790a482d Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 6 Feb 2023 12:09:02 +0100 Subject: [PATCH 68/98] Swift Optimizer: add Onone simplification of apply instructions --- .../InstructionSimplification/CMakeLists.txt | 1 + .../SimplifyApply.swift | 61 +++++++++++++++ test/SILOptimizer/simplify_apply.sil | 78 +++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyApply.swift create mode 100644 test/SILOptimizer/simplify_apply.sil diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt index 667287e8caa71..7c7801a4c7fa6 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt @@ -7,6 +7,7 @@ # See http://swift.org/CONTRIBUTORS.txt for Swift project authors swift_compiler_sources(Optimizer + SimplifyApply.swift SimplifyBeginCOWMutation.swift SimplifyGlobalValue.swift SimplifyStrongRetainRelease.swift) diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyApply.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyApply.swift new file mode 100644 index 0000000000000..06e5838318a4f --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyApply.swift @@ -0,0 +1,61 @@ +//===--- SimplifyApply.swift ----------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SIL + +extension ApplyInst : OnoneSimplifyable { + func simplify(_ context: SimplifyContext) { + tryReplaceTrivialApplyOfPartialApply(context) + } +} + +private extension ApplyInst { + func tryReplaceTrivialApplyOfPartialApply(_ context: SimplifyContext) { + guard let pa = callee as? PartialApplyInst else { + return + } + + if pa.referencedFunction == nil { + return + } + + // Currently we don't handle generic closures. For Onone this is good enough. + // TODO: handle it once we replace the SILCombine simplification with this. + if !allArgumentsAreTrivial(arguments) { + return + } + + if !allArgumentsAreTrivial(pa.arguments) { + return + } + + if !substitutionMap.isEmpty { + return + } + + let allArgs = Array(arguments) + Array(pa.arguments) + let builder = Builder(before: self, context) + let newApply = builder.createApply(function: pa.callee, pa.substitutionMap, arguments: allArgs, + isNonThrowing: isNonThrowing, isNonAsync: isNonAsync, + specializationInfo: specializationInfo) + uses.replaceAll(with: newApply, context) + context.erase(instruction: self) + + if context.tryDeleteDeadClosure(closure: pa) { + context.notifyInvalidatedStackNesting() + } + } +} + +private func allArgumentsAreTrivial(_ args: LazyMapSequence) -> Bool { + return !args.contains { !$0.hasTrivialType } +} diff --git a/test/SILOptimizer/simplify_apply.sil b/test/SILOptimizer/simplify_apply.sil new file mode 100644 index 0000000000000..f991bb57a9b73 --- /dev/null +++ b/test/SILOptimizer/simplify_apply.sil @@ -0,0 +1,78 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -onone-simplification -simplify-instruction=apply | %FileCheck %s + +// REQUIRES: swift_in_compiler + +import Swift +import Builtin + +sil @closure_with_args : $@convention(thin) (Int, Bool) -> () +sil @closure2_with_args : $@convention(thin) (Int, String) -> () +sil @closure3_with_args : $@convention(thin) (String, Bool) -> () +sil @generic_callee_inguaranteed : $@convention(thin) (@in_guaranteed T, @in_guaranteed U) -> () + +// CHECK-LABEL: sil @test_apply_of_partial_apply +// CHECK: [[F:%.*]] = function_ref @closure_with_args +// CHECK-NOT: partial_apply +// CHECK: apply [[F]](%0, %1) +// CHECK: } // end sil function 'test_apply_of_partial_apply' +sil @test_apply_of_partial_apply : $@convention(thin) (Int, Bool) -> () { +bb0(%0 : $Int, %1 : $Bool): + %2 = function_ref @closure_with_args : $@convention(thin) (Int, Bool) -> () + %3 = partial_apply %2(%1) : $@convention(thin) (Int, Bool) -> () + apply %3(%0) : $@callee_owned (Int) -> () + %r = tuple() + return %r : $() +} + +// Currently this is not optimized by the simplification passes. +// TODO: change the check lines if we can handle generic closures + +// CHECK-LABEL: sil @test_generic_partial_apply_apply_inguaranteed +// CHECK: [[F:%.*]] = function_ref @generic_callee_inguaranteed +// CHECK: [[PA:%.*]] = partial_apply [[F]] +// CHECK: apply [[PA]] +// CHECK: } // end sil function 'test_generic_partial_apply_apply_inguaranteed' +sil @test_generic_partial_apply_apply_inguaranteed : $@convention(thin) (@in T, @in T) -> () { +bb0(%0 : $*T, %1 : $*T): + %f1 = function_ref @generic_callee_inguaranteed : $@convention(thin) (@in_guaranteed T, @in_guaranteed U) -> () + %pa = partial_apply %f1(%1) : $@convention(thin) (@in_guaranteed T, @in_guaranteed U) -> () + %a1 = apply %pa(%0) : $@callee_owned (@in_guaranteed T) -> () + destroy_addr %0 : $*T + %r = tuple () + return %r : $() +} + +// Currently this is not optimized by the simplification passes. +// TODO: change the check lines if we can handle non-trivial arguments + +// CHECK-LABEL: sil @dont_handle_non_trivial_pa_args +// CHECK: [[F:%.*]] = function_ref @closure2_with_args +// CHECK: [[PA:%.*]] = partial_apply [[F]] +// CHECK: apply [[PA]] +// CHECK: } // end sil function 'dont_handle_non_trivial_pa_args' +sil @dont_handle_non_trivial_pa_args : $@convention(thin) (Int, String) -> () { +bb0(%0 : $Int, %1 : $String): + %2 = function_ref @closure2_with_args : $@convention(thin) (Int, String) -> () + %3 = partial_apply %2(%1) : $@convention(thin) (Int, String) -> () + apply %3(%0) : $@callee_owned (Int) -> () + %r = tuple() + return %r : $() +} + +// Currently this is not optimized by the simplification passes. +// TODO: change the check lines if we can handle non-trivial arguments + +// CHECK-LABEL: sil @dont_handle_non_trivial_apply_args +// CHECK: [[F:%.*]] = function_ref @closure3_with_args +// CHECK: [[PA:%.*]] = partial_apply [[F]] +// CHECK: apply [[PA]] +// CHECK: } // end sil function 'dont_handle_non_trivial_apply_args' +sil @dont_handle_non_trivial_apply_args : $@convention(thin) (String, Bool) -> () { +bb0(%0 : $String, %1 : $Bool): + %2 = function_ref @closure3_with_args : $@convention(thin) (String, Bool) -> () + %3 = partial_apply %2(%1) : $@convention(thin) (String, Bool) -> () + apply %3(%0) : $@callee_owned (String) -> () + %r = tuple() + return %r : $() +} + From 3f35a1d869a87098e03ed89c85ede9b2de8debe1 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Mon, 6 Feb 2023 14:15:46 +0100 Subject: [PATCH 69/98] Swift Optimizer: add Onone simplification of branch instructions --- .../InstructionSimplification/CMakeLists.txt | 1 + .../SimplifyBranch.swift | 113 ++++++++++++++++++ test/SILOptimizer/simplify_branch.sil | 101 ++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBranch.swift create mode 100644 test/SILOptimizer/simplify_branch.sil diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt index 7c7801a4c7fa6..7f37ef31744a1 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt @@ -9,5 +9,6 @@ swift_compiler_sources(Optimizer SimplifyApply.swift SimplifyBeginCOWMutation.swift + SimplifyBranch.swift SimplifyGlobalValue.swift SimplifyStrongRetainRelease.swift) diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBranch.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBranch.swift new file mode 100644 index 0000000000000..8d88bc3f1225a --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBranch.swift @@ -0,0 +1,113 @@ +//===--- SimplifyBranch.swift ---------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SIL + +extension BranchInst : OnoneSimplifyable { + func simplify(_ context: SimplifyContext) { + tryMergeWithTargetBlock(context) + } +} + +private extension BranchInst { + func tryMergeWithTargetBlock(_ context: SimplifyContext) { + if canMergeWithTargetBlock { + mergeWithTargetBlock(context) + } + } + + var canMergeWithTargetBlock: Bool { + // We can only merge if there is a 1:1 relation to the target block. + guard let pred = targetBlock.singlePredecessor else { + return false + } + assert(pred == parentBlock) + + // Ignore self cycles + if targetBlock == parentBlock { + return false + } + + if hasInvalidDominanceCycle { + return false + } + + return true + } + + func mergeWithTargetBlock(_ context: SimplifyContext) { + let targetBB = targetBlock + let parentBB = parentBlock + + for (argIdx, op) in operands.enumerated() { + targetBB.arguments[argIdx].uses.replaceAll(with: op.value, context) + } + targetBB.eraseAllArguments(context) + + if context.preserveDebugInfo { + let builder = Builder(before: self, context) + builder.createDebugStep() + } + context.erase(instruction: self) + + // Move instruction from the smaller block to the larger block. + // The order is essential because if many blocks are merged and this is done + // in the wrong order, we end up with quadratic complexity. + // + if parentBB.hasLessInstructions(than: targetBB) && + parentBB != parentBB.parentFunction.entryBlock { + for pred in parentBB.predecessors { + pred.terminator.replaceBranchTarget(from: parentBB, to: targetBB, context) + } + parentBB.moveAllInstructions(toBeginOf: targetBB, context) + parentBB.moveAllArguments(to: targetBB, context) + } else { + targetBB.moveAllInstructions(toEndOf: parentBB, context) + } + } +} + +private extension BasicBlock { + func hasLessInstructions(than otherBlock: BasicBlock) -> Bool { + var insts = instructions + var otherInsts = otherBlock.instructions + while true { + if otherInsts.next() == nil { + return false + } + if insts.next() == nil { + return true + } + } + } +} + +private extension BranchInst { + + // True if this block is part of an unreachable cfg cycle, where an argument dominates itself. + // For example: + // ``` + // bb1(arg1): // preds: bb2 + // br bb2 + // + // bb2: // preds: bb1 + // br bb1(arg1) + // ``` + var hasInvalidDominanceCycle: Bool { + for (argIdx, op) in operands.enumerated() { + if targetBlock.arguments[argIdx] == op.value { + return true + } + } + return false + } +} diff --git a/test/SILOptimizer/simplify_branch.sil b/test/SILOptimizer/simplify_branch.sil new file mode 100644 index 0000000000000..18b9e546e6ad0 --- /dev/null +++ b/test/SILOptimizer/simplify_branch.sil @@ -0,0 +1,101 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplification -simplify-instruction=br | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all %s -onone-simplification -simplify-instruction=br -sil-print-debuginfo | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ONONE + +// REQUIRES: swift_in_compiler + +import Swift +import Builtin + +// CHECK-LABEL: sil @merge_blocks_same_location1 +// CHECK: %0 = integer_literal $Builtin.Int64, 0 +// CHECK: fix_lifetime %0 : $Builtin.Int64 +// CHECK: return %0 : $Builtin.Int64 +// CHECK: } // end sil function 'merge_blocks_same_location1' +sil @merge_blocks_same_location1 : $@convention(thin) () -> Builtin.Int64 { +bb0: + %0 = integer_literal $Builtin.Int64, 0 + br bb1(%0 : $Builtin.Int64), loc "a.swift":1:1 + +bb1(%2 : $Builtin.Int64): + fix_lifetime %2 : $Builtin.Int64, loc "a.swift":1:1 + return %2 : $Builtin.Int64 +} + +// CHECK-LABEL: sil @merge_blocks_same_location2 +// CHECK: %0 = integer_literal $Builtin.Int64, 0 +// CHECK: fix_lifetime %0 : $Builtin.Int64 +// CHECK: return %0 : $Builtin.Int64 +// CHECK: } // end sil function 'merge_blocks_same_location2' +sil @merge_blocks_same_location2 : $@convention(thin) () -> Builtin.Int64 { +bb0: + %0 = integer_literal $Builtin.Int64, 0, loc "a.swift":1:1 + br bb1(%0 : $Builtin.Int64), loc "a.swift":1:1 + +bb1(%2 : $Builtin.Int64): + fix_lifetime %2 : $Builtin.Int64 + return %2 : $Builtin.Int64 +} + +// CHECK-LABEL: sil @merge_blocks_different_locations +// CHECK: %0 = integer_literal $Builtin.Int64, 0 +// CHECK-ONONE: debug_step , loc "a.swift":2:1 +// CHECK: fix_lifetime %0 : $Builtin.Int64 +// CHECK: return %0 : $Builtin.Int64 +// CHECK: } // end sil function 'merge_blocks_different_locations' +sil @merge_blocks_different_locations : $@convention(thin) () -> Builtin.Int64 { +bb0: + %0 = integer_literal $Builtin.Int64, 0, loc "a.swift":1:1 + br bb1(%0 : $Builtin.Int64), loc "a.swift":2:1 + +bb1(%2 : $Builtin.Int64): + fix_lifetime %2 : $Builtin.Int64, loc "a.swift":3:1 + return %2 : $Builtin.Int64 +} + +// CHECK-LABEL: sil @dont_merge_blocks +// CHECK: bb1: +// CHECK-NEXT: br bb3 +// CHECK: bb2: +// CHECK-NEXT: br bb3 +// CHECK: } // end sil function 'dont_merge_blocks' +sil @dont_merge_blocks : $@convention(thin) () -> () { +bb0: + cond_br undef, bb1, bb2 + +bb1: + br bb3 + +bb2: + br bb3 + +bb3: + %r = tuple () + return %r : $() +} + +// CHECK-LABEL: sil @dont_merge_block_in_single_cycle +// CHECK: bb1: +// CHECK-NEXT: br bb1 +// CHECK: } // end sil function 'dont_merge_block_in_single_cycle' +sil @dont_merge_block_in_single_cycle : $@convention(thin) () -> () { +bb0: + br bb1 + +bb1: + br bb1 +} + +// CHECK-LABEL: sil @dont_crash_with_unreachable_invalid_dominance +// CHECK-NOT: bb1 +// CHECK: } // end sil function 'dont_crash_with_unreachable_invalid_dominance' +sil @dont_crash_with_unreachable_invalid_dominance : $@convention(thin) (Builtin.Int64) -> Builtin.Int64 { +bb0(%0 : $Builtin.Int64): + return %0 : $Builtin.Int64 + +bb1(%2 : $Builtin.Int64): + br bb2 + +bb2: + br bb1(%2 : $Builtin.Int64) +} + From d56ed65718e72e9562f4c55905c964c4e5d49b47 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 7 Feb 2023 09:12:22 +0100 Subject: [PATCH 70/98] Swift Optimizer: add Onone simplification of some builtin instructions * `Builtin.isConcrete` * `Builtin.is_same_metatype` --- .../InstructionSimplification/CMakeLists.txt | 1 + .../SimplifyBuiltin.swift | 144 ++++++++++++ test/SILOptimizer/simplify_builtin.sil | 222 ++++++++++++++++++ 3 files changed, 367 insertions(+) create mode 100644 SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift create mode 100644 test/SILOptimizer/simplify_builtin.sil diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt index 7f37ef31744a1..179d50ccec5a4 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt @@ -10,5 +10,6 @@ swift_compiler_sources(Optimizer SimplifyApply.swift SimplifyBeginCOWMutation.swift SimplifyBranch.swift + SimplifyBuiltin.swift SimplifyGlobalValue.swift SimplifyStrongRetainRelease.swift) diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift new file mode 100644 index 0000000000000..4fbebe907d711 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBuiltin.swift @@ -0,0 +1,144 @@ +//===--- SimplifyBuiltin.swift --------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SIL + +extension BuiltinInst : OnoneSimplifyable { + func simplify(_ context: SimplifyContext) { + switch id { + case .IsConcrete: + // Don't constant fold a Builtin.isConcrete of a type with archetypes in the middle + // of the pipeline, because a generic specializer might run afterwards which turns the + // type into a concrete type. + optimizeIsConcrete(allowArchetypes: false, context) + case .IsSameMetatype: + optimizeIsSameMetatype(context) + default: + // TODO: handle other builtin types + break + } + } +} + +extension BuiltinInst : LateOnoneSimplifyable { + func simplifyLate(_ context: SimplifyContext) { + if id == .IsConcrete { + // At the end of the pipeline we can be sure that the isConcrete's type doesn't get "more" concrete. + optimizeIsConcrete(allowArchetypes: true, context) + } else { + simplify(context) + } + } +} + +private extension BuiltinInst { + func optimizeIsConcrete(allowArchetypes: Bool, _ context: SimplifyContext) { + let hasArchetype = operands[0].value.type.hasArchetype + if hasArchetype && !allowArchetypes { + return + } + let builder = Builder(before: self, context) + let result = builder.createIntegerLiteral(hasArchetype ? 0 : 1, type: type) + uses.replaceAll(with: result, context) + context.erase(instruction: self) + } + + func optimizeIsSameMetatype(_ context: SimplifyContext) { + let lhs = operands[0].value + let rhs = operands[1].value + + guard let equal = typesOfValuesAreEqual(lhs, rhs) else { + return + } + let builder = Builder(before: self, context) + let result = builder.createIntegerLiteral(equal ? 1 : 0, type: type) + + uses.replaceAll(with: result, context) + } +} + +private func typesOfValuesAreEqual(_ lhs: Value, _ rhs: Value) -> Bool? { + if lhs == rhs { + return true + } + + guard let lhsExistential = lhs as? InitExistentialMetatypeInst, + let rhsExistential = rhs as? InitExistentialMetatypeInst else { + return nil + } + + let lhsTy = lhsExistential.operand.type.instanceTypeOfMetatype + let rhsTy = rhsExistential.operand.type.instanceTypeOfMetatype + + // Do we know the exact types? This is not the case e.g. if a type is passed as metatype + // to the function. + let typesAreExact = lhsExistential.operand is MetatypeInst && + rhsExistential.operand is MetatypeInst + + switch (lhsTy.typeKind, rhsTy.typeKind) { + case (_, .unknown), (.unknown, _): + return nil + case (let leftKind, let rightKind) where leftKind != rightKind: + // E.g. a function type is always different than a struct, regardless of what archetypes + // the two types may contain. + return false + case (.struct, .struct), (.enum, .enum): + // Two different structs/enums are always not equal, regardless of what archetypes + // the two types may contain. + if lhsTy.nominal != rhsTy.nominal { + return false + } + case (.class, .class): + // In case of classes this only holds if we know the exact types. + // Otherwise one class could be a sub-class of the other class. + if typesAreExact && lhsTy.nominal != rhsTy.nominal { + return false + } + default: + break + } + + if !typesAreExact { + // Types which e.g. come from type parameters may differ at runtime while the declared AST types are the same. + return nil + } + + if lhsTy.hasArchetype || rhsTy.hasArchetype { + // We don't know anything about archetypes. They may be identical at runtime or not. + // We could do something more sophisticated here, e.g. look at conformances. But for simplicity, + // we are just conservative. + return nil + } + + // Generic ObjectiveC class, which are specialized for different NSObject types have different AST types + // but the same runtime metatype. + if lhsTy.isOrContainsObjectiveCClass || rhsTy.isOrContainsObjectiveCClass { + return nil + } + + return lhsTy == rhsTy +} + +private extension Type { + enum TypeKind { + case `struct`, `class`, `enum`, tuple, function, unknown + } + + var typeKind: TypeKind { + if isStruct { return .struct } + if isClass { return .class } + if isEnum { return .enum } + if isTuple { return .tuple } + if isFunction { return .function } + return .unknown + } +} diff --git a/test/SILOptimizer/simplify_builtin.sil b/test/SILOptimizer/simplify_builtin.sil new file mode 100644 index 0000000000000..c59a1a02c99a4 --- /dev/null +++ b/test/SILOptimizer/simplify_builtin.sil @@ -0,0 +1,222 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -onone-simplification -simplify-instruction=builtin | %FileCheck %s --check-prefix=CHECK --check-prefix=EARLY +// RUN: %target-sil-opt -enable-sil-verify-all %s -late-onone-simplification -simplify-instruction=builtin | %FileCheck %s --check-prefix=CHECK --check-prefix=LATE + +// REQUIRES: swift_in_compiler + +import Swift +import Builtin + +struct S1 { +} + +struct S2 { +} + +enum E1 { +} + +enum E2 { +} + +class C1 { +} + +class C2 : C1 { +} + +// CHECK-LABEL: sil @isConcrete_true +// CHECK: bb0(%0 : $@thin Int.Type): +// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, -1 +// CHECK: return [[R]] +// CHECK: } // end sil function 'isConcrete_true' +sil @isConcrete_true : $@convention(thin) (@thin Int.Type) -> Builtin.Int1 { +bb0(%0 : $@thin Int.Type): + %1 = builtin "isConcrete"(%0 : $@thin Int.Type) : $Builtin.Int1 + return %1 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @isConcrete_false +// CHECK: bb0(%0 : $@thin T.Type): +// CHECK-EARLY: [[R:%.*]] = builtin "isConcrete"(%0 : $@thin T.Type) : $Builtin.Int1 +// CHECK-LATE: [[R:%.*]] = integer_literal $Builtin.Int1, -1 +// CHECK: return [[R]] +// CHECK: } // end sil function 'isConcrete_false' +sil @isConcrete_false : $@convention(thin) (@thin T.Type) -> Builtin.Int1 { +bb0(%0 : $@thin T.Type): + %1 = builtin "isConcrete"(%0 : $@thin T.Type) : $Builtin.Int1 + return %1 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @same_metatype_same_operand +// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, -1 +// CHECK-NEXT: return [[R]] +// CHECK: } // end sil function 'same_metatype_same_operand' +sil @same_metatype_same_operand : $@convention(thin) (@thick T.Type) -> Builtin.Int1 { +bb0(%0 : $@thick T.Type): + %1 = init_existential_metatype %0 : $@thick T.Type, $@thick Any.Type + %3 = builtin "is_same_metatype"(%1 : $@thick Any.Type, %1 : $@thick Any.Type) : $Builtin.Int1 + return %3 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @unknown_same_metatype_int_and_T +// CHECK: [[R:%.*]] = builtin "is_same_metatype" +// CHECK: return [[R]] +// CHECK: } // end sil function 'unknown_same_metatype_int_and_T' +sil @unknown_same_metatype_int_and_T : $@convention(thin) (@thick T.Type) -> Builtin.Int1 { +bb0(%0 : $@thick T.Type): + %1 = metatype $@thick T.Type + %2 = metatype $@thick Int.Type + %3 = init_existential_metatype %1 : $@thick T.Type, $@thick Any.Type + %4 = init_existential_metatype %2 : $@thick Int.Type, $@thick Any.Type + %5 = builtin "is_same_metatype"(%3 : $@thick Any.Type, %4 : $@thick Any.Type) : $Builtin.Int1 + return %5 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @unknown_same_metatype_same_struct_different_T +// CHECK: [[R:%.*]] = builtin "is_same_metatype" +// CHECK: return [[R]] +// CHECK: } // end sil function 'unknown_same_metatype_same_struct_different_T' +sil @unknown_same_metatype_same_struct_different_T : $@convention(thin) (@thick T.Type, @thick U.Type) -> Builtin.Int1 { +bb0(%0 : $@thick T.Type, %1 : $@thick U.Type): + %2 = metatype $@thick S1.Type + %3 = metatype $@thick S1.Type + %4 = init_existential_metatype %2 : $@thick S1.Type, $@thick Any.Type + %5 = init_existential_metatype %3 : $@thick S1.Type, $@thick Any.Type + %6 = builtin "is_same_metatype"(%4 : $@thick Any.Type, %5 : $@thick Any.Type) : $Builtin.Int1 + return %6 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @same_metatype_same_type +// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, -1 +// CHECK: return [[R]] +// CHECK: } // end sil function 'same_metatype_same_type' +sil @same_metatype_same_type : $@convention(thin) () -> Builtin.Int1 { +bb0: + %0 = metatype $@thick S1.Type + %1 = metatype $@thick S1.Type + %2 = init_existential_metatype %0 : $@thick S1.Type, $@thick Any.Type + %3 = init_existential_metatype %1 : $@thick S1.Type, $@thick Any.Type + %4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1 + return %4 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @same_metatype_different_struct_arg +// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, 0 +// CHECK: return [[R]] +// CHECK: } // end sil function 'same_metatype_different_struct_arg' +sil @same_metatype_different_struct_arg : $@convention(thin) () -> Builtin.Int1 { +bb0: + %0 = metatype $@thick S1.Type + %1 = metatype $@thick S1.Type + %2 = init_existential_metatype %0 : $@thick S1.Type, $@thick Any.Type + %3 = init_existential_metatype %1 : $@thick S1.Type, $@thick Any.Type + %4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1 + return %4 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @same_metatype_same_tuple +// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, -1 +// CHECK: return [[R]] +// CHECK: } // end sil function 'same_metatype_same_tuple' +sil @same_metatype_same_tuple : $@convention(thin) () -> Builtin.Int1 { +bb0: + %0 = metatype $@thick (Int, Float).Type + %1 = metatype $@thick (Int, Float).Type + %2 = init_existential_metatype %0 : $@thick (Int, Float).Type, $@thick Any.Type + %3 = init_existential_metatype %1 : $@thick (Int, Float).Type, $@thick Any.Type + %4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1 + return %4 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @same_metatype_different_tuple +// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, 0 +// CHECK: return [[R]] +// CHECK: } // end sil function 'same_metatype_different_tuple' +sil @same_metatype_different_tuple : $@convention(thin) () -> Builtin.Int1 { +bb0: + %0 = metatype $@thick (Int, Float).Type + %1 = metatype $@thick (Int, Int).Type + %2 = init_existential_metatype %0 : $@thick (Int, Float).Type, $@thick Any.Type + %3 = init_existential_metatype %1 : $@thick (Int, Int).Type, $@thick Any.Type + %4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1 + return %4 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @same_metatype_different_struct +// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, 0 +// CHECK-NEXT: return [[R]] +// CHECK: } // end sil function 'same_metatype_different_struct' +sil @same_metatype_different_struct : $@convention(thin) (@thick S1.Type, @thick S2.Type) -> Builtin.Int1 { +bb0(%0 : $@thick S1.Type, %1 : $@thick S2.Type): + %2 = init_existential_metatype %0 : $@thick S1.Type, $@thick Any.Type + %3 = init_existential_metatype %1 : $@thick S2.Type, $@thick Any.Type + %4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1 + return %4 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @unknown_same_metatype_same_enum_different_T +// CHECK: [[R:%.*]] = builtin "is_same_metatype" +// CHECK: return [[R]] +// CHECK: } // end sil function 'unknown_same_metatype_same_enum_different_T' +sil @unknown_same_metatype_same_enum_different_T : $@convention(thin) (@thick T.Type, @thick U.Type) -> Builtin.Int1 { +bb0(%0 : $@thick T.Type, %1 : $@thick U.Type): + %2 = metatype $@thick E1.Type + %3 = metatype $@thick E1.Type + %4 = init_existential_metatype %2 : $@thick E1.Type, $@thick Any.Type + %5 = init_existential_metatype %3 : $@thick E1.Type, $@thick Any.Type + %6 = builtin "is_same_metatype"(%4 : $@thick Any.Type, %5 : $@thick Any.Type) : $Builtin.Int1 + return %6 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @same_metatype_different_enum +// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, 0 +// CHECK-NEXT: return [[R]] +// CHECK: } // end sil function 'same_metatype_different_enum' +sil @same_metatype_different_enum : $@convention(thin) (@thick E1.Type, @thick E2.Type) -> Builtin.Int1 { +bb0(%0 : $@thick E1.Type, %1 : $@thick E2.Type): + %2 = init_existential_metatype %0 : $@thick E1.Type, $@thick Any.Type + %3 = init_existential_metatype %1 : $@thick E2.Type, $@thick Any.Type + %4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1 + return %4 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @unknown_same_metatype_same_class_different_T +// CHECK: [[R:%.*]] = builtin "is_same_metatype" +// CHECK: return [[R]] +// CHECK: } // end sil function 'unknown_same_metatype_same_class_different_T' +sil @unknown_same_metatype_same_class_different_T : $@convention(thin) (@thick T.Type, @thick U.Type) -> Builtin.Int1 { +bb0(%0 : $@thick T.Type, %1 : $@thick U.Type): + %2 = metatype $@thick C1.Type + %3 = metatype $@thick C1.Type + %4 = init_existential_metatype %2 : $@thick C1.Type, $@thick Any.Type + %5 = init_existential_metatype %3 : $@thick C1.Type, $@thick Any.Type + %6 = builtin "is_same_metatype"(%4 : $@thick Any.Type, %5 : $@thick Any.Type) : $Builtin.Int1 + return %6 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @unknown_same_metatype_different_class +// CHECK: [[R:%.*]] = builtin "is_same_metatype" +// CHECK: return [[R]] +// CHECK: } // end sil function 'unknown_same_metatype_different_class' +sil @unknown_same_metatype_different_class : $@convention(thin) (@thick C1.Type, @thick C2.Type) -> Builtin.Int1 { +bb0(%0 : $@thick C1.Type, %1 : $@thick C2.Type): + %2 = init_existential_metatype %0 : $@thick C1.Type, $@thick Any.Type + %3 = init_existential_metatype %1 : $@thick C2.Type, $@thick Any.Type + %4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1 + return %4 : $Builtin.Int1 +} + +// CHECK-LABEL: sil @same_metatype_different_concrete_class +// CHECK: [[R:%.*]] = integer_literal $Builtin.Int1, 0 +// CHECK-NEXT: return [[R]] +// CHECK: } // end sil function 'same_metatype_different_concrete_class' +sil @same_metatype_different_concrete_class : $@convention(thin) () -> Builtin.Int1 { +bb0: + %0 = metatype $@thick C1.Type + %1 = metatype $@thick C2.Type + %2 = init_existential_metatype %0 : $@thick C1.Type, $@thick Any.Type + %3 = init_existential_metatype %1 : $@thick C2.Type, $@thick Any.Type + %4 = builtin "is_same_metatype"(%2 : $@thick Any.Type, %3 : $@thick Any.Type) : $Builtin.Int1 + return %4 : $Builtin.Int1 +} + From 4cf62696b60c7fbaad0ff5345110e370aa2be3d9 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 7 Feb 2023 09:22:57 +0100 Subject: [PATCH 71/98] Swift Optimizer: add Onone simplification of cond_br instructions --- .../InstructionSimplification/CMakeLists.txt | 1 + .../SimplifyCondBranch.swift | 34 ++++++++++ test/SILOptimizer/simplify_cond_br.sil | 64 +++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCondBranch.swift create mode 100644 test/SILOptimizer/simplify_cond_br.sil diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt index 179d50ccec5a4..bcab368cbd94a 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt @@ -11,5 +11,6 @@ swift_compiler_sources(Optimizer SimplifyBeginCOWMutation.swift SimplifyBranch.swift SimplifyBuiltin.swift + SimplifyCondBranch.swift SimplifyGlobalValue.swift SimplifyStrongRetainRelease.swift) diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCondBranch.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCondBranch.swift new file mode 100644 index 0000000000000..69a799e908711 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyCondBranch.swift @@ -0,0 +1,34 @@ +//===--- SimplifyCondBranch.swift -----------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SIL + +extension CondBranchInst : OnoneSimplifyable { + func simplify(_ context: SimplifyContext) { + tryConstantFold(context) + } +} + +private extension CondBranchInst { + func tryConstantFold(_ context: SimplifyContext) { + guard let literal = condition as? IntegerLiteralInst else { + return + } + let builder = Builder(before: self, context) + if literal.value.isZero() { + builder.createBranch(to: falseBlock, arguments: Array(falseOperands.map { $0.value })) + } else { + builder.createBranch(to: trueBlock, arguments: Array(trueOperands.map { $0.value })) + } + context.erase(instruction: self) + } +} diff --git a/test/SILOptimizer/simplify_cond_br.sil b/test/SILOptimizer/simplify_cond_br.sil new file mode 100644 index 0000000000000..342119677e347 --- /dev/null +++ b/test/SILOptimizer/simplify_cond_br.sil @@ -0,0 +1,64 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -onone-simplification -simplify-instruction=cond_br | %FileCheck %s + + +// REQUIRES: swift_in_compiler + +import Swift +import Builtin + +// CHECK-LABEL: sil @constant_cond_br_true +// CHECK: bb0(%0 : $Int, %1 : $Int): +// CHECK-NEXT: br bb1(%0 : $Int) +// CHECK: } // end sil function 'constant_cond_br_true' +sil @constant_cond_br_true : $@convention(thin) (Int, Int) -> Int { +bb0(%0 : $Int, %1 : $Int): + %2 = integer_literal $Builtin.Int1, 1 + cond_br %2, bb1(%0 : $Int), bb2(%1 : $Int) + +bb1(%4 : $Int): + br bb3(%4 : $Int) + +bb2(%6 : $Int): + br bb3(%6 : $Int) + +bb3(%8 : $Int): + return %8 : $Int +} + +// CHECK-LABEL: sil @constant_cond_br_false +// CHECK: bb0(%0 : $Int, %1 : $Int): +// CHECK-NEXT: br bb1(%1 : $Int) +// CHECK: } // end sil function 'constant_cond_br_false' +sil @constant_cond_br_false : $@convention(thin) (Int, Int) -> Int { +bb0(%0 : $Int, %1 : $Int): + %2 = integer_literal $Builtin.Int1, 0 + cond_br %2, bb1(%0 : $Int), bb2(%1 : $Int) + +bb1(%4 : $Int): + br bb3(%4 : $Int) + +bb2(%6 : $Int): + br bb3(%6 : $Int) + +bb3(%8 : $Int): + return %8 : $Int +} + +// CHECK-LABEL: sil @non_constant_cond_br +// CHECK: bb0(%0 : $Int, %1 : $Int, %2 : $Builtin.Int1): +// CHECK-NEXT: cond_br %2 +// CHECK: } // end sil function 'non_constant_cond_br' +sil @non_constant_cond_br : $@convention(thin) (Int, Int, Builtin.Int1) -> Int { +bb0(%0 : $Int, %1 : $Int, %2 : $Builtin.Int1): + cond_br %2, bb1(%0 : $Int), bb2(%1 : $Int) + +bb1(%4 : $Int): + br bb3(%4 : $Int) + +bb2(%6 : $Int): + br bb3(%6 : $Int) + +bb3(%8 : $Int): + return %8 : $Int +} + From 20edeb6a8c63fec1492298baa9c1e0c879dabcb5 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 7 Feb 2023 15:38:20 +0100 Subject: [PATCH 72/98] Swift Optimizer: add Onone simplification of unchecked_enum_data instructions --- .../InstructionSimplification/CMakeLists.txt | 3 +- .../SimplifyUncheckedEnumData.swift | 25 ++++++ .../simplify_unchecked_enum_data.sil | 86 +++++++++++++++++++ 3 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyUncheckedEnumData.swift create mode 100644 test/SILOptimizer/simplify_unchecked_enum_data.sil diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt index bcab368cbd94a..3c1c2a2b4f1fc 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt @@ -13,4 +13,5 @@ swift_compiler_sources(Optimizer SimplifyBuiltin.swift SimplifyCondBranch.swift SimplifyGlobalValue.swift - SimplifyStrongRetainRelease.swift) + SimplifyStrongRetainRelease.swift + SimplifyUncheckedEnumData.swift) diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyUncheckedEnumData.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyUncheckedEnumData.swift new file mode 100644 index 0000000000000..7c89afec00f98 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyUncheckedEnumData.swift @@ -0,0 +1,25 @@ +//===--- SimplifyUncheckedEnumData.swift ----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SIL + +extension UncheckedEnumDataInst : OnoneSimplifyable { + func simplify(_ context: SimplifyContext) { + guard let enumInst = operand as? EnumInst else { + return + } + if caseIndex != enumInst.caseIndex { + return + } + context.tryReplaceRedundantInstructionPair(first: enumInst, second: self, with: enumInst.operand) + } +} diff --git a/test/SILOptimizer/simplify_unchecked_enum_data.sil b/test/SILOptimizer/simplify_unchecked_enum_data.sil new file mode 100644 index 0000000000000..911ff52438461 --- /dev/null +++ b/test/SILOptimizer/simplify_unchecked_enum_data.sil @@ -0,0 +1,86 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -onone-simplification -simplify-instruction=unchecked_enum_data | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ONONE +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplification -simplify-instruction=unchecked_enum_data | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-O + +// REQUIRES: swift_in_compiler + +import Swift +import Builtin + +enum E { + case A(String) + case B(String) +} + +// CHECK-LABEL: sil @forward_enum_data +// CHECK: return %0 +// CHECK: } // end sil function 'forward_enum_data' +sil @forward_enum_data : $@convention(thin) (@owned String) -> @owned String { +bb0(%0 : $String): + %1 = enum $E, #E.B!enumelt, %0 : $String + %2 = unchecked_enum_data %1 : $E, #E.B!enumelt + return %2 : $String +} + +// CHECK-LABEL: sil @wrong_case +// CHECK: %1 = enum +// CHECK: %2 = unchecked_enum_data %1 +// CHECK: return %2 +// CHECK: } // end sil function 'wrong_case' +sil @wrong_case : $@convention(thin) (@owned String) -> @owned String { +bb0(%0 : $String): + %1 = enum $E, #E.B!enumelt, %0 : $String + // Strictly speaking this is illegal SIL. + %2 = unchecked_enum_data %1 : $E, #E.A!enumelt + return %2 : $String +} + +// CHECK-LABEL: sil [ossa] @forward_borrowed_enum_data +// CHECK: %1 = copy_value %0 +// CHECK: return %1 +// CHECK: } // end sil function 'forward_borrowed_enum_data' +sil [ossa] @forward_borrowed_enum_data : $@convention(thin) (@guaranteed String) -> @owned String { +bb0(%0 : @guaranteed $String): + %1 = enum $E, #E.B!enumelt, %0 : $String + %2 = unchecked_enum_data %1 : $E, #E.B!enumelt + %3 = copy_value %2 : $String + return %3 : $String +} + +// CHECK-LABEL: sil [ossa] @forward_owned_enum_data : +// CHECK: return %0 +// CHECK: } // end sil function 'forward_owned_enum_data' +sil [ossa] @forward_owned_enum_data : $@convention(thin) (@owned String) -> @owned String { +bb0(%0 : @owned $String): + %1 = enum $E, #E.B!enumelt, %0 : $String + %2 = unchecked_enum_data %1 : $E, #E.B!enumelt + return %2 : $String +} + +// CHECK-LABEL: sil [ossa] @dont_forward_owned_enum_data_with_uses : +// CHECK %1 = enum +// CHECK %4 = unchecked_enum_data %1 +// CHECK return %4 +// CHECK: } // end sil function 'dont_forward_owned_enum_data_with_uses' +sil [ossa] @dont_forward_owned_enum_data_with_uses : $@convention(thin) (@owned String) -> @owned String { +bb0(%0 : @owned $String): + %1 = enum $E, #E.B!enumelt, %0 : $String + %2 = begin_borrow %1 : $E + end_borrow %2 : $E + %4 = unchecked_enum_data %1 : $E, #E.B!enumelt + return %4 : $String +} + +// CHECK-LABEL: sil [ossa] @forward_owned_enum_data_with_debug_use +// CHECK-ONONE: %1 = enum +// CHECK-ONONE: %3 = unchecked_enum_data %1 +// CHECK-ONONE: return %3 +// CHECK-O: return %0 +// CHECK: } // end sil function 'forward_owned_enum_data_with_debug_use' +sil [ossa] @forward_owned_enum_data_with_debug_use : $@convention(thin) (@owned String) -> @owned String { +bb0(%0 : @owned $String): + %1 = enum $E, #E.B!enumelt, %0 : $String + debug_value %1 : $E, let, name "e" + %3 = unchecked_enum_data %1 : $E, #E.B!enumelt + return %3 : $String +} + From 1c70060e011b66ff165e8919694b8450fba9e0e7 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 7 Feb 2023 15:39:44 +0100 Subject: [PATCH 73/98] Swift Optimizer: add Onone simplification of struct_extract instructions --- .../InstructionSimplification/CMakeLists.txt | 1 + .../SimplifyStructExtract.swift | 23 ++++++++++ test/SILOptimizer/simplify_struct_extract.sil | 44 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStructExtract.swift create mode 100644 test/SILOptimizer/simplify_struct_extract.sil diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt index 3c1c2a2b4f1fc..f9ade116fb17a 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt @@ -14,4 +14,5 @@ swift_compiler_sources(Optimizer SimplifyCondBranch.swift SimplifyGlobalValue.swift SimplifyStrongRetainRelease.swift + SimplifyStructExtract.swift SimplifyUncheckedEnumData.swift) diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStructExtract.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStructExtract.swift new file mode 100644 index 0000000000000..4a9679f8482c2 --- /dev/null +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStructExtract.swift @@ -0,0 +1,23 @@ +//===--- SimplifyStructExtract.swift --------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SIL + +extension StructExtractInst : OnoneSimplifyable { + func simplify(_ context: SimplifyContext) { + guard let structInst = operand as? StructInst else { + return + } + context.tryReplaceRedundantInstructionPair(first: structInst, second: self, + with: structInst.operands[fieldIndex].value) + } +} diff --git a/test/SILOptimizer/simplify_struct_extract.sil b/test/SILOptimizer/simplify_struct_extract.sil new file mode 100644 index 0000000000000..d95809f28d676 --- /dev/null +++ b/test/SILOptimizer/simplify_struct_extract.sil @@ -0,0 +1,44 @@ +// RUN: %target-sil-opt -enable-sil-verify-all %s -onone-simplification -simplify-instruction=struct_extract | %FileCheck %s + +// REQUIRES: swift_in_compiler + +import Swift +import Builtin + +struct S { + var a: String + var b: String +} + +// CHECK-LABEL: sil @struct_extract_first_field +// CHECK: return %0 +// CHECK: } // end sil function 'struct_extract_first_field' +sil @struct_extract_first_field : $@convention(thin) (@owned String, @guaranteed String) -> @owned String { +bb0(%0 : $String, %1 : $String): + %2 = struct $S (%0 : $String, %1 : $String) + %3 = struct_extract %2 : $S, #S.a + return %3 : $String +} + +// CHECK-LABEL: sil @struct_extract_second_field +// CHECK: return %1 +// CHECK: } // end sil function 'struct_extract_second_field' +sil @struct_extract_second_field : $@convention(thin) (@guaranteed String, @owned String) -> @owned String { +bb0(%0 : $String, %1 : $String): + %2 = struct $S (%0 : $String, %1 : $String) + %3 = struct_extract %2 : $S, #S.b + return %3 : $String +} + +// CHECK-LABEL: sil [ossa] @struct_extract_ossa +// CHECK: %2 = copy_value %0 +// CHECK: return %2 +// CHECK: } // end sil function 'struct_extract_ossa' +sil [ossa] @struct_extract_ossa : $@convention(thin) (@guaranteed String, @guaranteed String) -> @owned String { +bb0(%0 : @guaranteed $String, %1 : @guaranteed $String): + %2 = struct $S (%0 : $String, %1 : $String) + %3 = struct_extract %2 : $S, #S.a + %4 = copy_value %3 : $String + return %4 : $String +} + From 85210a4e912433991084486817ead3f6d780e51f Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 7 Feb 2023 15:45:53 +0100 Subject: [PATCH 74/98] Swift Optimizer: make some SILCombine optimizations available for the "Simplification" pass * begin_cow_mutation * global_value * strong_retain and strong_release So far, those simplifications did only run in SILCombine. Now they are also considered in the swift Simplification pass. --- .../InstructionSimplification/SimplifyBeginCOWMutation.swift | 2 +- .../InstructionSimplification/SimplifyGlobalValue.swift | 2 +- .../SimplifyStrongRetainRelease.swift | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginCOWMutation.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginCOWMutation.swift index 550441c3732ef..b74544d2eba05 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginCOWMutation.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginCOWMutation.swift @@ -12,7 +12,7 @@ import SIL -extension BeginCOWMutationInst : SILCombineSimplifyable { +extension BeginCOWMutationInst : Simplifyable, SILCombineSimplifyable { func simplify(_ context: SimplifyContext) { /// The buffer of an empty Array/Set/Dictionary singleton is known to be not diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyGlobalValue.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyGlobalValue.swift index 20070b44a8f72..8831385875f2d 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyGlobalValue.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyGlobalValue.swift @@ -18,7 +18,7 @@ import SIL // Note that `simplifyStrongRetainPass` and `simplifyStrongReleasePass` can // even remove "unbalanced" retains/releases of a `global_value`, but this // requires a minimum deployment target. -extension GlobalValueInst : SILCombineSimplifyable { +extension GlobalValueInst : Simplifyable, SILCombineSimplifyable { func simplify(_ context: SimplifyContext) { var users = Stack(context) defer { users.deinitialize() } diff --git a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStrongRetainRelease.swift b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStrongRetainRelease.swift index 6a8e10b4176a2..7b4fd9d2dbba5 100644 --- a/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStrongRetainRelease.swift +++ b/SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyStrongRetainRelease.swift @@ -12,7 +12,7 @@ import SIL -extension StrongRetainInst : SILCombineSimplifyable { +extension StrongRetainInst : Simplifyable, SILCombineSimplifyable { func simplify(_ context: SimplifyContext) { if isNotReferenceCounted(value: operand) { context.erase(instruction: self) @@ -44,7 +44,7 @@ extension StrongRetainInst : SILCombineSimplifyable { } } -extension StrongReleaseInst : SILCombineSimplifyable { +extension StrongReleaseInst : Simplifyable, SILCombineSimplifyable { func simplify(_ context: SimplifyContext) { let op = operand if isNotReferenceCounted(value: op) { From d25b1ed834d757a7b966ceae8d352c8e9962a8cd Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 7 Feb 2023 16:15:27 +0100 Subject: [PATCH 75/98] Optimizer: Replace the MandatoryCombine pass with a Simplification pass, which is implemented in Swift The Swift Simplification pass can do more than the old MandatoryCombine pass: simplification of more instruction types and dead code elimination. The result is a better -Onone performance while still keeping debug info consistent. Currently following code patterns are simplified: * `struct` -> `struct_extract` * `enum` -> `unchecked_enum_data` * `partial_apply` -> `apply` * `br` to a 1:1 related block * `cond_br` with a constant condition * `isConcrete` and `is_same_metadata` builtins More simplifications can be added in the future. rdar://96708429 rdar://104562580 --- .../swift/SILOptimizer/PassManager/Passes.def | 4 - lib/SILOptimizer/Mandatory/CMakeLists.txt | 1 - .../Mandatory/MandatoryCombine.cpp | 416 ------------------ lib/SILOptimizer/PassManager/PassPipeline.cpp | 10 +- ...ifferentiability_witness_function_inst.sil | 2 +- .../SILOptimizer/differentiation_sil.swift | 2 +- test/ClangImporter/macro_literals.swift | 2 +- .../keypath_dynamic_member_lookup.swift | 2 +- ...distributed_actor_default_init_sil_4.swift | 7 +- test/IRGen/builtin_conflict.sil | 2 +- test/IRGen/enum.sil | 4 +- test/IRGen/enum_future.sil | 4 +- test/IRGen/fixed_layout_class.swift | 26 +- test/IRGen/foreign_types.sil | 2 +- test/IRGen/foreign_types_future.sil | 2 +- test/IRGen/global_resilience.sil | 4 +- test/IRGen/indexing.sil | 2 +- test/IRGen/indirect_argument.sil | 2 +- test/IRGen/metatype.sil | 2 +- ...ed_mode_class_with_unimportable_fields.sil | 4 +- test/IRGen/moveonly_deinits.swift | 3 +- test/IRGen/objc_block_storage.sil | 2 +- test/IRGen/partial_apply_forwarder.sil | 4 +- test/IRGen/polymorphic_builtins.swift | 4 +- .../relative_protocol_witness_table.swift | 2 +- test/IRGen/select_enum.sil | 2 +- test/IRGen/sil_linkage.sil | 4 +- test/IRGen/super.sil | 2 +- test/IRGen/witness_method_phi.sil | 2 +- .../Cxx/static/static-member-var-silgen.swift | 5 +- test/Interpreter/imported_objc_generics.swift | 1 + test/Interpreter/moveonly_bufferview.swift | 3 +- test/SIL/Serialization/globals.sil | 2 +- test/SIL/Serialization/public_non_abi.sil | 4 +- test/SILGen/builtins.swift | 3 +- test/SILGen/copy_operator.swift | 4 +- test/SILGen/moveonly_deinits.swift | 5 +- .../closure_lifetime_fixup_objc.swift | 8 +- .../constant_propagation_stdlib.swift | 5 +- .../definite_init_failable_initializers.swift | 11 +- ...virt_single_module_in_multiple_files.swift | 6 +- ...existential_specializer_indirect_class.sil | 18 +- test/SILOptimizer/generic_inline_self.swift | 4 +- test/SILOptimizer/mandatory_combine_canon.sil | 2 +- test/SILOptimizer/mandatory_combiner.sil | 4 +- test/SILOptimizer/mandatory_combiner_opt.sil | 2 +- test/SILOptimizer/mandatory_inlining.swift | 1 - .../mandatory_inlining_reasync.swift | 10 +- test/SILOptimizer/onone_simplifications.swift | 39 ++ test/SILOptimizer/optimizer_counters.sil | 8 +- test/SILOptimizer/ossa_rauw_tests.sil | 12 +- test/SILOptimizer/sil_combine_inst_passes.sil | 2 +- test/Serialization/basic_sil.swift | 2 +- test/Serialization/basic_sil_objc.swift | 2 +- test/Serialization/concurrency_sil.swift | 2 +- test/Serialization/moveonly_deinit.swift | 5 +- ...r_on_import_into_module_without_flag.swift | 3 +- .../sil_partial_apply_ownership.sil | 4 +- test/sil-passpipeline-dump/basic.test-sh | 7 +- utils/swift-autocomplete.bash | 1 - 60 files changed, 164 insertions(+), 546 deletions(-) delete mode 100644 lib/SILOptimizer/Mandatory/MandatoryCombine.cpp create mode 100644 test/SILOptimizer/onone_simplifications.swift diff --git a/include/swift/SILOptimizer/PassManager/Passes.def b/include/swift/SILOptimizer/PassManager/Passes.def index f4fe549596160..a8d0747b20ccf 100644 --- a/include/swift/SILOptimizer/PassManager/Passes.def +++ b/include/swift/SILOptimizer/PassManager/Passes.def @@ -434,10 +434,6 @@ PASS(YieldOnceCheck, "yield-once-check", PASS(OSLogOptimization, "os-log-optimization", "Optimize os log calls") PASS(ForEachLoopUnroll, "for-each-loop-unroll", "Unroll forEach loops over array literals") -PASS(MandatoryCombine, "mandatory-combine", - "Perform mandatory peephole combines") -PASS(OptimizedMandatoryCombine, "optimized-mandatory-combine", - "Perform -O level mandatory peephole combines") PASS(BugReducerTester, "bug-reducer-tester", "sil-bug-reducer Tool Testing by Asserting on a Sentinel Function") PASS(AssemblyVisionRemarkGenerator, "assembly-vision-remark-generator", diff --git a/lib/SILOptimizer/Mandatory/CMakeLists.txt b/lib/SILOptimizer/Mandatory/CMakeLists.txt index 08d6fb4512aca..6b518a9964576 100644 --- a/lib/SILOptimizer/Mandatory/CMakeLists.txt +++ b/lib/SILOptimizer/Mandatory/CMakeLists.txt @@ -38,7 +38,6 @@ target_sources(swiftSILOptimizer PRIVATE RawSILInstLowering.cpp SILGenCleanup.cpp YieldOnceCheck.cpp - MandatoryCombine.cpp OSLogOptimization.cpp MoveOnlyWrappedTypeEliminator.cpp OwnershipModelEliminator.cpp) diff --git a/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp b/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp deleted file mode 100644 index 2ceaf3129fe49..0000000000000 --- a/lib/SILOptimizer/Mandatory/MandatoryCombine.cpp +++ /dev/null @@ -1,416 +0,0 @@ -//===------- MandatoryCombiner.cpp ----------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// -/// Defines the MandatoryCombiner function transform. The pass contains basic -/// instruction combines to be performed at the beginning of both the Onone and -/// also the performance pass pipelines, after the diagnostics passes have been -/// run. It is intended to be run before and to be independent of other -/// transforms. -/// -/// The intention of this pass is to be a place for mandatory peepholes that -/// are not needed for diagnostics. Please put any such peepholes here instead -/// of in the diagnostic passes. -/// -//===----------------------------------------------------------------------===// - -#define DEBUG_TYPE "sil-mandatory-combiner" - -#include "swift/Basic/LLVM.h" -#include "swift/Basic/STLExtras.h" -#include "swift/SIL/BasicBlockUtils.h" -#include "swift/SIL/SILInstructionWorklist.h" -#include "swift/SIL/SILVisitor.h" -#include "swift/SIL/BasicBlockDatastructures.h" -#include "swift/SILOptimizer/PassManager/Passes.h" -#include "swift/SILOptimizer/PassManager/Transforms.h" -#include "swift/SILOptimizer/Utils/CanonicalizeInstruction.h" -#include "swift/SILOptimizer/Utils/InstOptUtils.h" -#include "swift/SILOptimizer/Utils/StackNesting.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/Statistic.h" -#include "llvm/Support/raw_ostream.h" -#include - -using namespace swift; - -//===----------------------------------------------------------------------===// -// Utility -//===----------------------------------------------------------------------===// - -/// \returns whether all the values are of trivial type in the provided -/// function. -template -static bool areAllValuesTrivial(Values values, SILFunction &function) { - return llvm::all_of(values, [&](SILValue value) -> bool { - return value->getType().isTrivial(function); - }); -} - -//===----------------------------------------------------------------------===// -// CanonicalizeInstruction subclass for use in Mandatory Combiner. -//===----------------------------------------------------------------------===// - -namespace { - -class MandatoryCombineCanonicalize final : CanonicalizeInstruction { -public: - using Worklist = SmallSILInstructionWorklist<256>; - -private: - Worklist &worklist; - bool changed = false; - -public: - MandatoryCombineCanonicalize(Worklist &worklist, DeadEndBlocks &deadEndBlocks) - : CanonicalizeInstruction(DEBUG_TYPE, deadEndBlocks), worklist(worklist) { - } - - void notifyNewInstruction(SILInstruction *inst) override { - worklist.add(inst); - worklist.addUsersOfAllResultsToWorklist(inst); - changed = true; - } - - // Just delete the given 'inst' and record its operands. The callback isn't - // allowed to mutate any other instructions. - void killInstruction(SILInstruction *inst) override { - worklist.eraseSingleInstFromFunction(*inst, - /*AddOperandsToWorklist*/ true); - changed = true; - } - - void notifyHasNewUsers(SILValue value) override { - if (worklist.size() < 10000) { - worklist.addUsersToWorklist(value); - } - changed = true; - } - - bool tryCanonicalize(SILInstruction *inst) { - changed = false; - canonicalize(inst); - return changed; - } -}; - -} // anonymous namespace - -//===----------------------------------------------------------------------===// -// MandatoryCombiner Interface -//===----------------------------------------------------------------------===// - -namespace { - -class MandatoryCombiner final - : public SILInstructionVisitor { - - bool compilingWithOptimization; - - using Worklist = SmallSILInstructionWorklist<256>; - - /// The list of instructions remaining to visit, perhaps to combine. - Worklist worklist; - - /// Whether any changes have been made. - bool madeChange; - - /// Set to true if some alloc/dealloc_stack instruction are inserted and at - /// the end of the run stack nesting needs to be corrected. - bool invalidatedStackNesting = false; - - /// The number of times that the worklist has been processed. - unsigned iteration; - - InstModCallbacks instModCallbacks; - SmallVectorImpl &createdInstructions; - SmallVector instructionsPendingDeletion; - DeadEndBlocks &deadEndBlocks; - -public: - MandatoryCombiner(bool optimized, - SmallVectorImpl &createdInstructions, - DeadEndBlocks &deadEndBlocks) - : compilingWithOptimization(optimized), worklist("MC"), madeChange(false), - iteration(0), - instModCallbacks(), - createdInstructions(createdInstructions), - deadEndBlocks(deadEndBlocks) { - instModCallbacks = InstModCallbacks() - .onDelete([&](SILInstruction *instruction) { - worklist.erase(instruction); - instructionsPendingDeletion.push_back(instruction); - }) - .onCreateNewInst([&](SILInstruction *instruction) { - worklist.add(instruction); - }) - .onSetUseValue([this](Operand *use, SILValue newValue) { - use->set(newValue); - worklist.add(use->getUser()); - }); - }; - - void addReachableCodeToWorklist(SILFunction &function); - - /// \return whether a change was made. - bool doOneIteration(SILFunction &function, unsigned iteration); - - void clear() { - iteration = 0; - worklist.resetChecked(); - madeChange = false; - } - - /// Applies the MandatoryCombiner to the provided function. - /// - /// \param function the function to which to apply the MandatoryCombiner. - /// - /// \return whether a change was made. - bool runOnFunction(SILFunction &function) { - bool changed = false; - - while (doOneIteration(function, iteration)) { - changed = true; - ++iteration; - } - - if (invalidatedStackNesting) { - StackNesting::fixNesting(&function); - } - - return changed; - } - - /// Base visitor that does not do anything. - SILInstruction *visitSILInstruction(SILInstruction *) { return nullptr; } - SILInstruction *visitApplyInst(ApplyInst *instruction); -}; - -} // end anonymous namespace - -//===----------------------------------------------------------------------===// -// MandatoryCombiner Non-Visitor Utility Methods -//===----------------------------------------------------------------------===// - -static llvm::cl::opt EnableCanonicalizationAndTrivialDCE( - "sil-mandatory-combine-enable-canon-and-simple-dce", llvm::cl::Hidden, - llvm::cl::init(false), - llvm::cl::desc("An option for compiler developers that cause the Mandatory " - "Combiner to be more aggressive at eliminating trivially " - "dead code and canonicalizing SIL")); - -void MandatoryCombiner::addReachableCodeToWorklist(SILFunction &function) { - BasicBlockWorklist blockWorklist(function.getEntryBlock()); - SmallVector initialInstructionWorklist; - - while (SILBasicBlock *block = blockWorklist.pop()) { - for (auto iterator = block->begin(), end = block->end(); iterator != end;) { - auto *instruction = &*iterator; - ++iterator; - - if (isInstructionTriviallyDead(instruction)) { - if (EnableCanonicalizationAndTrivialDCE) { - if (compilingWithOptimization) { - instruction->replaceAllUsesOfAllResultsWithUndef(); - instruction->eraseFromParent(); - } - } - continue; - } - - initialInstructionWorklist.push_back(instruction); - } - - for (SILBasicBlock *succ : block->getSuccessors()) { - blockWorklist.pushIfNotVisited(succ); - } - } - - worklist.addInitialGroup(initialInstructionWorklist); -} - -bool MandatoryCombiner::doOneIteration(SILFunction &function, - unsigned iteration) { - madeChange = false; - - addReachableCodeToWorklist(function); - MandatoryCombineCanonicalize mcCanonicalize(worklist, deadEndBlocks); - - while (!worklist.isEmpty()) { - auto *instruction = worklist.pop_back_val(); - if (instruction == nullptr) { - continue; - } - - if (EnableCanonicalizationAndTrivialDCE) { - if (compilingWithOptimization) { - if (isInstructionTriviallyDead(instruction)) { - worklist.eraseInstFromFunction(*instruction); - madeChange = true; - continue; - } - } - - if (mcCanonicalize.tryCanonicalize(instruction)) { - madeChange = true; - continue; - } - } - -#ifndef NDEBUG - std::string instructionDescription; -#endif - LLVM_DEBUG(llvm::raw_string_ostream SS(instructionDescription); - instruction->print(SS); instructionDescription = SS.str();); - LLVM_DEBUG(llvm::dbgs() - << "MC: Visiting: " << instructionDescription << '\n'); - - if (auto replacement = visit(instruction)) { - worklist.replaceInstructionWithInstruction(instruction, replacement -#ifndef NDEBUG - , - instructionDescription -#endif - ); - madeChange = true; - } - - for (SILInstruction *instruction : instructionsPendingDeletion) { - worklist.eraseInstFromFunction(*instruction); - madeChange = true; - } - instructionsPendingDeletion.clear(); - - // Our tracking list has been accumulating instructions created by the - // SILBuilder during this iteration. Go through the tracking list and add - // its contents to the worklist and then clear said list in preparation - // for the next iteration. - for (SILInstruction *instruction : createdInstructions) { - if (instruction->isDeleted()) - continue; - - LLVM_DEBUG(llvm::dbgs() << "MC: add " << *instruction - << " from tracking list to worklist\n"); - worklist.add(instruction); - madeChange = true; - } - createdInstructions.clear(); - } - - worklist.resetChecked(); - return madeChange; -} - -//===----------------------------------------------------------------------===// -// MandatoryCombiner Visitor Methods -//===----------------------------------------------------------------------===// - -SILInstruction *MandatoryCombiner::visitApplyInst(ApplyInst *instruction) { - - // Apply this pass only to partial applies all of whose arguments are - // trivial. - auto calledValue = instruction->getCallee(); - if (calledValue == nullptr) { - return nullptr; - } - auto fullApplyCallee = calledValue->getDefiningInstruction(); - if (fullApplyCallee == nullptr) { - return nullptr; - } - auto partialApply = dyn_cast(fullApplyCallee); - if (partialApply == nullptr) { - return nullptr; - } - auto *function = partialApply->getCalleeFunction(); - if (function == nullptr) { - return nullptr; - } - ApplySite fullApplySite(instruction); - auto fullApplyArguments = fullApplySite.getArguments(); - if (!areAllValuesTrivial(fullApplyArguments, *function)) { - return nullptr; - } - auto partialApplyArguments = ApplySite(partialApply).getArguments(); - if (!areAllValuesTrivial(partialApplyArguments, *function)) { - return nullptr; - } - - auto callee = partialApply->getCallee(); - - ApplySite partialApplySite(partialApply); - - SmallVector argsVec; - llvm::copy(fullApplyArguments, std::back_inserter(argsVec)); - llvm::copy(partialApplyArguments, std::back_inserter(argsVec)); - - SILBuilderWithScope builder(instruction, &createdInstructions); - ApplyInst *replacement = builder.createApply( - /*Loc=*/instruction->getDebugLocation().getLocation(), /*Fn=*/callee, - /*Subs=*/partialApply->getSubstitutionMap(), - /*Args*/ argsVec, - /*isNonThrowing=*/instruction->getApplyOptions(), - /*SpecializationInfo=*/partialApply->getSpecializationInfo()); - - worklist.replaceInstructionWithInstruction(instruction, replacement -#ifndef NDEBUG - , - /*instructionDescription=*/"" -#endif - ); - if (tryDeleteDeadClosure(partialApply, instModCallbacks)) { - invalidatedStackNesting = true; - } - return nullptr; -} - -//===----------------------------------------------------------------------===// -// Top Level Entrypoint -//===----------------------------------------------------------------------===// - -namespace { - -class MandatoryCombine final : public SILFunctionTransform { - bool optimized; - SmallVector createdInstructions; - -public: - MandatoryCombine(bool optimized) : optimized(optimized) {} - - void run() override { - auto *function = getFunction(); - - // If this function is an external declaration, bail. We only want to visit - // functions with bodies. - if (function->isExternalDeclaration()) { - return; - } - - DeadEndBlocks deadEndBlocks(function); - MandatoryCombiner combiner(optimized, createdInstructions, deadEndBlocks); - bool madeChange = combiner.runOnFunction(*function); - - if (madeChange) { - invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions); - } - } -}; - -} // end anonymous namespace - -SILTransform *swift::createMandatoryCombine() { - return new MandatoryCombine(/*optimized*/ false); -} - -SILTransform *swift::createOptimizedMandatoryCombine() { - return new MandatoryCombine(/*optimized*/ true); -} diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 5c4549edc439b..771f88f392411 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -572,7 +572,7 @@ static void addPrepareOptimizationsPipeline(SILPassPipelinePlan &P) { #endif P.addForEachLoopUnroll(); - P.addOptimizedMandatoryCombine(); + P.addSimplification(); P.addAccessMarkerElimination(); } @@ -971,7 +971,7 @@ SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) { // in the editor. P.startPipeline("Non-Diagnostic Mandatory Optimizations"); P.addForEachLoopUnroll(); - P.addMandatoryCombine(); + P.addOnoneSimplification(); // TODO: MandatoryARCOpts should be subsumed by CopyPropagation. There should // be no need to run another analysis of copies at -Onone. @@ -1011,6 +1011,12 @@ SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) { // In Onone builds, do a function-local analysis in a function pass. P.addFunctionStackProtection(); + // This is mainly there to optimize `Builtin.isConcrete`, which must not be + // constant folded before any generic specialization. + P.addLateOnoneSimplification(); + + P.addCleanupDebugSteps(); + // Has only an effect if the -sil-based-debuginfo option is specified. P.addSILDebugInfoGenerator(); diff --git a/test/AutoDiff/SIL/differentiability_witness_function_inst.sil b/test/AutoDiff/SIL/differentiability_witness_function_inst.sil index 832c1bd799f7b..c3246c428caba 100644 --- a/test/AutoDiff/SIL/differentiability_witness_function_inst.sil +++ b/test/AutoDiff/SIL/differentiability_witness_function_inst.sil @@ -15,7 +15,7 @@ // IRGen test. -// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s --check-prefix=IRGEN --check-prefix %target-cpu +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=Simplification -emit-ir %s | %FileCheck %s --check-prefix=IRGEN --check-prefix %target-cpu // NOTE: `%target-cpu`-specific FileCheck lines exist because lowered function types in LLVM IR differ between architectures. // `shell` is required only to run `sed` as a diff --git a/test/AutoDiff/SILOptimizer/differentiation_sil.swift b/test/AutoDiff/SILOptimizer/differentiation_sil.swift index a02728812bed5..92454bcfde542 100644 --- a/test/AutoDiff/SILOptimizer/differentiation_sil.swift +++ b/test/AutoDiff/SILOptimizer/differentiation_sil.swift @@ -23,7 +23,7 @@ func basic(_ x: Float) -> Float { x } @_silgen_name("test_differentiable_function") func testDifferentiableFunction() { - let _: @differentiable(reverse) (Float) -> Float = basic + let a: @differentiable(reverse) (Float) -> Float = basic } // CHECK-SILGEN-LABEL: sil hidden [ossa] @test_differentiable_function : $@convention(thin) () -> () { diff --git a/test/ClangImporter/macro_literals.swift b/test/ClangImporter/macro_literals.swift index d8368e6349678..6aba0502f5723 100644 --- a/test/ClangImporter/macro_literals.swift +++ b/test/ClangImporter/macro_literals.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -Xllvm -sil-print-debuginfo -emit-sil %s | %FileCheck %s +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -Xllvm -sil-disable-pass=Simplification -Xllvm -sil-print-debuginfo -emit-sil %s | %FileCheck %s import macros diff --git a/test/Constraints/keypath_dynamic_member_lookup.swift b/test/Constraints/keypath_dynamic_member_lookup.swift index 36e5bb9489bdd..d28d9767e5cdc 100644 --- a/test/Constraints/keypath_dynamic_member_lookup.swift +++ b/test/Constraints/keypath_dynamic_member_lookup.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-sil -verify %s | %FileCheck %s +// RUN: %target-swift-frontend -emit-sil -verify -Xllvm -sil-disable-pass=simplification %s | %FileCheck %s struct Point { let x: Int diff --git a/test/Distributed/SIL/distributed_actor_default_init_sil_4.swift b/test/Distributed/SIL/distributed_actor_default_init_sil_4.swift index 1bd8d6e874f94..f85f298595bc2 100644 --- a/test/Distributed/SIL/distributed_actor_default_init_sil_4.swift +++ b/test/Distributed/SIL/distributed_actor_default_init_sil_4.swift @@ -4,6 +4,8 @@ // REQUIRES: concurrency // REQUIRES: distributed +// REQUIRES: swift_in_compiler + /// The convention in this test is that the Swift declaration comes before its FileCheck lines. import Distributed @@ -37,16 +39,13 @@ distributed actor MyDistActor { // CHECK: hop_to_executor {{%[0-9]+}} // CHECK: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx0B00bC0RzAA0C7AddressV2IDRtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@guaranteed τ_0_0, @guaranteed FakeActorSystem) -> () // CHECK: = apply [[READY_FN]] - // CHECK: br [[RET_BB:bb[0-9]+]] + // CHECK: return // CHECK: [[FAIL_BB]]: // CHECK: [[RESIGN_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV8resignIDyyAA0C7AddressVF : $@convention(method) (@guaranteed ActorAddress, @guaranteed FakeActorSystem) -> () // CHECK: = apply [[RESIGN_FN]] // CHECK: builtin "destroyDefaultActor" // CHECK: throw {{%[0-9]+}} : $any Error - - // CHECK: [[RET_BB]]: - // CHECK: return // CHECK: } // end sil function '$s14default_deinit11MyDistActorC24system_async_fail_throws4condACSg015FakeDistributedE7Systems0kE6SystemV_SbtYaKcfc' } diff --git a/test/IRGen/builtin_conflict.sil b/test/IRGen/builtin_conflict.sil index 314020a8b3fbe..c1ded65d0eea4 100644 --- a/test/IRGen/builtin_conflict.sil +++ b/test/IRGen/builtin_conflict.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-ir -parse-sil %s -parse-stdlib | %FileCheck %s +// RUN: %target-swift-frontend -emit-ir -parse-sil %s -parse-stdlib -Xllvm -sil-disable-pass=simplification | %FileCheck %s import Builtin import Swift diff --git a/test/IRGen/enum.sil b/test/IRGen/enum.sil index 1db6f2f1b66cb..fc30759bfe910 100644 --- a/test/IRGen/enum.sil +++ b/test/IRGen/enum.sil @@ -1,7 +1,7 @@ // #if directives don't work with SIL keywords, therefore please put ObjC tests // in `enum_objc.sil`. -// RUN: %target-swift-frontend %s -disable-type-layout -disable-generic-metadata-prespecialization -disable-generic-metadata-prespecialization -gnone -emit-ir -disable-diagnostic-passes -enable-objc-interop | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-objc --check-prefix=CHECK-objc-%target-ptrsize --check-prefix=CHECK-objc-%target-ptrsize-simulator-%target-is-simulator -DWORD=i%target-ptrsize -// RUN: %target-swift-frontend %s -disable-type-layout -disable-generic-metadata-prespecialization -gnone -emit-ir -disable-diagnostic-passes -disable-objc-interop | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-native --check-prefix=CHECK-native-%target-ptrsize -DWORD=i%target-ptrsize +// RUN: %target-swift-frontend %s -Xllvm -sil-disable-pass=simplification -disable-type-layout -disable-generic-metadata-prespecialization -disable-generic-metadata-prespecialization -gnone -emit-ir -disable-diagnostic-passes -enable-objc-interop | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-objc --check-prefix=CHECK-objc-%target-ptrsize --check-prefix=CHECK-objc-%target-ptrsize-simulator-%target-is-simulator -DWORD=i%target-ptrsize +// RUN: %target-swift-frontend %s -Xllvm -sil-disable-pass=simplification -disable-type-layout -disable-generic-metadata-prespecialization -gnone -emit-ir -disable-diagnostic-passes -disable-objc-interop | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-native --check-prefix=CHECK-native-%target-ptrsize -DWORD=i%target-ptrsize // REQUIRES: CPU=i386 || CPU=x86_64 diff --git a/test/IRGen/enum_future.sil b/test/IRGen/enum_future.sil index 67a2acb1cabad..556a5a9dbe487 100644 --- a/test/IRGen/enum_future.sil +++ b/test/IRGen/enum_future.sil @@ -1,7 +1,7 @@ // #if directives don't work with SIL keywords, therefore please put ObjC tests // in `enum_objc.sil`. -// RUN: %target-swift-frontend -disable-type-layout -prespecialize-generic-metadata %s -target %module-target-future -gnone -emit-ir -disable-diagnostic-passes -enable-objc-interop | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-objc --check-prefix=CHECK-objc-%target-ptrsize --check-prefix=CHECK-objc-%target-ptrsize-simulator-%target-is-simulator -DWORD=i%target-ptrsize -// RUN: %target-swift-frontend -disable-type-layout -prespecialize-generic-metadata %s -target %module-target-future -gnone -emit-ir -disable-diagnostic-passes -disable-objc-interop | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-native --check-prefix=CHECK-native-%target-ptrsize -DWORD=i%target-ptrsize +// RUN: %target-swift-frontend -disable-type-layout -prespecialize-generic-metadata %s -Xllvm -sil-disable-pass=simplification -target %module-target-future -gnone -emit-ir -disable-diagnostic-passes -enable-objc-interop | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-objc --check-prefix=CHECK-objc-%target-ptrsize --check-prefix=CHECK-objc-%target-ptrsize-simulator-%target-is-simulator -DWORD=i%target-ptrsize +// RUN: %target-swift-frontend -disable-type-layout -prespecialize-generic-metadata %s -Xllvm -sil-disable-pass=simplification -target %module-target-future -gnone -emit-ir -disable-diagnostic-passes -disable-objc-interop | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-native --check-prefix=CHECK-native-%target-ptrsize -DWORD=i%target-ptrsize // REQUIRES: CPU=i386 || CPU=x86_64 // REQUIRES: VENDOR=apple || OS=linux-gnu diff --git a/test/IRGen/fixed_layout_class.swift b/test/IRGen/fixed_layout_class.swift index eac71a4c28acc..78fe1f20bfc17 100644 --- a/test/IRGen/fixed_layout_class.swift +++ b/test/IRGen/fixed_layout_class.swift @@ -13,20 +13,20 @@ import fixed_layout_class // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s16class_resilience20useRootClassPropertyyy013fixed_layout_A0026OutsideParentWithResilientF0CF"(%T18fixed_layout_class34OutsideParentWithResilientPropertyC* %0) public func useRootClassProperty(_ o: OutsideParentWithResilientProperty) { // CHECK: getelementptr inbounds %T18fixed_layout_class34OutsideParentWithResilientPropertyC, %T18fixed_layout_class34OutsideParentWithResilientPropertyC* %0, i32 0, i32 1 - _ = o.p + let a = o.p // CHECK: load [[INT]], [[INT]]* @"$s18fixed_layout_class34OutsideParentWithResilientPropertyC1s16resilient_struct4SizeVvpWvd" - _ = o.s + let b = o.s // CHECK: load [[INT]], [[INT]]* @"$s18fixed_layout_class34OutsideParentWithResilientPropertyC5colors5Int32VvpWvd" - _ = o.color + let c = o.color // CHECK: ret void } // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s16class_resilience19useSubclassPropertyyy013fixed_layout_A012OutsideChildCF"(%T18fixed_layout_class12OutsideChildC* %0) public func useSubclassProperty(_ o: OutsideChild) { - // CHECK: getelementptr inbounds %T18fixed_layout_class13OutsideParentC, %T18fixed_layout_class13OutsideParentC* %4, i32 0, i32 1 - _ = o.property + // CHECK: getelementptr inbounds %T18fixed_layout_class13OutsideParentC, %T18fixed_layout_class13OutsideParentC* {{%[0-9]+}}, i32 0, i32 1 + let a = o.property // CHECK: getelementptr inbounds %T18fixed_layout_class12OutsideChildC, %T18fixed_layout_class12OutsideChildC* %0, i32 0, i32 2 - _ = o.childProperty + let b = o.childProperty // CHECK: ret void } @@ -47,7 +47,7 @@ public func useGenericRootClassProperty(_ o: GenericOutsideParent) { // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i8, i8* [[METADATA_ADDR]], [[INT]] [[FIELD_OFFSET_OFFSET]] // CHECK: [[FIELD_OFFSET_PTR:%.*]] = bitcast i8* [[FIELD_OFFSET_ADDR]] to [[INT]]* // CHECK: [[FIELD_OFFSET:%.*]] = load [[INT]], [[INT]]* [[FIELD_OFFSET_PTR]] - _ = o.property + let a = o.property // CHECK: ret void } @@ -55,7 +55,7 @@ public func useGenericRootClassProperty(_ o: GenericOutsideParent) { // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @"$s16class_resilience27useGenericRootClassPropertyyy013fixed_layout_A00D13OutsideParentCySiGF"(%T18fixed_layout_class20GenericOutsideParentCySiG* %0) public func useGenericRootClassProperty(_ o: GenericOutsideParent) { // CHECK: getelementptr inbounds %T18fixed_layout_class20GenericOutsideParentCySiG, %T18fixed_layout_class20GenericOutsideParentCySiG* %0, i32 0, i32 1 - _ = o.property + let a = o.property // CHECK: ret void } @@ -78,7 +78,7 @@ public func useGenericSubclassProperty(_ o: GenericOutsideChild) { // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i8, i8* [[METADATA_ADDR]], [[INT]] [[FIELD_OFFSET_OFFSET]] // CHECK: [[FIELD_OFFSET_PTR:%.*]] = bitcast i8* [[FIELD_OFFSET_ADDR]] to [[INT]]* // CHECK: [[FIELD_OFFSET:%.*]] = load [[INT]], [[INT]]* [[FIELD_OFFSET_PTR]] - _ = o.property + let a = o.property // CHECK: [[METADATA_ADDR:%.*]] = bitcast %T18fixed_layout_class19GenericOutsideChildC* %0 to %swift.type** // CHECK: [[METADATA:%.*]] = load %swift.type*, %swift.type** [[METADATA_ADDR]] @@ -90,7 +90,7 @@ public func useGenericSubclassProperty(_ o: GenericOutsideChild) { // CHECK: [[FIELD_OFFSET_ADDR:%.*]] = getelementptr inbounds i8, i8* [[METADATA_ADDR]], [[INT]] [[FIELD_OFFSET_OFFSET]] // CHECK: [[FIELD_OFFSET_PTR:%.*]] = bitcast i8* [[FIELD_OFFSET_ADDR]] to [[INT]]* // CHECK: [[FIELD_OFFSET:%.*]] = load [[INT]], [[INT]]* [[FIELD_OFFSET_PTR]] - _ = o.childProperty + let b = o.childProperty // CHECK: ret void } @@ -99,10 +99,10 @@ public func useGenericSubclassProperty(_ o: GenericOutsideChild) { public func useGenericSubclassProperty(_ o: GenericOutsideChild) { // CHECK: [[UPCAST:%.*]] = bitcast %T18fixed_layout_class19GenericOutsideChildCySiG* %0 to %T18fixed_layout_class20GenericOutsideParentCySiG* // CHECK: getelementptr inbounds %T18fixed_layout_class20GenericOutsideParentCySiG, %T18fixed_layout_class20GenericOutsideParentCySiG* [[UPCAST]], i32 0, i32 1 - _ = o.property + let a = o.property // CHECK: getelementptr inbounds %T18fixed_layout_class19GenericOutsideChildCySiG, %T18fixed_layout_class19GenericOutsideChildCySiG* %0, i32 0, i32 2 - _ = o.childProperty + let b = o.childProperty // CHECK: ret void } @@ -112,7 +112,7 @@ public func callVirtualMethod(_ o: OutsideParent) { // Note: virtual method calls still use dispatch thunks // CHECK: call swiftcc void @"$s18fixed_layout_class13OutsideParentC6methodyyFTj" - _ = o.method() + let a = o.method() // CHECK: ret void } diff --git a/test/IRGen/foreign_types.sil b/test/IRGen/foreign_types.sil index 0d67f06a31746..df4347c537305 100644 --- a/test/IRGen/foreign_types.sil +++ b/test/IRGen/foreign_types.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -disable-generic-metadata-prespecialization -I %S/Inputs/abi %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -disable-generic-metadata-prespecialization -I %S/Inputs/abi %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize sil_stage canonical import c_layout diff --git a/test/IRGen/foreign_types_future.sil b/test/IRGen/foreign_types_future.sil index 19c5a56369e7d..41895b0056256 100644 --- a/test/IRGen/foreign_types_future.sil +++ b/test/IRGen/foreign_types_future.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -prespecialize-generic-metadata -target %module-target-future -I %S/Inputs/abi %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -prespecialize-generic-metadata -target %module-target-future -I %S/Inputs/abi %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize // REQUIRES: VENDOR=apple || OS=linux-gnu // UNSUPPORTED: CPU=i386 && OS=ios diff --git a/test/IRGen/global_resilience.sil b/test/IRGen/global_resilience.sil index d3622367a23c6..eccf0e8034199 100644 --- a/test/IRGen/global_resilience.sil +++ b/test/IRGen/global_resilience.sil @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend %S/../Inputs/resilient_struct.swift -enable-library-evolution -emit-module -emit-module-path %t/resilient_struct.swiftmodule -// RUN: %target-swift-frontend -enable-library-evolution -I %t -emit-ir %s | %FileCheck %s -// RUN: %target-swift-frontend -enable-library-evolution -I %t -emit-ir -O %s +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -enable-library-evolution -I %t -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -enable-library-evolution -I %t -emit-ir -O %s // CHECK: %swift.type = type { [[INT:i32|i64]] } diff --git a/test/IRGen/indexing.sil b/test/IRGen/indexing.sil index a415524e14211..492b348963dfb 100644 --- a/test/IRGen/indexing.sil +++ b/test/IRGen/indexing.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend %s -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend %s -Xllvm -sil-disable-pass=simplification -emit-ir | %FileCheck %s // REQUIRES: CPU=x86_64 diff --git a/test/IRGen/indirect_argument.sil b/test/IRGen/indirect_argument.sil index 8197591d78d6f..e318e9c3c4896 100644 --- a/test/IRGen/indirect_argument.sil +++ b/test/IRGen/indirect_argument.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=MandatoryCombine %s -emit-ir | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-%target-ptrsize %s +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=OnoneSimplification %s -emit-ir | %FileCheck -check-prefix=CHECK -check-prefix=CHECK-%target-ptrsize %s // UNSUPPORTED: CPU=arm64_32 diff --git a/test/IRGen/metatype.sil b/test/IRGen/metatype.sil index 1c1c802175910..e83fa70c61105 100644 --- a/test/IRGen/metatype.sil +++ b/test/IRGen/metatype.sil @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %build-irgen-test-overlays -// RUN: %target-swift-frontend -sdk %S/Inputs -I %t -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -sdk %S/Inputs -I %t -emit-ir %s | %FileCheck %s // REQUIRES: CPU=x86_64 // REQUIRES: objc_interop diff --git a/test/IRGen/mixed_mode_class_with_unimportable_fields.sil b/test/IRGen/mixed_mode_class_with_unimportable_fields.sil index f9e166eb12ef7..c680476232ce2 100644 --- a/test/IRGen/mixed_mode_class_with_unimportable_fields.sil +++ b/test/IRGen/mixed_mode_class_with_unimportable_fields.sil @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -o %t/UsingObjCStuff.swiftmodule -module-name UsingObjCStuff -I %t -I %S/Inputs/mixed_mode -swift-version 4 %S/Inputs/mixed_mode/UsingObjCStuff.swift -// RUN: %target-swift-frontend -emit-ir -I %t -I %S/Inputs/mixed_mode -module-name main -swift-version 4 %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-V5 --check-prefix=CHECK-V5-%target-ptrsize -DWORD=i%target-ptrsize -// RUN: %target-swift-frontend -emit-ir -I %t -I %S/Inputs/mixed_mode -module-name main -swift-version 5 %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-V5 --check-prefix=CHECK-V5-%target-ptrsize -DWORD=i%target-ptrsize +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -emit-ir -I %t -I %S/Inputs/mixed_mode -module-name main -swift-version 4 %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-V5 --check-prefix=CHECK-V5-%target-ptrsize -DWORD=i%target-ptrsize +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -emit-ir -I %t -I %S/Inputs/mixed_mode -module-name main -swift-version 5 %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-V5 --check-prefix=CHECK-V5-%target-ptrsize -DWORD=i%target-ptrsize // REQUIRES: objc_interop diff --git a/test/IRGen/moveonly_deinits.swift b/test/IRGen/moveonly_deinits.swift index e9a800b51a71e..7e1b93cb2af72 100644 --- a/test/IRGen/moveonly_deinits.swift +++ b/test/IRGen/moveonly_deinits.swift @@ -1,4 +1,5 @@ -// RUN: %target-swift-emit-ir -enable-experimental-move-only %s | %FileCheck -check-prefix=IR %s +// TODO: re-enable the simplification passes once rdar://104875010 is fixed +// RUN: %target-swift-emit-ir -enable-experimental-move-only -Xllvm -sil-disable-pass=simplification %s | %FileCheck -check-prefix=IR %s // Test that makes sure that at IRGen time we properly handle conditional // releases for trivial and non-trivial move only types. The SIL/SILGen part of diff --git a/test/IRGen/objc_block_storage.sil b/test/IRGen/objc_block_storage.sil index c0f18c106fb00..5f1f8f9d8c469 100644 --- a/test/IRGen/objc_block_storage.sil +++ b/test/IRGen/objc_block_storage.sil @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %build-irgen-test-overlays -// RUN: %target-swift-frontend -gnone -enable-objc-interop -sdk %S/Inputs -I %t %s -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -gnone -enable-objc-interop -sdk %S/Inputs -I %t %s -emit-ir | %FileCheck %s // REQUIRES: CPU=x86_64 // REQUIRES: objc_interop diff --git a/test/IRGen/partial_apply_forwarder.sil b/test/IRGen/partial_apply_forwarder.sil index ea7dfa3d75df5..73480dac72526 100644 --- a/test/IRGen/partial_apply_forwarder.sil +++ b/test/IRGen/partial_apply_forwarder.sil @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -enable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-objc -// RUN: %target-swift-frontend -disable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-native +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -enable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-objc +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -disable-objc-interop -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize --check-prefixes=CHECK,CHECK-native sil_stage canonical import Builtin diff --git a/test/IRGen/polymorphic_builtins.swift b/test/IRGen/polymorphic_builtins.swift index 150f2a1a8ae4d..13122b4f0fbab 100644 --- a/test/IRGen/polymorphic_builtins.swift +++ b/test/IRGen/polymorphic_builtins.swift @@ -1,13 +1,13 @@ // This line tests that IRGen is properly turning the unspecialized builtins // into traps. // -// RUN: %target-swift-frontend -emit-ir -parse-as-library -parse-stdlib %s | %FileCheck %s +// RUN: %target-swift-frontend -emit-ir -parse-as-library -parse-stdlib -Xllvm -sil-disable-pass=Simplification %s | %FileCheck %s // Make sure we are not eliminating these builtins before IRGen runs. As part of // the builtin's contract, we expect IRGen to convert them to traps, not // anything before. // -// RUN: %target-swift-frontend -emit-sil -parse-as-library -parse-stdlib %s | %FileCheck --check-prefix=SIL %s +// RUN: %target-swift-frontend -emit-sil -parse-as-library -parse-stdlib -Xllvm -sil-disable-pass=Simplification %s | %FileCheck --check-prefix=SIL %s import Swift diff --git a/test/IRGen/relative_protocol_witness_table.swift b/test/IRGen/relative_protocol_witness_table.swift index 164b7a167e03a..0b73c5e5a1b46 100644 --- a/test/IRGen/relative_protocol_witness_table.swift +++ b/test/IRGen/relative_protocol_witness_table.swift @@ -54,7 +54,7 @@ struct CStruct : WithAssoc { } func requireWitness3 (_ t: T) { - _ = T.self.AssocType + let a = T.self.AssocType } protocol WithAssocConformance { diff --git a/test/IRGen/select_enum.sil b/test/IRGen/select_enum.sil index 6e88193816aa9..cde3c2837c988 100644 --- a/test/IRGen/select_enum.sil +++ b/test/IRGen/select_enum.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -gnone -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -gnone -emit-ir %s | %FileCheck %s import Builtin diff --git a/test/IRGen/sil_linkage.sil b/test/IRGen/sil_linkage.sil index d0f6d90b45e7c..baa2d2697c3e1 100644 --- a/test/IRGen/sil_linkage.sil +++ b/test/IRGen/sil_linkage.sil @@ -1,5 +1,5 @@ -// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s --check-prefix=WMO --check-prefix=CHECK -// RUN: %target-swift-frontend -emit-ir -primary-file %s | %FileCheck %s --check-prefix=PRIMARY --check-prefix=CHECK +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -emit-ir %s | %FileCheck %s --check-prefix=WMO --check-prefix=CHECK +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -emit-ir -primary-file %s | %FileCheck %s --check-prefix=PRIMARY --check-prefix=CHECK sil_stage canonical diff --git a/test/IRGen/super.sil b/test/IRGen/super.sil index dda61fedc727d..81d5527b07f09 100644 --- a/test/IRGen/super.sil +++ b/test/IRGen/super.sil @@ -8,7 +8,7 @@ // RUN: %target-swift-frontend -emit-module -I %t -o %t %S/../Inputs/fixed_layout_class.swift -// RUN: %target-swift-frontend -enable-library-evolution -parse-sil -parse-as-library -emit-ir -I %t %s | %FileCheck %s +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -enable-library-evolution -parse-sil -parse-as-library -emit-ir -I %t %s | %FileCheck %s // CHECK: %swift.type = type { [[INT:i32|i64]] } diff --git a/test/IRGen/witness_method_phi.sil b/test/IRGen/witness_method_phi.sil index a67d4142899de..fe6a5735b02c7 100644 --- a/test/IRGen/witness_method_phi.sil +++ b/test/IRGen/witness_method_phi.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -emit-ir %s | %FileCheck %s +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -emit-ir %s | %FileCheck %s sil_stage canonical diff --git a/test/Interop/Cxx/static/static-member-var-silgen.swift b/test/Interop/Cxx/static/static-member-var-silgen.swift index b8aa08aa1981c..a92e46d60c606 100644 --- a/test/Interop/Cxx/static/static-member-var-silgen.swift +++ b/test/Interop/Cxx/static/static-member-var-silgen.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-emit-sil -I %S/Inputs -enable-experimental-cxx-interop %s | %FileCheck %s +// REQUIRES: swift_in_compiler + // CHECK: // clang name: WithStaticMember::staticMember // CHECK: sil_global public_external @{{_ZN16WithStaticMember12staticMemberE|\?staticMember@WithStaticMember@@2HA}} : $Int32 // CHECK: // clang name: WithIncompleteStaticMember::selfMember @@ -22,7 +24,8 @@ func writeStaticMember() { // CHECK: sil hidden @$s4main17writeStaticMemberyyF : $@convention(thin) () -> () // CHECK: [[ADDR:%.*]] = global_addr @{{_ZN16WithStaticMember12staticMemberE|\?staticMember@WithStaticMember@@2HA}} : $*Int32 -// CHECK: [[INT:%.*]] = struct $Int32 (%2 : $Builtin.Int32) +// CHECK: [[LIT:%.*]] = integer_literal $Builtin.Int32, -1 +// CHECK: [[INT:%.*]] = struct $Int32 ([[LIT]] : $Builtin.Int32) // CHECK: [[ACCESS:%.*]] = begin_access [modify] [dynamic] [[ADDR]] : $*Int32 // CHECK: store [[INT]] to [[ACCESS]] : $*Int32 diff --git a/test/Interpreter/imported_objc_generics.swift b/test/Interpreter/imported_objc_generics.swift index c868cb9e1056d..b824ac345da34 100644 --- a/test/Interpreter/imported_objc_generics.swift +++ b/test/Interpreter/imported_objc_generics.swift @@ -143,6 +143,7 @@ ImportedObjCGenerics.test("InheritanceFromNongeneric") { expectTrue(Container.superclass() == NSObject.self) expectTrue(Container.superclass() == NSObject.self) expectTrue(Container.self == Container.self) + expectTrue((Container, Int).self == (Container, Int).self) } public class InheritInSwift: Container { diff --git a/test/Interpreter/moveonly_bufferview.swift b/test/Interpreter/moveonly_bufferview.swift index 517bdc6df75fe..336f0d79fa1a6 100644 --- a/test/Interpreter/moveonly_bufferview.swift +++ b/test/Interpreter/moveonly_bufferview.swift @@ -1,4 +1,5 @@ -// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-move-only) | %FileCheck %s +// TODO: re-enable the simplification passes once rdar://104875010 is fixed +// RUN: %target-run-simple-swift(-Xfrontend -enable-experimental-move-only -Xllvm -sil-disable-pass=simplification) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: swift_test_mode_optimize_none diff --git a/test/SIL/Serialization/globals.sil b/test/SIL/Serialization/globals.sil index a5bff2029b89b..9dbc80e8832ec 100644 --- a/test/SIL/Serialization/globals.sil +++ b/test/SIL/Serialization/globals.sil @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -emit-module -o %t/tmp.swiftmodule +// RUN: %target-swift-frontend %s -Xllvm -sil-disable-pass=simplification -emit-module -o %t/tmp.swiftmodule // RUN: %target-sil-opt %t/tmp.swiftmodule | %FileCheck %s sil_stage canonical diff --git a/test/SIL/Serialization/public_non_abi.sil b/test/SIL/Serialization/public_non_abi.sil index dee6123d43774..d07d4f461f428 100644 --- a/test/SIL/Serialization/public_non_abi.sil +++ b/test/SIL/Serialization/public_non_abi.sil @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/def_public_non_abi.sil -// RUN: %target-swift-frontend -emit-sil -I %t %s | %FileCheck %s +// RUN: %target-swift-frontend -emit-module -o %t %S/Inputs/def_public_non_abi.sil -Xllvm -sil-disable-pass=simplification +// RUN: %target-swift-frontend -emit-sil -I %t %s -Xllvm -sil-disable-pass=simplification | %FileCheck %s sil_stage raw diff --git a/test/SILGen/builtins.swift b/test/SILGen/builtins.swift index fbb50acfce3c1..d5fb75b452007 100644 --- a/test/SILGen/builtins.swift +++ b/test/SILGen/builtins.swift @@ -1,6 +1,8 @@ // RUN: %target-swift-emit-silgen -parse-stdlib %s -disable-access-control -disable-objc-attr-requires-foundation-module -enable-objc-interop | %FileCheck %s // RUN: %target-swift-emit-sil -Onone -parse-stdlib %s -disable-access-control -disable-objc-attr-requires-foundation-module -enable-objc-interop | %FileCheck -check-prefix=CANONICAL %s +// REQUIRES: swift_in_compiler + import Swift protocol ClassProto : class { } @@ -829,7 +831,6 @@ func retain(ptr: Builtin.NativeObject) { // CANONICAL-NEXT: debug_value // CANONICAL-NEXT: strong_release [[P]] // CANONICAL-NEXT: tuple -// CANONICAL-NEXT: tuple // CANONICAL-NEXT: return // CANONICAL: } // end sil function '$s8builtins7release{{[_0-9a-zA-Z]*}}F' diff --git a/test/SILGen/copy_operator.swift b/test/SILGen/copy_operator.swift index 2fff7423f4da2..2055dd6092ef6 100644 --- a/test/SILGen/copy_operator.swift +++ b/test/SILGen/copy_operator.swift @@ -2,6 +2,8 @@ // RUN: %target-swift-emit-sil -enable-copy-propagation=requested-passes-only -module-name moveonly -parse-stdlib %s -disable-access-control -disable-objc-attr-requires-foundation-module -enable-experimental-move-only | %FileCheck -check-prefix=CHECK-SIL %s // RUN: %target-swift-emit-sil -enable-copy-propagation=requested-passes-only -module-name moveonly -parse-stdlib %s -disable-access-control -disable-objc-attr-requires-foundation-module -O -Xllvm -sil-disable-pass=FunctionSignatureOpts -enable-experimental-move-only | %FileCheck -check-prefix=CHECK-SIL-OPT %s +// REQUIRES: swift_in_compiler + import Swift class Klass {} @@ -45,7 +47,6 @@ class Klass {} // CHECK-SIL-NEXT: strong_retain [[VALUE]] // CHECK-SIL-NEXT: strong_retain [[VALUE]] // CHECK-SIL-NEXT: strong_release [[VALUE]] -// CHECK-SIL-NEXT: tuple () // CHECK-SIL-NEXT: dealloc_stack [[INPUT]] : $*Klass // CHECK-SIL-NEXT: strong_release [[VALUE]] : $Klass // CHECK-SIL-NEXT: return [[VALUE]] : $Klass @@ -87,7 +88,6 @@ public func useCopy(_ k: Klass) -> Klass { // CHECK-SIL-NEXT: strong_retain [[VALUE]] // CHECK-SIL-NEXT: strong_retain [[VALUE]] // CHECK-SIL-NEXT: strong_release [[VALUE]] -// CHECK-SIL-NEXT: tuple () // CHECK-SIL-NEXT: dealloc_stack [[INPUT]] // CHECK-SIL-NEXT: strong_release [[VALUE]] : $T // CHECK-SIL-NEXT: return [[VALUE]] : $T diff --git a/test/SILGen/moveonly_deinits.swift b/test/SILGen/moveonly_deinits.swift index 443e5d94dde5b..26cc414d2b1eb 100644 --- a/test/SILGen/moveonly_deinits.swift +++ b/test/SILGen/moveonly_deinits.swift @@ -1,5 +1,6 @@ -// RUN: %target-swift-emit-silgen -enable-experimental-move-only %s | %FileCheck -check-prefix=SILGEN %s -// RUN: %target-swift-emit-sil -enable-experimental-move-only %s | %FileCheck -check-prefix=SIL %s +// TODO: re-enable the simplification passes once rdar://104875010 is fixed +// RUN: %target-swift-emit-silgen -enable-experimental-move-only -Xllvm -sil-disable-pass=simplification %s | %FileCheck -check-prefix=SILGEN %s +// RUN: %target-swift-emit-sil -enable-experimental-move-only -Xllvm -sil-disable-pass=simplification %s | %FileCheck -check-prefix=SIL %s // Test that makes sure that throughout the pipeline we properly handle // conditional releases for trivial and non-trivial move only types. diff --git a/test/SILOptimizer/closure_lifetime_fixup_objc.swift b/test/SILOptimizer/closure_lifetime_fixup_objc.swift index bc692193d9f34..a846c288417a5 100644 --- a/test/SILOptimizer/closure_lifetime_fixup_objc.swift +++ b/test/SILOptimizer/closure_lifetime_fixup_objc.swift @@ -1,6 +1,8 @@ // RUN: %target-swift-frontend %s -sil-verify-all -emit-sil -enable-copy-propagation=false -o - -I %S/Inputs/usr/include | %FileCheck %s // REQUIRES: objc_interop +// REQUIRES: swift_in_compiler + // Using -enable-copy-propagation=false to pattern match against older SIL // output. At least until -enable-copy-propagation has been around // long enough in the same form to be worth rewriting CHECK lines. @@ -75,12 +77,6 @@ public func couldActuallyEscapeWithLoop(_ closure: @escaping () -> (), _ villain // CHECK-LABEL: sil @$s27closure_lifetime_fixup_objc9dontCrashyyF : $@convention(thin) () -> () { // CHECK: bb0: // CHECK: [[NONE:%.*]] = enum $Optional<{{.*}}>, #Optional.none!enumelt -// CHECK: br bb1 -// -// CHECK: bb1: -// CHECK: br bb2 -// -// CHECK: bb2: // CHECK: br [[LOOP_HEADER_BB:bb[0-9]+]]([[NONE]] // // CHECK: [[LOOP_HEADER_BB]]([[LOOP_IND_VAR:%.*]] : $Optional diff --git a/test/SILOptimizer/constant_propagation_stdlib.swift b/test/SILOptimizer/constant_propagation_stdlib.swift index 1ad8beb59ea65..8cb0012c152f2 100644 --- a/test/SILOptimizer/constant_propagation_stdlib.swift +++ b/test/SILOptimizer/constant_propagation_stdlib.swift @@ -1,6 +1,8 @@ // RUN: %target-swift-frontend -module-name constant_propagation_stdlib %s -parse-stdlib -emit-sil -o - | %FileCheck --check-prefix=CHECK-ONONE %s // RUN: %target-swift-frontend -module-name constant_propagation_stdlib %s -parse-stdlib -emit-sil -o - -O | %FileCheck --check-prefix=CHECK-O %s +// REQUIRES: swift_in_compiler + public struct MyInt { var v: Builtin.Int32 } @@ -21,8 +23,7 @@ public func isConcrete_true(_ x: MyInt) -> Builtin.Int1 { // CHECK-ONONE-LABEL: sil @$s27constant_propagation_stdlib16isConcrete_falseyBi1_xlF : $@convention(thin) (@in_guaranteed T) -> Builtin.Int1 { // CHECK-ONONE: bb0( -// CHECK-ONONE: [[METATYPE:%.*]] = metatype $@thick T.Type -// CHECK-ONONE: [[RESULT:%.*]] = builtin "isConcrete"([[METATYPE]] : $@thick T.Type) : $Builtin.Int1 +// CHECK-ONONE: [[RESULT:%.*]] = integer_literal $Builtin.Int1, 0 // CHECK-ONONE: return [[RESULT]] // CHECK-ONONE: } // end sil function '$s27constant_propagation_stdlib16isConcrete_falseyBi1_xlF' // CHECK-O-LABEL: sil [signature_optimized_thunk] [always_inline] {{.*}}@$s27constant_propagation_stdlib16isConcrete_falseyBi1_xlF : $@convention(thin) (@in_guaranteed T) -> Builtin.Int1 { diff --git a/test/SILOptimizer/definite_init_failable_initializers.swift b/test/SILOptimizer/definite_init_failable_initializers.swift index 1d7ef56acd517..c67337128bcf4 100644 --- a/test/SILOptimizer/definite_init_failable_initializers.swift +++ b/test/SILOptimizer/definite_init_failable_initializers.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend -emit-sil -enable-copy-propagation=false %s | %FileCheck %s +// REQUIRES: swift_in_compiler + // Using -enable-copy-propagation=false to pattern match against older SIL // output. At least until -enable-copy-propagation has been around // long enough in the same form to be worth rewriting CHECK lines. @@ -779,7 +781,9 @@ class FailableBaseClass { // // CHECK: [[FAIL_BB]]: // CHECK: release_value [[SELF_OPTIONAL]] -// CHECK: br [[FAIL_TRAMPOLINE_BB:bb[0-9]+]] +// CHECK: dealloc_stack [[SELF_BOX]] +// CHECK: [[NEW_SELF:%.*]] = enum $Optional, #Optional.none!enumelt +// CHECK-NEXT: br [[EPILOG_BB:bb[0-9]+]]([[NEW_SELF]] : $Optional) // // CHECK: [[SUCC_BB]]: // CHECK-NEXT: [[SELF_VALUE:%.*]] = unchecked_enum_data [[SELF_OPTIONAL]] @@ -788,11 +792,6 @@ class FailableBaseClass { // CHECK-NEXT: [[NEW_SELF:%.*]] = enum $Optional, #Optional.some!enumelt, [[SELF_VALUE]] // CHECK-NEXT: destroy_addr [[SELF_BOX]] // CHECK-NEXT: dealloc_stack [[SELF_BOX]] -// CHECK-NEXT: br [[EPILOG_BB:bb[0-9]+]]([[NEW_SELF]] : $Optional) -// -// CHECK: [[FAIL_TRAMPOLINE_BB]]: -// CHECK: dealloc_stack [[SELF_BOX]] -// CHECK: [[NEW_SELF:%.*]] = enum $Optional, #Optional.none!enumelt // CHECK-NEXT: br [[EPILOG_BB]]([[NEW_SELF]] : $Optional) // // CHECK: [[EPILOG_BB]]([[NEW_SELF:%.*]] : $Optional): diff --git a/test/SILOptimizer/devirt_single_module_in_multiple_files.swift b/test/SILOptimizer/devirt_single_module_in_multiple_files.swift index 056d592cd9418..b139824c52836 100644 --- a/test/SILOptimizer/devirt_single_module_in_multiple_files.swift +++ b/test/SILOptimizer/devirt_single_module_in_multiple_files.swift @@ -1,4 +1,4 @@ -// RUN: %target-swiftc_driver -module-name devirt_single_module_in_multiple_files -O %s %S/Inputs/BaseProblem.swift %S/Inputs/Problems.swift -parse-as-library -Xllvm -sil-disable-pass=inline -emit-sil 2>&1 | %FileCheck %s +// RUN: %target-swiftc_driver -module-name devirt_single_module_in_multiple_files -O %s %S/Inputs/BaseProblem.swift %S/Inputs/Problems.swift -parse-as-library -Xllvm -sil-disable-pass=inline -Xllvm -sil-disable-pass=function-signature-opts -emit-sil 2>&1 | %FileCheck %s public func test() { let e = Evaluator() @@ -6,12 +6,12 @@ public func test() { e.evaluate(1) } -// CHECK-LABEL: sil private @$s38devirt_single_module_in_multiple_files9EvaluatorCACycfcSiycfU_ +// CHECK-LABEL: sil {{.*}}@{{.*}}s38devirt_single_module_in_multiple_files9EvaluatorCACycfcSiycfU_{{.*}} // CHECK: %{{.*}} = class_method %{{.*}} : $Problem1, #Problem1.run : (Problem1) -> () -> Int, $@convention(method) (@guaranteed Problem1) -> Int // CHECK-NEXT: apply // CHECK: return -// CHECK-LABEL: sil private @$s38devirt_single_module_in_multiple_files9EvaluatorCACycfcSiycfU0_ +// CHECK-LABEL: sil {{.*}}@{{.*}}s38devirt_single_module_in_multiple_files9EvaluatorCACycfcSiycfU0_{{.*}} // CHECK: %{{.*}} = class_method %{{.*}} : $Problem2, #Problem2.run : (Problem2) -> () -> Int, $@convention(method) (@guaranteed Problem2) -> Int // CHECK-NEXT: apply // CHECK: return diff --git a/test/SILOptimizer/existential_specializer_indirect_class.sil b/test/SILOptimizer/existential_specializer_indirect_class.sil index b1e0aa5eb86f6..12e0213570533 100644 --- a/test/SILOptimizer/existential_specializer_indirect_class.sil +++ b/test/SILOptimizer/existential_specializer_indirect_class.sil @@ -1,4 +1,6 @@ -// RUN: %target-sil-opt -O -wmo -enable-sil-verify-all -sil-disable-pass=DeadFunctionAndGlobalElimination %s | %FileCheck %s +// RUN: %target-sil-opt -O -wmo -enable-sil-verify-all -sil-disable-pass=DeadFunctionAndGlobalElimination -sil-disable-pass=function-signature-opts %s | %FileCheck %s + +// REQUIRES: swift_in_compiler sil_stage canonical @@ -9,13 +11,10 @@ protocol ClassProtocol: AnyObject { func method() } class C: ClassProtocol { func method() {} } // CHECK-LABEL: sil [signature_optimized_thunk] [always_inline] @test_indirect_class_protocol : $@convention(thin) (@in any ClassProtocol) -> () -sil @test_indirect_class_protocol : $@convention(thin) (@in ClassProtocol) -> () { +sil [noinline] @test_indirect_class_protocol : $@convention(thin) (@in ClassProtocol) -> () { // CHECK: bb0(%0 : $*any ClassProtocol): bb0(%0 : $*ClassProtocol): - // CHECK-NEXT: %1 = load %0 - // CHECK-NEXT: strong_release %1 destroy_addr %0 : $*ClassProtocol - // CHECK-NEXT: return undef return undef : $() } @@ -39,19 +38,12 @@ bb0(%0 : $*ClassProtocol): } // Check that a specialization of test_indirect_class_protocol is created. -// CHECK-LABEL: sil shared [signature_optimized_thunk] [always_inline] {{.*}}@$s28test_indirect_class_protocolTf4e_n4main1CC_Tg5 : $@convention(thin) (@owned C) -> () +// CHECK-LABEL: sil shared {{.*}}@$s28test_indirect_class_protocolTf4e_n4main1CC_Tg5 : $@convention(thin) (@owned C) -> () // CHECK: bb0(%0 : $C): // CHECK-NEXT: strong_release %0 // CHECK-NEXT: return undef // CHECK-LABEL: end sil function '$s28test_indirect_class_protocolTf4e_n4main1CC_Tg5' -// Check the generated specialization of test_indirect_class_protocol -// CHECK-LABEL: sil shared @$s28test_indirect_class_protocolTf4e_n4main1CC_Tg5Tf4d_n : $@convention(thin) () -> () -// Make sure *everything* was inlined / optimized away -// CHECK: bb0: -// CHECK-NEXT: return undef -// CHECK: } // end sil function '$s28test_indirect_class_protocolTf4e_n4main1CC_Tg5Tf4d_n' - sil @invoke_indirect_class_protocol : $@convention(thin) (@guaranteed C) -> () { bb0(%0 : $C): %1 = init_existential_ref %0 : $C : $C, $ClassProtocol diff --git a/test/SILOptimizer/generic_inline_self.swift b/test/SILOptimizer/generic_inline_self.swift index 752f0f30071b2..316f6940e8db9 100644 --- a/test/SILOptimizer/generic_inline_self.swift +++ b/test/SILOptimizer/generic_inline_self.swift @@ -1,5 +1,7 @@ // RUN: %target-swift-frontend -emit-sil -primary-file %s | %FileCheck %s +// REQUIRES: swift_in_compiler + // Test to ensure that mandatory inlining of generics with a dynamic Self // substitution works correctly with thick_metatype instructions and SIL // type lowering. @@ -53,8 +55,6 @@ class C : P { // CHECK-NEXT: [[STATIC_METATYPE:%.*]] = upcast [[METATYPE]] : $@thick @dynamic_self C.Type to $@thick C.Type // CHECK-NEXT: [[FN:%.*]] = class_method [[STATIC_METATYPE]] : $@thick C.Type, #C.init!allocator : (C.Type) -> () -> C, $@convention(method) (@thick C.Type) -> @owned C // CHECK-NEXT: [[RESULT2:%.*]] = apply [[FN]]([[STATIC_METATYPE]]) : $@convention(method) (@thick C.Type) -> @owned C -// CHECK-NEXT: tuple () -// CHECK-NEXT: tuple () // CHECK-NEXT: return %5 : $C func returnsNewInstanceTransparentProtocol() -> Self { return makeInstanceTransparentProtocol(type(of: self)) diff --git a/test/SILOptimizer/mandatory_combine_canon.sil b/test/SILOptimizer/mandatory_combine_canon.sil index 5994101e9a498..26852dd57db37 100644 --- a/test/SILOptimizer/mandatory_combine_canon.sil +++ b/test/SILOptimizer/mandatory_combine_canon.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-sil-verify-all -mandatory-combine -sil-mandatory-combine-enable-canon-and-simple-dce %s | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all -sil-combine %s | %FileCheck %s sil_stage canonical diff --git a/test/SILOptimizer/mandatory_combiner.sil b/test/SILOptimizer/mandatory_combiner.sil index 20406921cf92f..f80cd37d10e9e 100644 --- a/test/SILOptimizer/mandatory_combiner.sil +++ b/test/SILOptimizer/mandatory_combiner.sil @@ -1,4 +1,6 @@ -// RUN: %target-sil-opt -mandatory-combine %s | %FileCheck %s +// RUN: %target-sil-opt -onone-simplification %s | %FileCheck %s + +// REQUIRES: swift_in_compiler sil_stage canonical diff --git a/test/SILOptimizer/mandatory_combiner_opt.sil b/test/SILOptimizer/mandatory_combiner_opt.sil index ad0c00c8c9322..c07c76858cdb1 100644 --- a/test/SILOptimizer/mandatory_combiner_opt.sil +++ b/test/SILOptimizer/mandatory_combiner_opt.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -optimized-mandatory-combine -sil-mandatory-combine-enable-canon-and-simple-dce %s | %FileCheck %s +// RUN: %target-sil-opt -sil-combine %s | %FileCheck %s // Tests for when the mandatory combiner is running with optimizations // enabled. Only put tests here for functionality that only occurs when the diff --git a/test/SILOptimizer/mandatory_inlining.swift b/test/SILOptimizer/mandatory_inlining.swift index fa28c1584349c..c897b35c87e5c 100644 --- a/test/SILOptimizer/mandatory_inlining.swift +++ b/test/SILOptimizer/mandatory_inlining.swift @@ -157,7 +157,6 @@ func class_constrained_generic(_ o: T) -> AnyClass? { func invoke(_ c: C) { // CHECK-NOT: function_ref @$s18mandatory_inlining25class_constrained_generic{{[_0-9a-zA-Z]*}}F // CHECK-NOT: apply - // CHECK: init_existential_metatype _ = class_constrained_generic(c) // CHECK: return } diff --git a/test/SILOptimizer/mandatory_inlining_reasync.swift b/test/SILOptimizer/mandatory_inlining_reasync.swift index 915a000bb6c2d..0bb203c1da92d 100644 --- a/test/SILOptimizer/mandatory_inlining_reasync.swift +++ b/test/SILOptimizer/mandatory_inlining_reasync.swift @@ -1,6 +1,8 @@ // RUN: %target-swift-frontend -emit-sil -enable-experimental-concurrency -disable-availability-checking %s | %FileCheck %s // REQUIRES: concurrency +// REQUIRES: swift_in_compiler + @_transparent func reasyncFunction(_ value: Optional, _ fn: () async throws -> Int) reasync rethrows -> Int { switch value { @@ -12,15 +14,7 @@ func reasyncFunction(_ value: Optional, _ fn: () async throws -> Int) reasy // CHECK-LABEL: sil hidden @$s26mandatory_inlining_reasync20callsReasyncFunctionSiyF : $@convention(thin) () -> Int { // CHECK: [[FN:%.*]] = function_ref @$s26mandatory_inlining_reasync20callsReasyncFunctionSiyFSiyXEfU_ : $@convention(thin) () -> Int // CHECK-NEXT: [[THICK:%.*]] = thin_to_thick_function [[FN]] : $@convention(thin) () -> Int to $@noescape @callee_guaranteed () -> Int -// FIXME: it looks like the hop is being removed but not this instruction -// CHECK-NEXT: [[GENERIC_EXEC:%.*]] = enum $Optional, #Optional.none -// CHECK-NEXT: br bb1 - -// CHECK: bb1: // CHECK-NEXT: [[RESULT:%.*]] = apply [[THICK]]() : $@noescape @callee_guaranteed () -> Int -// CHECK-NEXT: br bb2 - -// CHECK: bb2: // CHECK-NEXT: return [[RESULT]] : $Int func callsReasyncFunction() -> Int { return reasyncFunction(nil, { return 321 } ) diff --git a/test/SILOptimizer/onone_simplifications.swift b/test/SILOptimizer/onone_simplifications.swift new file mode 100644 index 0000000000000..b343c899745f0 --- /dev/null +++ b/test/SILOptimizer/onone_simplifications.swift @@ -0,0 +1,39 @@ +// RUN: %target-swift-frontend -primary-file %s -module-name=test -Xllvm -sil-print-debuginfo -emit-sil | %FileCheck %s + +// REQUIRES: swift_in_compiler + +// CHECK-LABEL: sil [transparent] @$s4test9checkTypeySixs17FixedWidthIntegerRzlF +@_transparent +public func checkType(_ a: A) -> Int { + // CHECK-NOT: builtin + // CHECK: debug_step , loc "{{[^"]+}}":[[# @LINE + 1]] + if _isConcrete(A.self) { + // CHECK-NOT: builtin + if A.self == Int.self { + return 1 + } + } + return 0 +} +// CHECK: } // end sil function '$s4test9checkTypeySixs17FixedWidthIntegerRzlF' + +// CHECK-LABEL: sil @$s4test0A10IsConcreteSiyF +public func testIsConcrete() -> Int { + // CHECK: debug_step , loc "{{[^"]+}}":[[# @LINE + 1]]:3 + checkType(1) + // CHECK: [[IL:%[0-9]+]] = integer_literal $Builtin.Int{{[0-9]+}}, 1 + // CHECK: [[I:%[0-9]+]] = struct $Int ([[IL]] : + // CHECK: return [[I]] +} +// CHECK: } // end sil function '$s4test0A10IsConcreteSiyF' + + +// CHECK-LABEL: sil @$s4test0A17MetadatComparisonSbyF +public func testMetadatComparison() -> Bool { + // CHECK: debug_step , loc "{{[^"]+}}":[[# @LINE + 1]] + [String: Int].self == Never.self + // CHECK: [[IL:%[0-9]+]] = integer_literal $Builtin.Int1, 0 + // CHECK: [[I:%[0-9]+]] = struct $Bool ([[IL]] : + // CHECK: return [[I]] +} +// CHECK: } // end sil function '$s4test0A17MetadatComparisonSbyF' diff --git a/test/SILOptimizer/optimizer_counters.sil b/test/SILOptimizer/optimizer_counters.sil index ac3b1f4446d9c..76f287ab85ded 100644 --- a/test/SILOptimizer/optimizer_counters.sil +++ b/test/SILOptimizer/optimizer_counters.sil @@ -5,6 +5,8 @@ // RUN: %target-sil-opt -enable-sil-verify-all %s -O -sil-stats-functions -sil-stats-only-function=test_simple 2>&1 | %FileCheck --check-prefix=CHECK-SIL-STATS-ONLY-FUNCTION %s // RUN: %target-sil-opt -enable-sil-verify-all %s -O -sil-stats-functions -sil-stats-only-functions=test 2>&1 | %FileCheck --check-prefix=CHECK-SIL-STATS-ONLY-FUNCTIONS %s +// REQUIRES: swift_in_compiler + // Test different modes of optimizer counters statistics collection. @@ -17,9 +19,9 @@ sil @fatalError : $@convention(thin) () -> Never // Check that module level statistics are produced. // -// CHECK-SIL-STATS-MODULES: module, inst, HighLevel,Function+EarlyLoopOpt, PerformanceConstantPropagation, {{.*}}, 15, 12 -// CHECK-SIL-STATS-MODULES: module, block, HighLevel,Function+EarlyLoopOpt, SimplifyCFG, {{.*}}, 6, 3 -// CHECK-SIL-STATS-MODULES: module, inst, HighLevel,Function+EarlyLoopOpt, SimplifyCFG, {{.*}}, 12, 6 +// CHECK-SIL-STATS-MODULES: module, inst, HighLevel,Function+EarlyLoopOpt, PerformanceConstantPropagation, {{.*}}, 14, 11 +// CHECK-SIL-STATS-MODULES: module, block, HighLevel,Function+EarlyLoopOpt, SimplifyCFG, {{.*}}, 5, 3 +// CHECK-SIL-STATS-MODULES: module, inst, HighLevel,Function+EarlyLoopOpt, SimplifyCFG, {{.*}}, 11, 6 // Check that module level statistics are produced. // diff --git a/test/SILOptimizer/ossa_rauw_tests.sil b/test/SILOptimizer/ossa_rauw_tests.sil index 4d14fec5e8e8f..af1bba4f71abe 100644 --- a/test/SILOptimizer/ossa_rauw_tests.sil +++ b/test/SILOptimizer/ossa_rauw_tests.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-sil-verify-all -optimized-mandatory-combine -sil-mandatory-combine-enable-canon-and-simple-dce -semantic-arc-opts %s | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all -sil-combine -semantic-arc-opts %s | %FileCheck %s // Make sure that we can perform all of these RAUW without producing ARC traffic // that semantic arc opts can't eliminate. @@ -258,19 +258,18 @@ bb0(%0 : @guaranteed $Klass): // CHECK-NOT: destroy_value // // CHECK: bb2: -// CHECK-NEXT: [[COPY:%.*]] = copy_value [[ARG]] // CHECK-NEXT: cond_br undef, bb3, bb4 // // CHECK: bb3: -// CHECK-NEXT: destroy_value [[COPY]] // CHECK-NEXT: br bb2 // // CHECK: bb4: -// CHECK-NEXT: [[ENUM_SOME_RESULT:%.*]] = enum $FakeOptional, #FakeOptional.some!enumelt, [[COPY]] -// CHECK-NEXT: br bb6([[ENUM_SOME_RESULT]] : $FakeOptional) +// CHECK-NEXT: [[ENUM_SOME_RESULT:%.*]] = enum $FakeOptional, #FakeOptional.some!enumelt, %0 +// CHECK-NEXT: [[COPY:%.*]] = copy_value [[ENUM_SOME_RESULT]] +// CHECK-NEXT: br bb6([[COPY]] : $FakeOptional) // // CHECK: bb5: -// CHECK-NEXT: [[ENUM_NONE_RESULT:%.*]] = enum $FakeOptional, #FakeOptional.none!enumelt // user: %10 +// CHECK-NEXT: [[ENUM_NONE_RESULT:%.*]] = enum $FakeOptional, #FakeOptional.none!enumelt // CHECK-NEXT: br bb6([[ENUM_NONE_RESULT]] : // // CHECK: bb6([[RESULT:%.*]] : @owned $FakeOptional): @@ -340,7 +339,6 @@ bb0(%0 : @guaranteed $Builtin.NativeObject): // CHECK-LABEL: sil [ossa] @unowned_to_guaranteed_rauw_2b : $@convention(thin) (@guaranteed Builtin.NativeObject) -> Klass { // CHECK: bb0( // CHECK-NEXT: unchecked_ref_cast -// CHECK-NEXT: unchecked_ownership_conversion // CHECK-NEXT: return // CHECK: } // end sil function 'unowned_to_guaranteed_rauw_2b' sil [ossa] @unowned_to_guaranteed_rauw_2b : $@convention(thin) (@guaranteed Builtin.NativeObject) -> Klass { diff --git a/test/SILOptimizer/sil_combine_inst_passes.sil b/test/SILOptimizer/sil_combine_inst_passes.sil index 7951c4f902e79..3b83be423b732 100644 --- a/test/SILOptimizer/sil_combine_inst_passes.sil +++ b/test/SILOptimizer/sil_combine_inst_passes.sil @@ -1,4 +1,4 @@ -// RUN: %target-sil-opt -enable-sil-verify-all %s -sil-combine | %FileCheck %s +// RUN: %target-sil-opt -enable-sil-verify-all %s -simplification | %FileCheck %s // REQUIRES: swift_in_compiler diff --git a/test/Serialization/basic_sil.swift b/test/Serialization/basic_sil.swift index 8561ef5894ea2..7ef2f0d2e98dd 100644 --- a/test/Serialization/basic_sil.swift +++ b/test/Serialization/basic_sil.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -emit-module -Xfrontend -disable-diagnostic-passes -whole-module-optimization -Xfrontend -enable-objc-interop -o %t/def_basic.swiftmodule %S/Inputs/def_basic.sil +// RUN: %target-build-swift -Xllvm -sil-disable-pass=simplification -emit-module -Xfrontend -disable-diagnostic-passes -whole-module-optimization -Xfrontend -enable-objc-interop -o %t/def_basic.swiftmodule %S/Inputs/def_basic.sil // RUN: llvm-bcanalyzer %t/def_basic.swiftmodule | %FileCheck %s // RUN: %target-build-swift -emit-sil -I %t %s -o %t/basic_sil.sil // RUN: %target-sil-opt -parse-serialized-sil -I %t %t/basic_sil.sil -performance-linker | %FileCheck %S/Inputs/def_basic.sil diff --git a/test/Serialization/basic_sil_objc.swift b/test/Serialization/basic_sil_objc.swift index d58c26ac375b8..9b12f4fd0d101 100644 --- a/test/Serialization/basic_sil_objc.swift +++ b/test/Serialization/basic_sil_objc.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -Xfrontend %clang-importer-sdk -I %S/../Inputs/clang-importer-sdk/swift-modules -emit-module -Xfrontend -disable-diagnostic-passes -whole-module-optimization -o %t/def_basic_objc.swiftmodule %S/Inputs/def_basic_objc.sil +// RUN: %target-build-swift -Xllvm -sil-disable-pass=simplification -Xfrontend %clang-importer-sdk -I %S/../Inputs/clang-importer-sdk/swift-modules -emit-module -Xfrontend -disable-diagnostic-passes -whole-module-optimization -o %t/def_basic_objc.swiftmodule %S/Inputs/def_basic_objc.sil // RUN: llvm-bcanalyzer %t/def_basic_objc.swiftmodule | %FileCheck %s // RUN: %target-build-swift -Xfrontend %clang-importer-sdk -emit-sil -I %t %s -o %t/basic_sil_objc.sil diff --git a/test/Serialization/concurrency_sil.swift b/test/Serialization/concurrency_sil.swift index bc77e4ec307db..e5ecfb179fe14 100644 --- a/test/Serialization/concurrency_sil.swift +++ b/test/Serialization/concurrency_sil.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -emit-module -Xfrontend -disable-availability-checking -Xfrontend -disable-diagnostic-passes -whole-module-optimization -Xfrontend -enable-objc-interop -o %t/def_concurrency.swiftmodule %S/Inputs/def_concurrency.sil +// RUN: %target-build-swift -Xllvm -sil-disable-pass=simplification -emit-module -Xfrontend -disable-availability-checking -Xfrontend -disable-diagnostic-passes -whole-module-optimization -Xfrontend -enable-objc-interop -o %t/def_concurrency.swiftmodule %S/Inputs/def_concurrency.sil // RUN: llvm-bcanalyzer %t/def_concurrency.swiftmodule | %FileCheck %s // RUN: %target-build-swift -emit-sil -I %t %s -o %t/concurrency_sil.sil // RUN: %target-sil-opt -parse-serialized-sil -I %t %t/concurrency_sil.sil -performance-linker | %FileCheck %S/Inputs/def_concurrency.sil diff --git a/test/Serialization/moveonly_deinit.swift b/test/Serialization/moveonly_deinit.swift index 11290053fe116..9081c98449f04 100644 --- a/test/Serialization/moveonly_deinit.swift +++ b/test/Serialization/moveonly_deinit.swift @@ -1,6 +1,7 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -enable-experimental-move-only -g -emit-module -module-name OtherModule %S/Inputs/moveonly_deinit.swift -emit-module-path %t/OtherModule.swiftmodule -// RUN: %target-swift-frontend -enable-experimental-move-only -g -I %t %s -emit-silgen +// TODO: re-enable the simplification passes once rdar://104875010 is fixed +// RUN: %target-swift-frontend -enable-experimental-move-only -Xllvm -sil-disable-pass=simplification -g -emit-module -module-name OtherModule %S/Inputs/moveonly_deinit.swift -emit-module-path %t/OtherModule.swiftmodule +// RUN: %target-swift-frontend -enable-experimental-move-only -Xllvm -sil-disable-pass=simplification -g -I %t %s -emit-silgen // Make sure we can deserialize deinits of both enums and structs. diff --git a/test/Serialization/moveonly_error_on_import_into_module_without_flag.swift b/test/Serialization/moveonly_error_on_import_into_module_without_flag.swift index eba9bd33480f2..0418a2831ce07 100644 --- a/test/Serialization/moveonly_error_on_import_into_module_without_flag.swift +++ b/test/Serialization/moveonly_error_on_import_into_module_without_flag.swift @@ -1,5 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend -emit-module -o %t/Library.swiftmodule -module-name Library %S/Inputs/moveonly_deinit.swift -enable-experimental-move-only +// TODO: re-enable the simplification passes once rdar://104875010 is fixed +// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=simplification -emit-module -o %t/Library.swiftmodule -module-name Library %S/Inputs/moveonly_deinit.swift -enable-experimental-move-only // RUN: not %target-swift-frontend -I %t %s -emit-sil -o /dev/null 2>&1 | %FileCheck %s // This test makes sure that if we import a move only type and do not set the diff --git a/test/Serialization/sil_partial_apply_ownership.sil b/test/Serialization/sil_partial_apply_ownership.sil index 531142f5eb600..8007dc6b7d4d4 100644 --- a/test/Serialization/sil_partial_apply_ownership.sil +++ b/test/Serialization/sil_partial_apply_ownership.sil @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift -emit-module -Xllvm -verify-skip-convert-escape-to-noescape-attributes -Xfrontend -disable-diagnostic-passes -whole-module-optimization -o %t/sil_partial_apply_ownership.swiftmodule %s -// RUN: %target-build-swift -Xllvm -verify-skip-convert-escape-to-noescape-attributes -emit-sil -I %t %s | %FileCheck %s +// RUN: %target-build-swift -Xllvm -sil-disable-pass=simplification -emit-module -Xllvm -verify-skip-convert-escape-to-noescape-attributes -Xfrontend -disable-diagnostic-passes -whole-module-optimization -o %t/sil_partial_apply_ownership.swiftmodule %s +// RUN: %target-build-swift -Xllvm -sil-disable-pass=simplification -Xllvm -verify-skip-convert-escape-to-noescape-attributes -emit-sil -I %t %s | %FileCheck %s import Builtin diff --git a/test/sil-passpipeline-dump/basic.test-sh b/test/sil-passpipeline-dump/basic.test-sh index 9a9380715641b..1ac76355bb0b8 100644 --- a/test/sil-passpipeline-dump/basic.test-sh +++ b/test/sil-passpipeline-dump/basic.test-sh @@ -1,9 +1,11 @@ // RUN: %sil-passpipeline-dumper -Onone | %FileCheck %s +// REQUIRES: swift_in_compiler + // CHECK: --- // CHECK: name: Non-Diagnostic Mandatory Optimizations -// CHECK: passes: [ "for-each-loop-unroll", "mandatory-combine", "mandatory-arc-opts", -// CHECK: "onone-prespecializer" ] +// CHECK: passes: [ "for-each-loop-unroll", "onone-simplification", "mandatory-arc-opts", +// CHECK: "onone-prespecializer" ] // CHECK: --- // CHECK: name: Serialization // CHECK: passes: [ "serialize-sil", "sil-moved-async-var-dbginfo-propagator", @@ -11,5 +13,6 @@ // CHECK: --- // CHECK: name: Rest of Onone // CHECK: passes: [ "use-prespecialized", "target-constant-folding", "function-stack-protection", +// CHECK-NEXT: "late-onone-simplification", "cleanup-debug-steps", // CHECK-NEXT: "sil-debuginfo-gen" ] // CHECK: ... diff --git a/utils/swift-autocomplete.bash b/utils/swift-autocomplete.bash index 97ef168884282..1bf12e1af8e44 100644 --- a/utils/swift-autocomplete.bash +++ b/utils/swift-autocomplete.bash @@ -91,7 +91,6 @@ _swift_complete() -sil-loop-region-view-cfg-only-function \ -sil-loop-region-view-cfg-only-functions \ -sil-lower-agg-instrs-expand-all \ - -sil-mandatory-combine-enable-canon-and-simple-dce \ -sil-merge-stack-slots \ -sil-opt-pass-count \ -sil-opt-remark-ignore-always-infer \ From 919eea704586317146443f7fb9bff623c586c144 Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Tue, 7 Feb 2023 18:43:55 +0000 Subject: [PATCH 76/98] [cxx-interop] Add `CxxDictionary` protocol for `std::map` ergonomics This adds a protocol to the C++ standard library overlay which will improve the ergonomics of `std::map` and `std::unordered_map` when used from Swift code. As of now, `CxxDictionary` adds a subscript with an optional return type that mimics the subscript of `Swift.Dictionary`. Similar to https://github.com/apple/swift/pull/63244. --- include/swift/AST/KnownProtocols.def | 2 + lib/AST/ASTContext.cpp | 2 + .../ClangDerivedConformances.cpp | 131 +++++++++++++----- lib/ClangImporter/ClangDerivedConformances.h | 12 ++ lib/ClangImporter/ImportDecl.cpp | 2 + lib/IRGen/GenMeta.cpp | 2 + stdlib/public/Cxx/CMakeLists.txt | 2 + stdlib/public/Cxx/CxxDictionary.swift | 37 +++++ stdlib/public/Cxx/CxxPair.swift | 19 +++ test/Interop/Cxx/stdlib/Inputs/std-map.h | 3 + test/Interop/Cxx/stdlib/use-std-map.swift | 26 ++-- 11 files changed, 195 insertions(+), 43 deletions(-) create mode 100644 stdlib/public/Cxx/CxxDictionary.swift create mode 100644 stdlib/public/Cxx/CxxPair.swift diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index 11990214f5275..019b21926a98c 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -106,6 +106,8 @@ PROTOCOL(DistributedTargetInvocationResultHandler) // C++ Standard Library Overlay: PROTOCOL(CxxConvertibleToCollection) +PROTOCOL(CxxDictionary) +PROTOCOL(CxxPair) PROTOCOL(CxxSet) PROTOCOL(CxxRandomAccessCollection) PROTOCOL(CxxSequence) diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 8d18980099249..ce99191ec26e8 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1125,6 +1125,8 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { M = getLoadedModule(Id_Distributed); break; case KnownProtocolKind::CxxConvertibleToCollection: + case KnownProtocolKind::CxxDictionary: + case KnownProtocolKind::CxxPair: case KnownProtocolKind::CxxRandomAccessCollection: case KnownProtocolKind::CxxSet: case KnownProtocolKind::CxxSequence: diff --git a/lib/ClangImporter/ClangDerivedConformances.cpp b/lib/ClangImporter/ClangDerivedConformances.cpp index 5b0142db23e15..5e92d9974da53 100644 --- a/lib/ClangImporter/ClangDerivedConformances.cpp +++ b/lib/ClangImporter/ClangDerivedConformances.cpp @@ -61,6 +61,15 @@ lookupDirectWithoutExtensions(NominalTypeDecl *decl, Identifier id) { return result; } +template +static Decl *lookupDirectSingleWithoutExtensions(NominalTypeDecl *decl, + Identifier id) { + auto results = lookupDirectWithoutExtensions(decl, id); + if (results.size() != 1) + return nullptr; + return dyn_cast(results.front()); +} + /// Similar to ModuleDecl::conformsToProtocol, but doesn't introduce a /// dependency on Sema. static bool isConcreteAndValid(ProtocolConformanceRef conformanceRef, @@ -315,19 +324,14 @@ void swift::conformToCxxIteratorIfNeeded( // Check if present: `var pointee: Pointee { get }` auto pointeeId = ctx.getIdentifier("pointee"); - auto pointees = lookupDirectWithoutExtensions(decl, pointeeId); - if (pointees.size() != 1) - return; - auto pointee = dyn_cast(pointees.front()); + auto pointee = lookupDirectSingleWithoutExtensions(decl, pointeeId); if (!pointee || pointee->isGetterMutating() || pointee->getType()->hasError()) return; // Check if present: `func successor() -> Self` auto successorId = ctx.getIdentifier("successor"); - auto successors = lookupDirectWithoutExtensions(decl, successorId); - if (successors.size() != 1) - return; - auto successor = dyn_cast(successors.front()); + auto successor = + lookupDirectSingleWithoutExtensions(decl, successorId); if (!successor || successor->isMutating()) return; auto successorTy = successor->getResultInterfaceType(); @@ -398,20 +402,14 @@ void swift::conformToCxxSequenceIfNeeded( // Check if present: `func __beginUnsafe() -> RawIterator` auto beginId = ctx.getIdentifier("__beginUnsafe"); - auto begins = lookupDirectWithoutExtensions(decl, beginId); - if (begins.size() != 1) - return; - auto begin = dyn_cast(begins.front()); + auto begin = lookupDirectSingleWithoutExtensions(decl, beginId); if (!begin) return; auto rawIteratorTy = begin->getResultInterfaceType(); // Check if present: `func __endUnsafe() -> RawIterator` auto endId = ctx.getIdentifier("__endUnsafe"); - auto ends = lookupDirectWithoutExtensions(decl, endId); - if (ends.size() != 1) - return; - auto end = dyn_cast(ends.front()); + auto end = lookupDirectSingleWithoutExtensions(decl, endId); if (!end) return; @@ -524,6 +522,16 @@ void swift::conformToCxxSequenceIfNeeded( } } +static bool isStdDecl(const clang::CXXRecordDecl *clangDecl, + llvm::ArrayRef names) { + if (!clangDecl->isInStdNamespace()) + return false; + if (!clangDecl->getIdentifier()) + return false; + StringRef name = clangDecl->getName(); + return llvm::is_contained(names, name); +} + void swift::conformToCxxSetIfNeeded(ClangImporter::Implementation &impl, NominalTypeDecl *decl, const clang::CXXRecordDecl *clangDecl) { @@ -535,25 +543,15 @@ void swift::conformToCxxSetIfNeeded(ClangImporter::Implementation &impl, // Only auto-conform types from the C++ standard library. Custom user types // might have a similar interface but different semantics. - if (!clangDecl->isInStdNamespace()) - return; - if (!clangDecl->getIdentifier()) - return; - StringRef name = clangDecl->getName(); - if (name != "set" && name != "unordered_set" && name != "multiset") - return; - - auto valueTypeId = ctx.getIdentifier("value_type"); - auto valueTypes = lookupDirectWithoutExtensions(decl, valueTypeId); - if (valueTypes.size() != 1) + if (!isStdDecl(clangDecl, {"set", "unordered_set", "multiset"})) return; - auto valueType = dyn_cast(valueTypes.front()); - auto sizeTypeId = ctx.getIdentifier("size_type"); - auto sizeTypes = lookupDirectWithoutExtensions(decl, sizeTypeId); - if (sizeTypes.size() != 1) + auto valueType = lookupDirectSingleWithoutExtensions( + decl, ctx.getIdentifier("value_type")); + auto sizeType = lookupDirectSingleWithoutExtensions( + decl, ctx.getIdentifier("size_type")); + if (!valueType || !sizeType) return; - auto sizeType = dyn_cast(sizeTypes.front()); impl.addSynthesizedTypealias(decl, ctx.Id_Element, valueType->getUnderlyingType()); @@ -561,3 +559,72 @@ void swift::conformToCxxSetIfNeeded(ClangImporter::Implementation &impl, sizeType->getUnderlyingType()); impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxSet}); } + +void swift::conformToCxxPairIfNeeded(ClangImporter::Implementation &impl, + NominalTypeDecl *decl, + const clang::CXXRecordDecl *clangDecl) { + PrettyStackTraceDecl trace("conforming to CxxPair", decl); + + assert(decl); + assert(clangDecl); + ASTContext &ctx = decl->getASTContext(); + + // Only auto-conform types from the C++ standard library. Custom user types + // might have a similar interface but different semantics. + if (!isStdDecl(clangDecl, {"pair"})) + return; + + auto firstType = lookupDirectSingleWithoutExtensions( + decl, ctx.getIdentifier("first_type")); + auto secondType = lookupDirectSingleWithoutExtensions( + decl, ctx.getIdentifier("second_type")); + if (!firstType || !secondType) + return; + + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("First"), + firstType->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("Second"), + secondType->getUnderlyingType()); + impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxPair}); +} + +void swift::conformToCxxDictionaryIfNeeded( + ClangImporter::Implementation &impl, NominalTypeDecl *decl, + const clang::CXXRecordDecl *clangDecl) { + PrettyStackTraceDecl trace("conforming to CxxDictionary", decl); + + assert(decl); + assert(clangDecl); + ASTContext &ctx = decl->getASTContext(); + + // Only auto-conform types from the C++ standard library. Custom user types + // might have a similar interface but different semantics. + if (!isStdDecl(clangDecl, {"map", "unordered_map"})) + return; + + auto keyType = lookupDirectSingleWithoutExtensions( + decl, ctx.getIdentifier("key_type")); + auto valueType = lookupDirectSingleWithoutExtensions( + decl, ctx.getIdentifier("mapped_type")); + auto iterType = lookupDirectSingleWithoutExtensions( + decl, ctx.getIdentifier("const_iterator")); + if (!keyType || !valueType || !iterType) + return; + + // Make the original subscript that returns a non-optional value unavailable. + // CxxDictionary adds another subscript that returns an optional value, + // similarly to Swift.Dictionary. + for (auto member : decl->getCurrentMembersWithoutLoading()) { + if (auto subscript = dyn_cast(member)) { + impl.markUnavailable(subscript, + "use subscript with optional return value"); + } + } + + impl.addSynthesizedTypealias(decl, ctx.Id_Key, keyType->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.Id_Value, + valueType->getUnderlyingType()); + impl.addSynthesizedTypealias(decl, ctx.getIdentifier("RawIterator"), + iterType->getUnderlyingType()); + impl.addSynthesizedProtocolAttrs(decl, {KnownProtocolKind::CxxDictionary}); +} diff --git a/lib/ClangImporter/ClangDerivedConformances.h b/lib/ClangImporter/ClangDerivedConformances.h index 895bc7459d8f1..b31f50e5b7b14 100644 --- a/lib/ClangImporter/ClangDerivedConformances.h +++ b/lib/ClangImporter/ClangDerivedConformances.h @@ -39,6 +39,18 @@ void conformToCxxSetIfNeeded(ClangImporter::Implementation &impl, NominalTypeDecl *decl, const clang::CXXRecordDecl *clangDecl); +/// If the decl is an instantiation of C++ `std::pair`, synthesize a conformance +/// to CxxPair, which is defined in the Cxx module. +void conformToCxxPairIfNeeded(ClangImporter::Implementation &impl, + NominalTypeDecl *decl, + const clang::CXXRecordDecl *clangDecl); + +/// If the decl is an instantiation of C++ `std::map` or `std::unordered_map`, +/// synthesize a conformance to CxxDictionary, which is defined in the Cxx module. +void conformToCxxDictionaryIfNeeded(ClangImporter::Implementation &impl, + NominalTypeDecl *decl, + const clang::CXXRecordDecl *clangDecl); + } // namespace swift #endif // SWIFT_CLANG_DERIVED_CONFORMANCES_H diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 6a511adf1f94b..fcbdcc3ee1d9c 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -2613,6 +2613,8 @@ namespace { conformToCxxIteratorIfNeeded(Impl, nominalDecl, decl); conformToCxxSequenceIfNeeded(Impl, nominalDecl, decl); conformToCxxSetIfNeeded(Impl, nominalDecl, decl); + conformToCxxDictionaryIfNeeded(Impl, nominalDecl, decl); + conformToCxxPairIfNeeded(Impl, nominalDecl, decl); } if (auto *ntd = dyn_cast(result)) diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 190709da5c285..021cb481e520d 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -5875,6 +5875,8 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::DistributedTargetInvocationDecoder: case KnownProtocolKind::DistributedTargetInvocationResultHandler: case KnownProtocolKind::CxxConvertibleToCollection: + case KnownProtocolKind::CxxDictionary: + case KnownProtocolKind::CxxPair: case KnownProtocolKind::CxxRandomAccessCollection: case KnownProtocolKind::CxxSet: case KnownProtocolKind::CxxSequence: diff --git a/stdlib/public/Cxx/CMakeLists.txt b/stdlib/public/Cxx/CMakeLists.txt index 788523e87a028..69c3fe63e19c1 100644 --- a/stdlib/public/Cxx/CMakeLists.txt +++ b/stdlib/public/Cxx/CMakeLists.txt @@ -5,6 +5,8 @@ endif() add_swift_target_library(swiftCxx ${SWIFT_CXX_LIBRARY_KIND} NO_LINK_NAME IS_STDLIB CxxConvertibleToCollection.swift + CxxDictionary.swift + CxxPair.swift CxxSet.swift CxxRandomAccessCollection.swift CxxSequence.swift diff --git a/stdlib/public/Cxx/CxxDictionary.swift b/stdlib/public/Cxx/CxxDictionary.swift new file mode 100644 index 0000000000000..62b924092610b --- /dev/null +++ b/stdlib/public/Cxx/CxxDictionary.swift @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +public protocol CxxDictionary { + associatedtype Key + associatedtype Value + associatedtype RawIterator: UnsafeCxxInputIterator + where RawIterator.Pointee: CxxPair + + /// Do not implement this function manually in Swift. + func __findUnsafe(_ key: Key) -> RawIterator + + /// Do not implement this function manually in Swift. + func __endUnsafe() -> RawIterator +} + +extension CxxDictionary { + @inlinable + public subscript(key: Key) -> Value? { + get { + let iter = __findUnsafe(key) + guard iter != __endUnsafe() else { + return nil + } + return iter.pointee.second + } + } +} diff --git a/stdlib/public/Cxx/CxxPair.swift b/stdlib/public/Cxx/CxxPair.swift new file mode 100644 index 0000000000000..489fdf3ea7857 --- /dev/null +++ b/stdlib/public/Cxx/CxxPair.swift @@ -0,0 +1,19 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +public protocol CxxPair { + associatedtype First + associatedtype Second + + var first: First { get } + var second: Second { get } +} diff --git a/test/Interop/Cxx/stdlib/Inputs/std-map.h b/test/Interop/Cxx/stdlib/Inputs/std-map.h index ffb09f933acbc..25af5dc6ce7ab 100644 --- a/test/Interop/Cxx/stdlib/Inputs/std-map.h +++ b/test/Interop/Cxx/stdlib/Inputs/std-map.h @@ -2,9 +2,12 @@ #define TEST_INTEROP_CXX_STDLIB_INPUTS_STD_MAP_H #include +#include using Map = std::map; +using UnorderedMap = std::unordered_map; inline Map initMap() { return {{1, 3}, {2, 2}, {3, 3}}; } +inline UnorderedMap initUnorderedMap() { return {{1, 3}, {3, 3}, {2, 2}}; } #endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_MAP_H diff --git a/test/Interop/Cxx/stdlib/use-std-map.swift b/test/Interop/Cxx/stdlib/use-std-map.swift index a6f542420b018..675bda38f3d93 100644 --- a/test/Interop/Cxx/stdlib/use-std-map.swift +++ b/test/Interop/Cxx/stdlib/use-std-map.swift @@ -2,8 +2,8 @@ // // REQUIRES: executable_test // -// Enable this everywhere once we have a solution for modularizing other C++ stdlibs: rdar://87654514 -// REQUIRES: OS=macosx || OS=linux-gnu +// REQUIRES: OS=macosx +// TODO: enable on Linux (rdar://105220600) import StdlibUnittest import StdMap @@ -20,22 +20,26 @@ StdMapTestSuite.test("init") { } #endif -StdMapTestSuite.test("subscript") { +StdMapTestSuite.test("Map.subscript") { + // This relies on the `std::map` conformance to `CxxDictionary` protocol. var m = initMap() let at1 = m[1] + expectNotNil(at1) expectEqual(at1, 3) expectEqual(m[2], 2) expectEqual(m[3], 3) + expectNil(m[-1]) + expectNil(m[5]) } -extension Map : CxxSequence { } - -StdMapTestSuite.test("first(where:)") { - let m = initMap() - let found = m.first(where: { $0.first > 1 }) - - expectEqual(found!.first, 2) - expectEqual(found!.second, 2) +StdMapTestSuite.test("UnorderedMap.subscript") { + // This relies on the `std::unordered_map` conformance to `CxxDictionary` protocol. + var m = initUnorderedMap() + expectEqual(m[1], 3) + expectEqual(m[2], 2) + expectEqual(m[3], 3) + expectNil(m[-1]) + expectNil(m[5]) } runAllTests() From 7acc339b905b2be725254999f7ceb07560410070 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 8 Feb 2023 15:20:27 -0800 Subject: [PATCH 77/98] [IRGen] Lowered tuple_pack_element_addr. --- lib/IRGen/GenTuple.cpp | 14 ++++++-------- test/IRGen/variadic_generics.sil | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/lib/IRGen/GenTuple.cpp b/lib/IRGen/GenTuple.cpp index 378377dac4cb3..0a48070488a9b 100644 --- a/lib/IRGen/GenTuple.cpp +++ b/lib/IRGen/GenTuple.cpp @@ -509,14 +509,12 @@ Address irgen::projectTupleElementAddressByDynamicIndex(IRGenFunction &IGF, SILType tupleType, llvm::Value *index, SILType elementType) { - // TODO: retrieve type metadata (ForLayout) for the tuple type - // and retrieve the field offset for the given index. Consider - // special-casing constant indices if we can reason statically - // about the prior elements. It's probably not necessary to try - // to handle fixed-size tuples specially, because the optimizer - // should ideally be lowering this operation to something static - // in that case. - llvm_unreachable("unimplemented"); + auto *metadata = IGF.emitTypeMetadataRefForLayout(tupleType); + + llvm::Value *offset = loadTupleOffsetFromMetadata(IGF, metadata, index); + auto *gep = + IGF.emitByteOffsetGEP(tuple.getAddress(), offset, IGF.IGM.OpaqueTy); + return Address(gep, IGF.IGM.OpaqueTy, IGF.IGM.getPointerAlignment()); } Optional irgen::getFixedTupleElementOffset(IRGenModule &IGM, diff --git a/test/IRGen/variadic_generics.sil b/test/IRGen/variadic_generics.sil index ad7f6952b6cf7..d04f93fd58dd7 100644 --- a/test/IRGen/variadic_generics.sil +++ b/test/IRGen/variadic_generics.sil @@ -106,3 +106,31 @@ bb0(%pack : $*Pack{Int, repeat each T, Int}, %value : $Int): %ret = tuple () return %ret : $() } + +sil @borrow : $ (@in_guaranteed T) -> () { +entry(%addr : $*T): + %ret = tuple () + return %ret : $() +} + +// CHECK-LABEL: define {{.*}}@test_tuple_pack_element_addr_1( +// CHECK-SAME: {{.*}}* nocapture [[TUPLE_ADDR:%[^,]+]], i{{(64|32)}} [[INDEX:%[^,]+]] +// CHECK: [[RESPONSE:%[^,]+]] = call swiftcc %swift.metadata_response @swift_getTupleTypeMetadata +// CHECK: [[UNCAST_METADATA:%[^,]+]] = extractvalue %swift.metadata_response [[RESPONSE]], 0 +// CHECK: [[METADATA:%[^,]+]] = bitcast %swift.type* [[UNCAST_METADATA]] to %swift.tuple_type* +// CHECK: [[OFFSET_PTR:%[^,]+]] = getelementptr inbounds %swift.tuple_type, %swift.tuple_type* [[METADATA]], i{{(64|32)}} 0, i32 3, i{{(64|32)}} [[INDEX]] +// CHECK: [[OFFSET:%[^,]+]] = load i32, i32* [[OFFSET_PTR]], align 8 +// CHECK: [[CAST_TUPLE_ADDR:%[^,]+]] = bitcast <{ %TSS }>* [[TUPLE_ADDR]] to i8* +// CHECK: [[UNCAST_ELEMENT_ADDR:%[^,]+]] = getelementptr inbounds i8, i8* [[CAST_TUPLE_ADDR]], i32 [[OFFSET]] +// CHECK: [[ELEMENT_ADDR:%[^,]+]] = bitcast i8* [[UNCAST_ELEMENT_ADDR]] to %swift.opaque* +// CHECK: call swiftcc void @borrow(%swift.opaque* noalias nocapture [[ELEMENT_ADDR]], %swift.type* %"\CF\84_1_0") +sil @test_tuple_pack_element_addr_1 : $ (@inout (String, T, U, Int), Builtin.Word) -> () { +bb0(%tuple : $*(String, T, U, Int), %i : $Builtin.Word): + %index = dynamic_pack_index %i of $Pack{Float, T, U, Float} + %0 = open_pack_element %index of at , shape $U_1, uuid "01234567-89AB-CDEF-0123-000000000004" + %elt = tuple_pack_element_addr %index of %tuple : $*(String, T, U, Int) as $*@pack_element("01234567-89AB-CDEF-0123-000000000004") U_1 + %blackhole = function_ref @borrow : $@convention(thin) (@in_guaranteed T) -> () + apply %blackhole<(@pack_element("01234567-89AB-CDEF-0123-000000000004") U_1)>(%elt) : $@convention(thin) (@in_guaranteed T) -> () + %ret = tuple () + return %ret : $() +} From da5b5c1ea5ae7c3b5d257b4526f289ad4b91d006 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 7 Feb 2023 20:34:11 -0800 Subject: [PATCH 78/98] [IRGen] Lowered de/alloc_pack. --- lib/IRGen/GenPack.cpp | 39 ++++++++++++++++++++++++++++++++ lib/IRGen/GenPack.h | 4 ++++ lib/IRGen/IRGenSIL.cpp | 7 ++++-- test/IRGen/variadic_generics.sil | 36 +++++++++++++++++++++++++++-- 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/lib/IRGen/GenPack.cpp b/lib/IRGen/GenPack.cpp index 220fe8cc0ad37..5daa102ad8b4c 100644 --- a/lib/IRGen/GenPack.cpp +++ b/lib/IRGen/GenPack.cpp @@ -882,3 +882,42 @@ Size irgen::getPackElementSize(IRGenModule &IGM, CanSILPackType ty) { assert(ty->isElementAddress() && "not implemented for direct packs"); return IGM.getPointerSize(); } + +StackAddress irgen::allocatePack(IRGenFunction &IGF, CanSILPackType packType) { + auto *shape = IGF.emitPackShapeExpression(packType); + + auto elementSize = getPackElementSize(IGF.IGM, packType); + + if (auto *constantInt = dyn_cast(shape)) { + assert(packType->getNumElements() == constantInt->getValue()); + (void)constantInt; + assert(!packType->containsPackExpansionType()); + unsigned elementCount = packType->getNumElements(); + auto allocType = llvm::ArrayType::get( + IGF.IGM.OpaquePtrTy, elementCount); + + auto addr = IGF.createAlloca(allocType, IGF.IGM.getPointerAlignment()); + IGF.Builder.CreateLifetimeStart(addr, + elementSize * elementCount); + return addr; + } + + assert(packType->containsPackExpansionType()); + auto addr = IGF.emitDynamicAlloca(IGF.IGM.OpaquePtrTy, shape, + IGF.IGM.getPointerAlignment(), + /*allowTaskAlloc=*/true); + + return addr; +} + +void irgen::deallocatePack(IRGenFunction &IGF, StackAddress addr, CanSILPackType packType) { + if (packType->containsPackExpansionType()) { + IGF.emitDeallocateDynamicAlloca(addr); + return; + } + + auto elementSize = getPackElementSize(IGF.IGM, packType); + auto elementCount = packType->getNumElements(); + IGF.Builder.CreateLifetimeEnd(addr.getAddress(), + elementSize * elementCount); +} diff --git a/lib/IRGen/GenPack.h b/lib/IRGen/GenPack.h index 0b57b9f99973c..701f0d2aefb15 100644 --- a/lib/IRGen/GenPack.h +++ b/lib/IRGen/GenPack.h @@ -91,6 +91,10 @@ Address emitStorageAddressOfPackElement(IRGenFunction &IGF, Address pack, Size getPackElementSize(IRGenModule &, CanSILPackType ty); +StackAddress allocatePack(IRGenFunction &IGF, CanSILPackType packType); + +void deallocatePack(IRGenFunction &IGF, StackAddress addr, CanSILPackType packType); + } // end namespace irgen } // end namespace swift diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index c7a77280e7eb9..bf8ce15cb15de 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -5502,7 +5502,8 @@ void IRGenSILFunction::visitAllocStackInst(swift::AllocStackInst *i) { } void IRGenSILFunction::visitAllocPackInst(swift::AllocPackInst *i) { - IGM.unimplemented(i->getLoc().getSourceLoc(), "alloc_pack"); + auto addr = allocatePack(*this, i->getPackType()); + setLoweredStackAddress(i, addr); } static void @@ -5605,7 +5606,9 @@ void IRGenSILFunction::visitDeallocStackRefInst(DeallocStackRefInst *i) { } void IRGenSILFunction::visitDeallocPackInst(swift::DeallocPackInst *i) { - IGM.unimplemented(i->getLoc().getSourceLoc(), "dealloc_pack"); + auto allocatedType = cast(i->getOperand()->getType().getASTType()); + StackAddress stackAddr = getLoweredStackAddress(i->getOperand()); + deallocatePack(*this, stackAddr, allocatedType); } void IRGenSILFunction::visitDeallocRefInst(swift::DeallocRefInst *i) { diff --git a/test/IRGen/variadic_generics.sil b/test/IRGen/variadic_generics.sil index ad7f6952b6cf7..c2436dc53a046 100644 --- a/test/IRGen/variadic_generics.sil +++ b/test/IRGen/variadic_generics.sil @@ -88,8 +88,8 @@ bb0(%pack : $*Pack{Int, repeat each T, Int}): %elementAddr = pack_element_get %index of %pack : $*Pack{Int, repeat each T, Int} as $*Int %value = load %elementAddr : $*Int return %value : $Int -} - + } + // CHECK-LABEL: define{{.*}} @test_pack_element_set_1( // CHECK: [[INDEX:%.*]] = add [[INT]] %2, 1 // CHECK: [[ELT_STORAGE:%.*]] = getelementptr inbounds %swift.opaque*, %swift.opaque** %0, [[INT]] [[INDEX]] @@ -103,6 +103,38 @@ bb0(%pack : $*Pack{Int, repeat each T, Int}, %value : $Int): %index = scalar_pack_index 2 of $Pack{Int, repeat each T, Int} %elementAddr = pack_element_get %index of %pack : $*Pack{Int, repeat each T, Int} as $*Int store %value to %elementAddr : $*Int + %ret = tuple () + return %ret : $() + } + +// CHECK-LABEL: define {{.*}}@test_pack_alloc_1_dynamic( +// CHECK-SAME: i{{(32|64)}} [[PACK_SIZE:%[^,]+]] +// CHECK: [[SIZE:%[^,]+]] = add i64 [[PACK_SIZE]], [[PACK_SIZE]] +// CHECK: [[SP_SAVE:%[^,]+]] = call i8* @llvm.stacksave() +// CHECK: alloca %swift.opaque*, i{{(32|64)}} [[SIZE]] +// CHECK: call void @llvm.stackrestore(i8* [[SP_SAVE]]) +sil @test_pack_alloc_1_dynamic : $ () -> () { +// Control flow so that stack saving/restoring is emitted +entry: + cond_br undef, left, right +left: + br exit +right: + br exit +exit: + %addr = alloc_pack $Pack{repeat each T, repeat each T} + dealloc_pack %addr : $*Pack{repeat each T, repeat each T} + %ret = tuple () + return %ret : $() +} + +// CHECK-LABEL: define {{.*}}@test_pack_alloc_2_static +// CHECK: [[STACK:%[^,]+]] = alloca [2 x %swift.opaque*] +// CHECK: call void @llvm.lifetime.start.p0i8 +// CHECK: call void @llvm.lifetime.end.p0i8 +sil @test_pack_alloc_2_static : $ () -> () { + %addr = alloc_pack $Pack{Int, Int} + dealloc_pack %addr : $*Pack{Int, Int} %ret = tuple () return %ret : $() } From 11bcfd4d0c4e5c4959d126214fdeaeaadd39bbcd Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Thu, 9 Feb 2023 11:16:27 -0500 Subject: [PATCH 79/98] [Test] Mark test/stdlib/Reflection/PartialType.swift unsupported in freestanding builds. --- test/stdlib/Reflection/PartialType.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/test/stdlib/Reflection/PartialType.swift b/test/stdlib/Reflection/PartialType.swift index 1a99e915d818d..49f7a2f61428e 100644 --- a/test/stdlib/Reflection/PartialType.swift +++ b/test/stdlib/Reflection/PartialType.swift @@ -1,5 +1,6 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +// UNSUPPORTED: freestanding import StdlibUnittest import _Runtime From a705ee26f03cebce7ae4bd61e682f8fb2aef5007 Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Thu, 9 Feb 2023 09:31:16 -0800 Subject: [PATCH 80/98] [Build] Limit Ubuntu 18.04 LSAN CI to 12 link jobs It's currently running out of memory, likely due to the machine's high core count and linking many jobs at once. Limit to 12 only. --- utils/build-presets.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 9841e08c3b457..77a906ba99920 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -1215,6 +1215,9 @@ reconfigure [preset: buildbot_incremental_linux,lsan,tools=RDA,stdlib=DA,test=no] build-subdir=buildbot_incremental_lsan +llvm-cmake-options=-DLLVM_PARALLEL_LINK_JOBS=12 +swift-cmake-options=-DSWIFT_PARALLEL_LINK_JOBS=12 + release-debuginfo assertions enable-lsan From 975111064b764ec09de4fa4e44db6986c1e7b8f0 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 9 Feb 2023 10:26:45 -0800 Subject: [PATCH 81/98] [Test] Loosened FileCheck lines for 32bit. rdar://105226018 --- test/IRGen/variadic_generic_functions.sil | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/IRGen/variadic_generic_functions.sil b/test/IRGen/variadic_generic_functions.sil index 1b47f6ddc421e..2f8155f09b174 100644 --- a/test/IRGen/variadic_generic_functions.sil +++ b/test/IRGen/variadic_generic_functions.sil @@ -24,7 +24,7 @@ sil_witness_table S_3 : P module main {} // CHECK: store i8** getelementptr inbounds {{.*}}$s26variadic_generic_functions3S_2VAA1PAAWP{{.*}}, i8*** [[WTABLE_ELEMENT_0]] // CHECK: [[METADATA_PACK_PTR:%[^,]+]] = bitcast [1 x %swift.type*]* [[METADATA_PACK]] to %swift.type** // CHECK: [[WTABLE_PACK_ADDR:%[^,]+]] = bitcast [1 x i8**]* [[WTABLE_PACK]] to i8*** -// CHECK: call swiftcc void @g(i64 1, %swift.type** [[METADATA_PACK_PTR]], i8*** [[WTABLE_PACK_ADDR]]) +// CHECK: call swiftcc void @g(i{{(64|32)}} 1, %swift.type** [[METADATA_PACK_PTR]], i8*** [[WTABLE_PACK_ADDR]]) sil @c : $() -> () { %g = function_ref @g : $@convention(thin) () -> () apply %g() : $@convention(thin) () -> () @@ -32,13 +32,13 @@ sil @c : $() -> () { return %ret : $() } -// CHECK: define {{.*}}void @f(i64 %0, %swift.type** %T, i8*** %T.P) +// CHECK: define {{.*}}void @f(i{{(64|32)}} %0, %swift.type** %T, i8*** %T.P) sil @f : $ () -> () { %ret = tuple () return %ret : $() } -// CHECK: define {{.*}}void @g(i64 %0, %swift.type** %T, i8*** %T.P) +// CHECK: define {{.*}}void @g(i{{(64|32)}} %0, %swift.type** %T, i8*** %T.P) sil @g : $ () -> () { %f = function_ref @f : $@convention(thin) () -> () apply %f() : $@convention(thin) () -> () From 8a163fff1b80a82506af5cfa32c43b4bfae3ba77 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Thu, 9 Feb 2023 10:25:57 -0800 Subject: [PATCH 82/98] Limit the amount of parallelism on asan LLDB bots to nphyscpus/2 --- utils/build-script-impl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/build-script-impl b/utils/build-script-impl index 3e87709ac509d..4f13eeb54d236 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -2911,7 +2911,7 @@ for host in "${ALL_HOSTS[@]}"; do if [[ "${ENABLE_ASAN}" ]] ; then # Limit the number of parallel tests - LLVM_LIT_ARGS="${LLVM_LIT_ARGS} -j $(sysctl hw.physicalcpu | awk -v N=${LIT_JOBS} '{ print (N < $2) ? N : int($2/2) }')" + LLVM_LIT_ARGS="${LLVM_LIT_ARGS} -j $(sysctl hw.physicalcpu | awk -v N=${LIT_JOBS} '{ print (N < int($2/2)) ? N : int($2/2) }')" fi FILTER_SWIFT_OPTION="--filter=[sS]wift" From 47051831490df3626f6cfe8fac47aab82bb32973 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 9 Feb 2023 13:47:26 -0800 Subject: [PATCH 83/98] [Macros] Add plugin search paths to load plugin modules by name. Introduce `-plugin-path ` to add a search path where we will look for compiler plugins. When resolving an external macro definition, look for libraries in these search paths whose names match the module name of the macro. Implements rdar://105095761. --- include/swift/AST/SearchPathOptions.h | 4 ++ include/swift/AST/TypeCheckRequests.h | 24 +++++++ include/swift/AST/TypeCheckerTypeIDZone.def | 3 + include/swift/Option/Options.td | 4 ++ lib/Driver/ToolChains.cpp | 1 + lib/Frontend/CompilerInvocation.cpp | 4 ++ lib/Sema/TypeCheckMacros.cpp | 74 ++++++++++++++++++--- test/Macros/macro_expand.swift | 2 + 8 files changed, 107 insertions(+), 9 deletions(-) diff --git a/include/swift/AST/SearchPathOptions.h b/include/swift/AST/SearchPathOptions.h index e1d68dc50c2fa..a1fd90a6fe2b7 100644 --- a/include/swift/AST/SearchPathOptions.h +++ b/include/swift/AST/SearchPathOptions.h @@ -339,6 +339,10 @@ class SearchPathOptions { /// preference. std::vector RuntimeLibraryPaths; + /// Paths that contain compiler plugins loaded on demand for, e.g., + /// macro implementations. + std::vector PluginSearchPaths; + /// Don't look in for compiler-provided modules. bool SkipRuntimeLibraryImportPaths = false; diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 6973e086091ee..9baab995f0972 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -4048,6 +4048,30 @@ class ExpandSynthesizedMemberMacroRequest bool isCached() const { return true; } }; +/// Load a plugin module with the given name. +/// +/// +class CompilerPluginLoadRequest + : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + void *evaluate( + Evaluator &evaluator, ASTContext *ctx, Identifier moduleName + ) const; + +public: + // Source location + SourceLoc getNearestLoc() const { return SourceLoc(); } + + bool isCached() const { return true; } +}; + /// Resolve an external macro given its module and type name. class ExternalMacroDefinitionRequest : public SimpleRequest, def I_EQ : Joined<["-"], "I=">, Flags<[FrontendOption, ArgumentIsPath]>, Alias; +def plugin_path : Separate<["-"], "plugin-path">, + Flags<[FrontendOption, ArgumentIsPath, SwiftAPIExtractOption, SwiftSymbolGraphExtractOption, SwiftAPIDigesterOption]>, + HelpText<"Add directory to the plugin search path">; + def import_underlying_module : Flag<["-"], "import-underlying-module">, Flags<[FrontendOption, NoInteractiveOption]>, HelpText<"Implicitly imports the Objective-C half of a module">; diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 1a5375dfa9376..371384b3d4f1a 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -218,6 +218,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddAllArgs(arguments, options::OPT_I); inputArgs.AddAllArgs(arguments, options::OPT_F, options::OPT_Fsystem); inputArgs.AddAllArgs(arguments, options::OPT_vfsoverlay); + inputArgs.AddAllArgs(arguments, options::OPT_plugin_path); inputArgs.AddLastArg(arguments, options::OPT_AssertConfig); inputArgs.AddLastArg(arguments, options::OPT_autolink_force_load); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 58bf5e0dc902d..861ac32799428 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1413,6 +1413,10 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts, } Opts.setFrameworkSearchPaths(FrameworkSearchPaths); + for (const Arg *A : Args.filtered(OPT_plugin_path)) { + Opts.PluginSearchPaths.push_back(resolveSearchPath(A->getValue())); + } + for (const Arg *A : Args.filtered(OPT_L)) { Opts.LibrarySearchPaths.push_back(resolveSearchPath(A->getValue())); } diff --git a/lib/Sema/TypeCheckMacros.cpp b/lib/Sema/TypeCheckMacros.cpp index 08042dfb07be8..fb3c65995a8b7 100644 --- a/lib/Sema/TypeCheckMacros.cpp +++ b/lib/Sema/TypeCheckMacros.cpp @@ -32,6 +32,15 @@ #include "swift/Demangling/ManglingMacros.h" #include "swift/Parse/Lexer.h" #include "swift/Subsystems.h" +#include "llvm/Config/config.h" + +#if defined(_WIN32) +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#else +#include +#endif using namespace swift; @@ -100,7 +109,9 @@ static std::string mangledNameForTypeMetadataAccessor( #if SWIFT_SWIFT_PARSER /// Look for macro's type metadata given its external module and type name. static void const *lookupMacroTypeMetadataByExternalName( - ASTContext &ctx, StringRef moduleName, StringRef typeName) { + ASTContext &ctx, StringRef moduleName, StringRef typeName, + void *libraryHint = nullptr +) { // Look up the type metadata accessor as a struct, enum, or class. const Demangle::Node::Kind typeKinds[] = { Demangle::Node::Kind::Structure, @@ -111,8 +122,8 @@ static void const *lookupMacroTypeMetadataByExternalName( void *accessorAddr = nullptr; for (auto typeKind : typeKinds) { auto symbolName = mangledNameForTypeMetadataAccessor( - moduleName, typeName, typeKind); - accessorAddr = ctx.getAddressOfSymbol(symbolName.c_str()); + moduleName, typeName, typeKind); + accessorAddr = ctx.getAddressOfSymbol(symbolName.c_str(), libraryHint); if (accessorAddr) break; } @@ -289,20 +300,43 @@ MacroDefinition MacroDefinitionRequest::evaluate( return MacroDefinition::forExternal(*moduleName, *typeName); } -ExternalMacroDefinition -ExternalMacroDefinitionRequest::evaluate( - Evaluator &evaluator, ASTContext *ctx, - Identifier moduleName, Identifier typeName +/// Load a plugin library based on a module name. +static void *loadPluginByName(StringRef searchPath, StringRef moduleName) { + SmallString<128> fullPath(searchPath); + llvm::sys::path::append(fullPath, "lib" + moduleName + LTDL_SHLIB_EXT); +#if defined(_WIN32) + return LoadLibraryA(fullPath.c_str()); +#else + return dlopen(fullPath.c_str(), RTLD_LAZY); +#endif +} + +void *CompilerPluginLoadRequest::evaluate( + Evaluator &evaluator, ASTContext *ctx, Identifier moduleName ) const { + auto &searchPathOpts = ctx->SearchPathOpts; + for (const auto &path : searchPathOpts.PluginSearchPaths) { + if (auto found = loadPluginByName(path, moduleName.str())) + return found; + } + + return nullptr; +} + +static Optional +resolveInProcessMacro( + ASTContext &ctx, Identifier moduleName, Identifier typeName, + void *libraryHint = nullptr +) { #if SWIFT_SWIFT_PARSER /// Look for the type metadata given the external module and type names. auto macroMetatype = lookupMacroTypeMetadataByExternalName( - *ctx, moduleName.str(), typeName.str()); + ctx, moduleName.str(), typeName.str(), libraryHint); if (macroMetatype) { // Check whether the macro metatype is in-process. if (auto inProcess = swift_ASTGen_resolveMacroType(macroMetatype)) { // Make sure we clean up after the macro. - ctx->addCleanup([inProcess]() { + ctx.addCleanup([inProcess]() { swift_ASTGen_destroyMacro(inProcess); }); @@ -311,6 +345,28 @@ ExternalMacroDefinitionRequest::evaluate( } #endif + return None; +} + +ExternalMacroDefinition +ExternalMacroDefinitionRequest::evaluate( + Evaluator &evaluator, ASTContext *ctx, + Identifier moduleName, Identifier typeName +) const { + // Try to load a plugin module from the plugin search paths. If it + // succeeds, resolve in-process from that plugin + CompilerPluginLoadRequest loadRequest{ctx, moduleName}; + if (auto loadedLibrary = evaluateOrDefault( + evaluator, loadRequest, nullptr)) { + if (auto inProcess = resolveInProcessMacro( + *ctx, moduleName, typeName, loadedLibrary)) + return *inProcess; + } + + // Try to resolve in-process. + if (auto inProcess = resolveInProcessMacro(*ctx, moduleName, typeName)) + return *inProcess; + return ExternalMacroDefinition{nullptr}; } diff --git a/test/Macros/macro_expand.swift b/test/Macros/macro_expand.swift index 645b6b1749db1..cc56b13a60558 100644 --- a/test/Macros/macro_expand.swift +++ b/test/Macros/macro_expand.swift @@ -5,6 +5,8 @@ // Diagnostics testing // RUN: %target-typecheck-verify-swift -swift-version 5 -enable-experimental-feature Macros -load-plugin-library %t/%target-library-name(MacroDefinition) -I %swift-host-lib-dir -module-name MacroUser -DTEST_DIAGNOSTICS +// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-experimental-feature Macros -plugin-path %t -I %swift-host-lib-dir -module-name MacroUser -DTEST_DIAGNOSTICS + // RUN: not %target-swift-frontend -swift-version 5 -typecheck -enable-experimental-feature Macros -load-plugin-library %t/%target-library-name(MacroDefinition) -I %swift-host-lib-dir -module-name MacroUser -DTEST_DIAGNOSTICS -serialize-diagnostics-path %t/macro_expand.dia %s -emit-macro-expansion-files no-diagnostics > %t/macro-printing.txt // RUN: c-index-test -read-diagnostics %t/macro_expand.dia 2>&1 | %FileCheck -check-prefix CHECK-DIAGS %s From e7ee83918999144ef51a68502b46ef37243258ba Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Tue, 7 Feb 2023 10:59:46 +0100 Subject: [PATCH 84/98] [CursorInfo] A few miscellaneous fixes to the AST-based cursor info and new test cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In these cases the solver-based and AST-based cursor info differed in their results. Fix the AST-based cursor info to return the correct results and add test cases to make sure the solver-based implementation doesn’t regress them. --- lib/IDE/Utils.cpp | 13 ++++--- .../CursorInfo/cursor_simplify_type.swift | 35 ++++++++++++++++++ ...look_through_identity_expr_and_types.swift | 26 +++++++++++++ .../CursorInfo/static_vs_class_spelling.swift | 37 +++++++++++++++++++ 4 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 test/SourceKit/CursorInfo/cursor_simplify_type.swift create mode 100644 test/SourceKit/CursorInfo/look_through_identity_expr_and_types.swift create mode 100644 test/SourceKit/CursorInfo/static_vs_class_spelling.swift diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index acda37ad8cd0a..2b100fa378463 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -860,6 +860,9 @@ Expr *swift::ide::getBase(ArrayRef ExprStack) { Expr *CurrentE = ExprStack.back(); Expr *ParentE = getContainingExpr(ExprStack, 1); + if (ParentE && isa(ParentE)) { + ParentE = getContainingExpr(ExprStack, 2); + } Expr *Base = nullptr; if (auto DSE = dyn_cast_or_null(ParentE)) @@ -925,6 +928,8 @@ bool swift::ide::isDynamicRef(Expr *Base, ValueDecl *D, llvm::function_refgetSemanticsProvidingExpr(); + // super.method() // TODO: Should be dynamic if `D` is marked as dynamic and @objc, but in // that case we really need to change the role the index outputs as @@ -956,11 +961,9 @@ void swift::ide::getReceiverType(Expr *Base, if (!ReceiverTy) return; - if (auto LVT = ReceiverTy->getAs()) - ReceiverTy = LVT->getObjectType(); - else if (auto MetaT = ReceiverTy->getAs()) - ReceiverTy = MetaT->getInstanceType(); - else if (auto SelfT = ReceiverTy->getAs()) + ReceiverTy = ReceiverTy->getWithoutSpecifierType(); + ReceiverTy = ReceiverTy->getMetatypeInstanceType(); + if (auto SelfT = ReceiverTy->getAs()) ReceiverTy = SelfT->getSelfType(); // TODO: Handle generics and composed protocols diff --git a/test/SourceKit/CursorInfo/cursor_simplify_type.swift b/test/SourceKit/CursorInfo/cursor_simplify_type.swift new file mode 100644 index 0000000000000..9d92f2c25432f --- /dev/null +++ b/test/SourceKit/CursorInfo/cursor_simplify_type.swift @@ -0,0 +1,35 @@ +protocol Publisher { + associatedtype Output +} + +extension Publisher { + func stage2() -> MyGenerictype { + fatalError() + } + + func stage3() -> Self { + fatalError() + } +} + +struct MyGenerictype : Publisher { + init() {} + + func stage1(with output: Self.Output) -> HasTypeAccessInTypealias { + fatalError() + } +} + +struct HasTypeAccessInTypealias : Publisher where Upstream : Publisher { + typealias Output = Upstream.Output +} + +func test() { + MyGenerictype() + .stage1(with: 0) + .stage2() + // RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):6 %s -- %s + .stage3() +} + +// CHECK: func stage3() -> MyGenerictype<HasTypeAccessInTypealias<MyGenerictype<Int>>.Output> diff --git a/test/SourceKit/CursorInfo/look_through_identity_expr_and_types.swift b/test/SourceKit/CursorInfo/look_through_identity_expr_and_types.swift new file mode 100644 index 0000000000000..3836e7fc6c285 --- /dev/null +++ b/test/SourceKit/CursorInfo/look_through_identity_expr_and_types.swift @@ -0,0 +1,26 @@ +func lookThroughFunctionConversions() { + class DispatchQueue { + class var main: DispatchQueue { get } + func async(execute work: @escaping @convention(block) () -> Void) + } + + // RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):22 %s -- %s | %FileCheck %s --check-prefix=FUNCTION_CONVERSION + DispatchQueue.main.async {} +} + +// FUNCTION_CONVERSION: (DispatchQueue) -> (@escaping @convention(block) () -> ()) -> () +// FUNCTION_CONVERSION: DYNAMIC + +func lookThorughInout() { + public struct Villager {} + let villager = Villager() + + var villagers: [Villager] = [] + // RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):13 %s -- %s | %FileCheck %s + villagers.removeAll(where: { _ in true }) +} + +// CHECK: RECEIVERS BEGIN +// CHECK: s:Sa +// CHECK: RECEIVERS END + diff --git a/test/SourceKit/CursorInfo/static_vs_class_spelling.swift b/test/SourceKit/CursorInfo/static_vs_class_spelling.swift new file mode 100644 index 0000000000000..420122bc8d8bc --- /dev/null +++ b/test/SourceKit/CursorInfo/static_vs_class_spelling.swift @@ -0,0 +1,37 @@ +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/Modules) +// RUN: %{python} %utils/split_file.py -o %t %s + + +// BEGIN MyModule.swift + +public class UserCollection { + public static let sharedStatic = UserCollection() + public class var sharedComputedClass: UserCollection { UserCollection() } +} + +// RUN: %target-swift-frontend \ +// RUN: -emit-module \ +// RUN: -module-name MyModule \ +// RUN: -emit-module-path %t/Modules/MyModule.swiftmodule \ +// RUN: -emit-module-doc-path %t/Modules/MyModule.swiftdoc \ +// RUN: %t/MyModule.swift + +// BEGIN test.swift +import MyModule + +func application() { + UserCollection.sharedStatic + UserCollection.sharedComputedClass +} + +// RUN: %sourcekitd-test -req=cursor -pos=4:18 %t/test.swift -- %t/test.swift -I %t/Modules | %FileCheck %s --check-prefix=SHARED_STATIC + +// FIXME: This should be reported as 'static var' rdar://105239467 +// SHARED_STATIC: class let sharedStatic: UserCollection +// SHARED_STATIC: class let sharedStatic: UserCollection + +// RUN: %sourcekitd-test -req=cursor -pos=5:18 %t/test.swift -- %t/test.swift -I %t/Modules| %FileCheck %s --check-prefix=SHARED_COMPUTED_CLASS + +// SHARED_COMPUTED_CLASS: class var sharedComputedClass: UserCollection { get } +// SHARED_COMPUTED_CLASS: class var sharedComputedClass: UserCollection { get } From 3e460630b7bc142dae52d65f8ee1a5963ed2c130 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 8 Feb 2023 11:11:18 +0100 Subject: [PATCH 85/98] [CursorInfo] Fix bug when performing solver-based cursor info on Self --- lib/AST/ASTPrinter.cpp | 5 +++++ test/SourceKit/CursorInfo/dynamic_self.swift | 7 +++++++ 2 files changed, 12 insertions(+) create mode 100644 test/SourceKit/CursorInfo/dynamic_self.swift diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 6d2262ebfae09..9e6e378071826 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -81,6 +81,11 @@ const std::function void PrintOptions::setBaseType(Type T) { if (T->is()) return; + if (auto DynamicSelf = T->getAs()) { + // TypeTransformContext requires `T` to have members. Look through dynamic + // Self. + T = DynamicSelf->getSelfType(); + } TransformContext = TypeTransformContext(T); } diff --git a/test/SourceKit/CursorInfo/dynamic_self.swift b/test/SourceKit/CursorInfo/dynamic_self.swift new file mode 100644 index 0000000000000..38ecc16cbb3ac --- /dev/null +++ b/test/SourceKit/CursorInfo/dynamic_self.swift @@ -0,0 +1,7 @@ +class UserCollection { + static let staticMember = "ABC" + func test() { + // RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):10 %s -- %s + Self.staticMember + } +} From a9cba5457c0db32fe05de886d037ff9a2f9627e1 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 8 Feb 2023 11:26:33 +0100 Subject: [PATCH 86/98] [CursorInfo] Fix a bug that caused solver-based cursor info to crash if invoked on a symbol from a different module --- lib/IDE/CursorInfo.cpp | 11 ++++++++--- lib/IDE/SelectedOverloadInfo.cpp | 3 +++ .../CursorInfo/cursor_in_stdlib_module.swift | 12 ++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 test/SourceKit/CursorInfo/cursor_in_stdlib_module.swift diff --git a/lib/IDE/CursorInfo.cpp b/lib/IDE/CursorInfo.cpp index 99defbcf52c19..8d68d7fd191e2 100644 --- a/lib/IDE/CursorInfo.cpp +++ b/lib/IDE/CursorInfo.cpp @@ -48,9 +48,14 @@ void typeCheckDeclAndParentClosures(ValueDecl *VD) { DC = DC->getParent(); } - typeCheckASTNodeAtLoc( - TypeCheckASTNodeAtLocContext::declContext(VD->getDeclContext()), - VD->getLoc()); + if (!VD->getInterfaceType()) { + // The decl has an interface time if it came from another module. In that + // case, there's nothing to do. Otherwise, type check the decl to get its + // type. + typeCheckASTNodeAtLoc( + TypeCheckASTNodeAtLocContext::declContext(VD->getDeclContext()), + VD->getLoc()); + } if (auto VarD = dyn_cast(VD)) { if (VarD->hasAttachedPropertyWrapper()) { // Type check any attached property wrappers so the annotated declaration diff --git a/lib/IDE/SelectedOverloadInfo.cpp b/lib/IDE/SelectedOverloadInfo.cpp index 1f6bb4e7490c9..778a2dff6591c 100644 --- a/lib/IDE/SelectedOverloadInfo.cpp +++ b/lib/IDE/SelectedOverloadInfo.cpp @@ -36,6 +36,9 @@ swift::ide::getSelectedOverloadInfo(const Solution &S, if (Result.BaseTy) { Result.BaseTy = S.simplifyType(Result.BaseTy)->getRValueType(); } + if (Result.BaseTy && Result.BaseTy->is()) { + Result.BaseTy = nullptr; + } Result.Value = SelectedOverload->choice.getDeclOrNull(); Result.ValueTy = diff --git a/test/SourceKit/CursorInfo/cursor_in_stdlib_module.swift b/test/SourceKit/CursorInfo/cursor_in_stdlib_module.swift new file mode 100644 index 0000000000000..2cde771d5fd94 --- /dev/null +++ b/test/SourceKit/CursorInfo/cursor_in_stdlib_module.swift @@ -0,0 +1,12 @@ +func test() { + // RUN: %sourcekitd-test -req=cursor -pos=%(line + 1):9 %s -- %s | %FileCheck %s + Swift.min(1, 2) +} + +// CHECK: source.lang.swift.ref.function.free () +// CHECK-NEXT: min(_:_:) +// CHECK-NEXT: s:s3minyxx_xtSLRzlF +// CHECK-NEXT: source.lang.swift +// CHECK-NEXT: (T, T) -> T +// CHECK-NEXT: $syxx_xtcSLRzluD +// CHECK-NEXT: Swift From ba3cc288b6240a9012b09d2b2cd926cda8ea5ae3 Mon Sep 17 00:00:00 2001 From: Xi Ge Date: Thu, 9 Feb 2023 12:44:23 -0800 Subject: [PATCH 87/98] ABI Checker: avoid reporting module importation changes ABI changes due to imported module changes should be reflected by other symbol changes. Reporting module import changes seems to be redundant. --- lib/DriverTool/swift_api_digester_main.cpp | 3 +-- test/api-digester/Outputs/apinotes-diags.txt | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/DriverTool/swift_api_digester_main.cpp b/lib/DriverTool/swift_api_digester_main.cpp index 0f50c34310b77..00376f813982b 100644 --- a/lib/DriverTool/swift_api_digester_main.cpp +++ b/lib/DriverTool/swift_api_digester_main.cpp @@ -549,8 +549,7 @@ namespace { static bool isMissingDeclAcceptable(const SDKNodeDecl *D) { // Don't complain about removing importation of SwiftOnoneSupport. - if (D->getKind() == SDKNodeKind::DeclImport && - D->getName() == "SwiftOnoneSupport") { + if (D->getKind() == SDKNodeKind::DeclImport) { return true; } return false; diff --git a/test/api-digester/Outputs/apinotes-diags.txt b/test/api-digester/Outputs/apinotes-diags.txt index b9db649487959..7fc6938e7ffe3 100644 --- a/test/api-digester/Outputs/apinotes-diags.txt +++ b/test/api-digester/Outputs/apinotes-diags.txt @@ -15,7 +15,6 @@ APINotesTest(APINotesTest.h): TypeAlias CatAttributeName has been removed APINotesTest(APINotesTest.h): Protocol SwiftTypeWithMethodLeft has been renamed to Protocol SwiftTypeWithMethodRight APINotesTest(APINotesTest.h): Var OldType.oldMember has been renamed to Var NewType.newMember APINotesTest(APINotesTest.h): Var globalAttributeName has been renamed to Var AnimalAttributeName.globalAttributeName -APINotesTest: Import Foundation has been renamed to Import objc_generics /* Type Changes */ APINotesTest(APINotesTest.h): Constructor Cat.init(name:) has return type change from APINotesTest.Cat to APINotesTest.Cat? From 09b06a8cd36c2c8be6c2308eeccf30fdeb300e8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 5 Jan 2023 20:48:57 -0800 Subject: [PATCH 88/98] Revert "[Serialization] Enable deserialization safety by default" This reverts commit af70cc546413da12d85a654187ac5b6bde901be0. # Conflicts: # test/Serialization/Safety/unsafe-decls.swift --- include/swift/Basic/LangOptions.h | 2 +- test/Serialization/Safety/override-internal-func.swift | 6 ++++-- .../Safety/skip-reading-internal-anyobject.swift | 3 ++- .../Safety/skip-reading-internal-details.swift | 3 ++- test/Serialization/Safety/unsafe-decls.swift | 5 ++++- test/Serialization/Safety/unsafe-extensions.swift | 5 ++++- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index f4a5909454842..c80f20209f500 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -366,7 +366,7 @@ namespace swift { /// Enable early skipping deserialization of decls that are marked as /// unsafe to read. - bool EnableDeserializationSafety = true; + bool EnableDeserializationSafety = false; /// Whether to enable the new operator decl and precedencegroup lookup /// behavior. This is a staging flag, and will be removed in the future. diff --git a/test/Serialization/Safety/override-internal-func.swift b/test/Serialization/Safety/override-internal-func.swift index 756cdba913156..5b0306e923815 100644 --- a/test/Serialization/Safety/override-internal-func.swift +++ b/test/Serialization/Safety/override-internal-func.swift @@ -10,11 +10,13 @@ // RUN: -emit-module-interface-path %t/Lib.swiftinterface /// Build against the swiftmodule. -// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -enable-deserialization-safety /// Build against the swiftinterface. // RUN: rm %t/Lib.swiftmodule -// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -enable-deserialization-safety //--- Lib.swift diff --git a/test/Serialization/Safety/skip-reading-internal-anyobject.swift b/test/Serialization/Safety/skip-reading-internal-anyobject.swift index 49e665e3cb94e..9de5cdb905ecf 100644 --- a/test/Serialization/Safety/skip-reading-internal-anyobject.swift +++ b/test/Serialization/Safety/skip-reading-internal-anyobject.swift @@ -11,7 +11,8 @@ /// Build client. // RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ -// RUN: -verify -Xllvm -debug-only=Serialization 2>&1 \ +// RUN: -verify -Xllvm -debug-only=Serialization \ +// RUN: -enable-deserialization-safety 2>&1 \ // RUN: | %FileCheck --check-prefixes=SAFE %s /// Decls skips by the deserialization safety logic. diff --git a/test/Serialization/Safety/skip-reading-internal-details.swift b/test/Serialization/Safety/skip-reading-internal-details.swift index 283815c33ba32..9e3229eb9114a 100644 --- a/test/Serialization/Safety/skip-reading-internal-details.swift +++ b/test/Serialization/Safety/skip-reading-internal-details.swift @@ -19,7 +19,8 @@ // RUN: | %FileCheck --check-prefixes=NEEDED,UNSAFE %s // RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ -// RUN: -verify -Xllvm -debug-only=Serialization 2>&1 \ +// RUN: -verify -Xllvm -debug-only=Serialization \ +// RUN: -enable-deserialization-safety 2>&1 \ // RUN: | %FileCheck --check-prefixes=NEEDED,CLEAN,SAFE %s /// Build against the swiftinterface. diff --git a/test/Serialization/Safety/unsafe-decls.swift b/test/Serialization/Safety/unsafe-decls.swift index 3bc82deb8df3a..4af0b4ad7a74f 100644 --- a/test/Serialization/Safety/unsafe-decls.swift +++ b/test/Serialization/Safety/unsafe-decls.swift @@ -4,11 +4,13 @@ // RUN: %target-swift-frontend -emit-module %s \ // RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -enable-deserialization-safety \ // RUN: -Xllvm -debug-only=Serialization 2>&1 | %swift-demangle --simplified \ // RUN: | %FileCheck --check-prefixes=SAFETY-PRIVATE,SAFETY-INTERNAL %s // RUN: %target-swift-frontend -emit-module %s \ // RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -enable-deserialization-safety \ // RUN: -Xllvm -debug-only=Serialization \ // RUN: -enable-testing 2>&1 \ // RUN: | %FileCheck --check-prefixes=DISABLED %s @@ -16,13 +18,14 @@ /// Don't mark decls as unsafe when private import is enabled. // RUN: %target-swift-frontend -emit-module %s \ // RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -enable-deserialization-safety \ // RUN: -Xllvm -debug-only=Serialization \ // RUN: -enable-private-imports 2>&1 \ // RUN: | %FileCheck --check-prefixes=DISABLED %s /// Don't mark decls as unsafe without library evolution. // RUN: %target-swift-frontend -emit-module %s \ -// RUN: -swift-version 5 \ +// RUN: -enable-deserialization-safety -swift-version 5 \ // RUN: -Xllvm -debug-only=Serialization 2>&1 \ // RUN: | %FileCheck --check-prefixes=DISABLED %s diff --git a/test/Serialization/Safety/unsafe-extensions.swift b/test/Serialization/Safety/unsafe-extensions.swift index b4d4e588572ef..63d432d584952 100644 --- a/test/Serialization/Safety/unsafe-extensions.swift +++ b/test/Serialization/Safety/unsafe-extensions.swift @@ -4,11 +4,13 @@ // RUN: %target-swift-frontend -emit-module %s \ // RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -enable-deserialization-safety \ // RUN: -Xllvm -debug-only=Serialization 2>&1 \ // RUN: | %FileCheck --check-prefixes=SAFETY-PRIVATE,SAFETY-INTERNAL %s // RUN: %target-swift-frontend -emit-module %s \ // RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -enable-deserialization-safety \ // RUN: -Xllvm -debug-only=Serialization \ // RUN: -enable-testing 2>&1 \ // RUN: | %FileCheck --check-prefixes=DISABLED %s @@ -16,13 +18,14 @@ /// Don't mark decls as unsafe when private import is enabled. // RUN: %target-swift-frontend -emit-module %s \ // RUN: -enable-library-evolution -swift-version 5 \ +// RUN: -enable-deserialization-safety \ // RUN: -Xllvm -debug-only=Serialization \ // RUN: -enable-private-imports 2>&1 \ // RUN: | %FileCheck --check-prefixes=DISABLED %s /// Don't mark decls as unsafe without library evolution. // RUN: %target-swift-frontend -emit-module %s \ -// RUN: -swift-version 5 \ +// RUN: -enable-deserialization-safety -swift-version 5 \ // RUN: -Xllvm -debug-only=Serialization 2>&1 \ // RUN: | %FileCheck --check-prefixes=DISABLED %s From 049d7856f43aebb44d1c2ecfe2cf762b1b8cc79f Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Thu, 9 Feb 2023 13:11:53 -0800 Subject: [PATCH 89/98] Add visitAccessPathBaseUses API For the move-only checker --- include/swift/SIL/MemAccessUtils.h | 6 +++ lib/SIL/Utils/MemAccessUtils.cpp | 39 ++++++++++++++- .../UtilityPasses/UnitTestRunner.cpp | 40 +++++++++++++++ test/SILOptimizer/accesspath_unit.sil | 50 +++++++++++++++++++ 4 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 test/SILOptimizer/accesspath_unit.sil diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index 4d5373c1115fb..bece253a35f64 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -1397,6 +1397,12 @@ bool visitAccessStorageUses(AccessUseVisitor &visitor, AccessStorage storage, bool visitAccessPathUses(AccessUseVisitor &visitor, AccessPath accessPath, SILFunction *function); +/// Similar to visitAccessPathUses, but the visitor is restricted to a specific +/// access base, such as a particular ref_element_addr. +bool visitAccessPathBaseUses(AccessUseVisitor &visitor, + AccessPathWithBase accessPathWithBase, + SILFunction *function); + } // end namespace swift //===----------------------------------------------------------------------===// diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 29a29e2fd73f4..609681151aecb 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -1534,6 +1534,16 @@ class AccessPathDefUseTraversal { // The origin of the def-use traversal. AccessStorage storage; + // The base of the formal access. For class storage, it is the + // ref_element_addr. For global storage it is the global_addr or initializer + // apply. For other storage, it is the same as accessPath.getRoot(). + // + // 'base' is typically invalid, maning that all uses of 'storage' for the + // access path will be visited. When 'base' is set, the the visitor is + // restricted to a specific access base, such as a particular + // ref_element_addr. + SILValue base; + // Indices of the path to match from inner to outer component. // A cursor is used to represent the most recently visited def. // During def-use traversal, the cursor starts at the end of pathIndices and @@ -1581,6 +1591,20 @@ class AccessPathDefUseTraversal { }); } + AccessPathDefUseTraversal(AccessUseVisitor &visitor, + AccessPathWithBase accessPathWithBase, + SILFunction *function) + : visitor(visitor), storage(accessPathWithBase.accessPath.getStorage()), + base(accessPathWithBase.getAccessBase().getBaseAddress()) { + + auto accessPath = accessPathWithBase.accessPath; + assert(accessPath.isValid()); + + initializePathIndices(accessPath); + + initializeDFS(base); + } + // Return true is all uses have been visited. bool visitUses() { // Return false if initialization failed. @@ -1601,8 +1625,8 @@ class AccessPathDefUseTraversal { if (phi->isPhi()) visitedPhis.insert(phi); } - pushUsers(root, - DFSEntry(nullptr, storage.isReference(), pathIndices.size(), 0)); + bool isRef = !base && storage.isReference(); + pushUsers(root, DFSEntry(nullptr, isRef, pathIndices.size(), 0)); } void pushUsers(SILValue def, const DFSEntry &dfs) { @@ -1654,6 +1678,10 @@ void AccessPathDefUseTraversal::initializePathIndices(AccessPath accessPath) { if (int offset = accessPath.getOffset()) { pathIndices.push_back(AccessPath::Index::forOffset(offset)); } + // If traversal starts at the base address, then class storage is irrelevant. + if (base) + return; + // The search will start from the object root, not the formal access base, // so add the class index to the front. if (storage.getKind() == AccessStorage::Class) { @@ -1966,6 +1994,13 @@ bool swift::visitAccessPathUses(AccessUseVisitor &visitor, return AccessPathDefUseTraversal(visitor, accessPath, function).visitUses(); } +bool swift::visitAccessPathBaseUses(AccessUseVisitor &visitor, + AccessPathWithBase accessPathWithBase, + SILFunction *function) { + return AccessPathDefUseTraversal(visitor, accessPathWithBase, function) + .visitUses(); +} + bool swift::visitAccessStorageUses(AccessUseVisitor &visitor, AccessStorage storage, SILFunction *function) { diff --git a/lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp b/lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp index fe87362a4b9cb..b3e3fcf5304ce 100644 --- a/lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp +++ b/lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp @@ -68,6 +68,7 @@ #include "swift/AST/Type.h" #include "swift/Basic/TaggedUnion.h" +#include "swift/SIL/MemAccessUtils.h" #include "swift/SIL/PrunedLiveness.h" #include "swift/SIL/SILArgumentArrayRef.h" #include "swift/SIL/SILBasicBlock.h" @@ -621,6 +622,44 @@ struct SimplifyCFGTryJumpThreading : UnitTest { } }; +//===----------------------------------------------------------------------===// +// MARK: AccessPath Unit Tests +//===----------------------------------------------------------------------===// + +struct AccessUseTestVisitor : public AccessUseVisitor { + AccessUseTestVisitor() + : AccessUseVisitor(AccessUseType::Overlapping, + NestedAccessType::IgnoreAccessBegin) {} + + bool visitUse(Operand *op, AccessUseType useTy) override { + switch (useTy) { + case AccessUseType::Exact: + llvm::errs() << "Exact Use: "; + break; + case AccessUseType::Inner: + llvm::errs() << "Inner Use: "; + break; + case AccessUseType::Overlapping: + llvm::errs() << "Overlapping Use "; + break; + } + llvm::errs() << *op->getUser(); + return true; + } +}; + +struct AccessPathBaseTest : UnitTest { + AccessPathBaseTest(UnitTestRunner *pass) : UnitTest(pass) {} + void invoke(Arguments &arguments) override { + auto value = arguments.takeValue(); + getFunction()->dump(); + llvm::outs() << "Access path base: " << value; + auto accessPathWithBase = AccessPathWithBase::compute(value); + AccessUseTestVisitor visitor; + visitAccessPathBaseUses(visitor, accessPathWithBase, getFunction()); + } +}; + /// [new_tests] Add the new UnitTest subclass above this line. //===----------------------------------------------------------------------===// @@ -637,6 +676,7 @@ void UnitTestRunner::withTest(StringRef name, Doit doit) { } // Alphabetical mapping from string to unit test subclass. + ADD_UNIT_TEST_SUBCLASS("accesspath-base", AccessPathBaseTest) ADD_UNIT_TEST_SUBCLASS("canonicalize-ossa-lifetime", CanonicalizeOSSALifetimeTest) ADD_UNIT_TEST_SUBCLASS("canonicalize-borrow-scope", CanonicalizeBorrowScopeTest) diff --git a/test/SILOptimizer/accesspath_unit.sil b/test/SILOptimizer/accesspath_unit.sil new file mode 100644 index 0000000000000..35644a830e164 --- /dev/null +++ b/test/SILOptimizer/accesspath_unit.sil @@ -0,0 +1,50 @@ +// RUN: %target-sil-opt -unit-test-runner %s -o /dev/null 2>&1 | %FileCheck %s + +sil_stage raw + +import Builtin + +import Swift + +struct S {} + +class Klass { + var f: S +} + +// CHECK-LABEL: begin running test 1 of 2 on testRefElement: accesspath-base with: @trace[0] +// CHECK: [[P1:%.*]] = ref_element_addr %0 : $Klass, #Klass.f +// CHECK: [[A1:%.*]] = begin_access [read] [dynamic] [[P1]] : $*S +// CHECK: [[P2:%.*]] = ref_element_addr %0 : $Klass, #Klass.f +// CHECK: [[A2:%.*]] = begin_access [read] [dynamic] [[P2]] : $*S +// CHECK: Access path base: [[P1]] = ref_element_addr %0 : $Klass, #Klass.f +// CHECK-NEXT: Exact Use: %{{.*}} = load [trivial] [[A1]] : $*S +// CHECK-NEXT: Exact Use: end_access [[A1]] : $*S +// CHECK-LABEL: end running test 1 of 2 on testRefElement: accesspath-base with: @trace[0] + +// CHECK-LABEL: begin running test 2 of 2 on testRefElement: accesspath-base with: @trace[1] +// CHECK: [[P1:%.*]] = ref_element_addr %0 : $Klass, #Klass.f +// CHECK: [[A1:%.*]] = begin_access [read] [dynamic] [[P1]] : $*S +// CHECK: [[P2:%.*]] = ref_element_addr %0 : $Klass, #Klass.f +// CHECK: [[A2:%.*]] = begin_access [read] [dynamic] [[P2]] : $*S +// CHECK: Access path base: [[P2]] = ref_element_addr %0 : $Klass, #Klass.f +// CHECK-NEXT: Exact Use: %{{.*}} = load [trivial] [[A2]] : $*S +// CHECK-NEXT: Exact Use: end_access [[A2]] : $*S +// CHECK-LABEL: end running test 2 of 2 on testRefElement: accesspath-base with: @trace[1] +sil hidden [ossa] @testRefElement : $@convention(thin) (@guaranteed Klass) -> () { +bb0(%0 : @guaranteed $Klass): + test_specification "accesspath-base @trace[0]" + %p1 = ref_element_addr %0 : $Klass, #Klass.f + debug_value [trace] %p1 : $*S + %a1 = begin_access [read] [dynamic] %p1 : $*S + %l1 = load [trivial] %a1 : $*S + end_access %a1 : $*S + test_specification "accesspath-base @trace[1]" + %p2 = ref_element_addr %0 : $Klass, #Klass.f + debug_value [trace] %p2 : $*S + %a2 = begin_access [read] [dynamic] %p2 : $*S + %l2 = load [trivial] %a2 : $*S + end_access %a2 : $*S + %99 = tuple () + return %99 : $() +} From 62a646974f73073e8b3528179888f447e700b9aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20Laferri=C3=A8re?= Date: Thu, 9 Feb 2023 17:26:36 -0800 Subject: [PATCH 90/98] [Serialization] Move deserialization safety behind an env var or flag --- include/swift/Basic/LangOptions.h | 3 ++- .../Safety/skip-reading-internal-details.swift | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index c80f20209f500..5b004d6bcbdf0 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -366,7 +366,8 @@ namespace swift { /// Enable early skipping deserialization of decls that are marked as /// unsafe to read. - bool EnableDeserializationSafety = false; + bool EnableDeserializationSafety = + ::getenv("SWIFT_ENABLE_DESERIALIZATION_SAFETY"); /// Whether to enable the new operator decl and precedencegroup lookup /// behavior. This is a staging flag, and will be removed in the future. diff --git a/test/Serialization/Safety/skip-reading-internal-details.swift b/test/Serialization/Safety/skip-reading-internal-details.swift index 9e3229eb9114a..5b7cd4c7eb843 100644 --- a/test/Serialization/Safety/skip-reading-internal-details.swift +++ b/test/Serialization/Safety/skip-reading-internal-details.swift @@ -23,6 +23,18 @@ // RUN: -enable-deserialization-safety 2>&1 \ // RUN: | %FileCheck --check-prefixes=NEEDED,CLEAN,SAFE %s +/// Disabled by default. +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -verify -Xllvm -debug-only=Serialization \ +// RUN: -disable-deserialization-safety 2>&1 \ +// RUN: | %FileCheck --check-prefixes=NEEDED,UNSAFE %s + +/// Enable with env var. +// RUN: env SWIFT_ENABLE_DESERIALIZATION_SAFETY=true \ +// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ +// RUN: -verify -Xllvm -debug-only=Serialization 2>&1 \ +// RUN: | %FileCheck --check-prefixes=NEEDED,CLEAN,SAFE %s + /// Build against the swiftinterface. // RUN: rm %t/Lib.swiftmodule // RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \ From b7007cb74892fda503d28047ddfc434165ac76df Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Thu, 9 Feb 2023 17:03:43 -0800 Subject: [PATCH 91/98] [interop][SwiftToCxx] dispatch Swift class methods correctly using the vtable --- include/swift/IRGen/IRABIDetailsProvider.h | 34 +++++ lib/IRGen/IRABIDetailsProvider.cpp | 41 ++++++ lib/IRGen/MetadataLayout.h | 8 ++ lib/PrintAsClang/DeclAndTypePrinter.cpp | 11 +- lib/PrintAsClang/PrintClangFunction.cpp | 39 ++++-- lib/PrintAsClang/PrintClangFunction.h | 26 ++-- ...lass-virtual-method-dispatch-execution.cpp | 94 ++++++++++++++ .../swift-class-virtual-method-dispatch.swift | 121 ++++++++++++++++++ 8 files changed, 351 insertions(+), 23 deletions(-) create mode 100644 test/Interop/SwiftToCxx/class/swift-class-virtual-method-dispatch-execution.cpp create mode 100644 test/Interop/SwiftToCxx/class/swift-class-virtual-method-dispatch.swift diff --git a/include/swift/IRGen/IRABIDetailsProvider.h b/include/swift/IRGen/IRABIDetailsProvider.h index 6531964baced2..e94039a04f25b 100644 --- a/include/swift/IRGen/IRABIDetailsProvider.h +++ b/include/swift/IRGen/IRABIDetailsProvider.h @@ -248,6 +248,40 @@ class IRABIDetailsProvider { llvm::MapVector getEnumTagMapping(const EnumDecl *ED); + /// Details how a specific method should be dispatched. + struct MethodDispatchInfo { + enum class Kind { + /// A direct call can be made to the underlying function. + Direct, + /// An indirect call that can be made via a static offset in a vtable. + IndirectVTableStaticOffset + }; + + static MethodDispatchInfo direct() { + return MethodDispatchInfo(Kind::Direct, 0); + } + + static MethodDispatchInfo indirectVTableStaticOffset(size_t bitOffset) { + return MethodDispatchInfo(Kind::IndirectVTableStaticOffset, bitOffset); + } + + Kind getKind() const { return kind; } + size_t getStaticBitOffset() const { + assert(kind == Kind::IndirectVTableStaticOffset); + return bitOffset; + } + + private: + constexpr MethodDispatchInfo(Kind kind, size_t bitOffset) + : kind(kind), bitOffset(bitOffset) {} + + Kind kind; + size_t bitOffset; + }; + + Optional + getMethodDispatchInfo(const AbstractFunctionDecl *funcDecl); + private: std::unique_ptr impl; }; diff --git a/lib/IRGen/IRABIDetailsProvider.cpp b/lib/IRGen/IRABIDetailsProvider.cpp index 7784a80828b86..1907e56c3d39c 100644 --- a/lib/IRGen/IRABIDetailsProvider.cpp +++ b/lib/IRGen/IRABIDetailsProvider.cpp @@ -18,6 +18,7 @@ #include "GenericRequirement.h" #include "IRGen.h" #include "IRGenModule.h" +#include "MetadataLayout.h" #include "NativeConventionSchema.h" // FIXME: This include should removed once getFunctionLoweredSignature() is @@ -202,6 +203,40 @@ class IRABIDetailsProviderImpl { return result; } + using MethodDispatchInfo = IRABIDetailsProvider::MethodDispatchInfo; + + Optional + getMethodDispatchInfo(const AbstractFunctionDecl *funcDecl) { + if (funcDecl->isSemanticallyFinal()) + return MethodDispatchInfo::direct(); + // If this is an override of an existing method, then lookup + // its base method in its base class. + if (auto *overridenDecl = funcDecl->getOverriddenDecl()) + funcDecl = overridenDecl; + auto *parentClass = dyn_cast(funcDecl->getDeclContext()); + if (!parentClass) + return MethodDispatchInfo::direct(); + auto &layout = IGM.getMetadataLayout(parentClass); + if (!isa(layout)) + return {}; + auto &classLayout = cast(layout); + auto *mi = classLayout.getStoredMethodInfoIfPresent( + SILDeclRef(const_cast(funcDecl))); + if (!mi) + return {}; + switch (mi->TheKind) { + case ClassMetadataLayout::MethodInfo::Kind::DirectImpl: + return MethodDispatchInfo::direct(); + case ClassMetadataLayout::MethodInfo::Kind::Offset: + if (mi->TheOffset.isStatic()) { + return MethodDispatchInfo::indirectVTableStaticOffset( + /*bitOffset=*/mi->TheOffset.getStaticOffset().getValue()); + } + return {}; + } + llvm_unreachable("invalid kind"); + } + Lowering::TypeConverter typeConverter; // Default silOptions are sufficient, as we don't need to generated SIL. SILOptions silOpts; @@ -405,3 +440,9 @@ llvm::MapVector IRABIDetailsProvider::getEnumTagMapping(const EnumDecl *ED) { return impl->getEnumTagMapping(ED); } + +Optional +IRABIDetailsProvider::getMethodDispatchInfo( + const AbstractFunctionDecl *funcDecl) { + return impl->getMethodDispatchInfo(funcDecl); +} diff --git a/lib/IRGen/MetadataLayout.h b/lib/IRGen/MetadataLayout.h index c31a56f5c3e6a..9b61b1f04ab83 100644 --- a/lib/IRGen/MetadataLayout.h +++ b/lib/IRGen/MetadataLayout.h @@ -273,6 +273,14 @@ class ClassMetadataLayout : public NominalMetadataLayout { MethodInfo getMethodInfo(IRGenFunction &IGF, SILDeclRef method) const; + const StoredMethodInfo * + getStoredMethodInfoIfPresent(SILDeclRef method) const { + auto it = MethodInfos.find(method); + if (it != MethodInfos.end()) + return &it->second; + return nullptr; + } + Offset getFieldOffset(IRGenFunction &IGF, VarDecl *field) const; /// Assuming that the given field offset is at a static offset in diff --git a/lib/PrintAsClang/DeclAndTypePrinter.cpp b/lib/PrintAsClang/DeclAndTypePrinter.cpp index 7cfe848387f89..1ccb1254fb842 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.cpp +++ b/lib/PrintAsClang/DeclAndTypePrinter.cpp @@ -995,6 +995,13 @@ class DeclAndTypePrinter::Implementation /*selfTypeDeclContext=*/typeDeclContext); if (!funcABI) return; + Optional dispatchInfo; + if (!isa(AFD)) { + dispatchInfo = owningPrinter.interopContext.getIrABIDetails() + .getMethodDispatchInfo(AFD); + if (!dispatchInfo) + return; + } owningPrinter.prologueOS << cFuncPrologueOS.str(); printDocumentationComment(AFD); @@ -1018,7 +1025,7 @@ class DeclAndTypePrinter::Implementation funcABI->getSignature(), funcABI->getSymbolName(), resultTy, /*isStatic=*/isClassMethod, - /*isDefinition=*/false); + /*isDefinition=*/false, dispatchInfo); } DeclAndTypeClangFunctionPrinter defPrinter( @@ -1041,7 +1048,7 @@ class DeclAndTypePrinter::Implementation defPrinter.printCxxMethod(typeDeclContext, AFD, funcABI->getSignature(), funcABI->getSymbolName(), resultTy, /*isStatic=*/isClassMethod, - /*isDefinition=*/true); + /*isDefinition=*/true, dispatchInfo); } // FIXME: SWIFT_WARN_UNUSED_RESULT diff --git a/lib/PrintAsClang/PrintClangFunction.cpp b/lib/PrintAsClang/PrintClangFunction.cpp index 1c25059f8438d..f9d83a567a405 100644 --- a/lib/PrintAsClang/PrintClangFunction.cpp +++ b/lib/PrintAsClang/PrintClangFunction.cpp @@ -1084,7 +1084,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody( const AbstractFunctionDecl *FD, const LoweredFunctionSignature &signature, StringRef swiftSymbolName, const NominalTypeDecl *typeDeclContext, const ModuleDecl *moduleContext, Type resultTy, const ParameterList *params, - bool hasThrows, const AnyFunctionType *funcType, bool isStaticMethod) { + bool hasThrows, const AnyFunctionType *funcType, bool isStaticMethod, + Optional dispatchInfo) { if (typeDeclContext) ClangSyntaxPrinter(os).printNominalTypeOutsideMemberDeclInnerStaticAssert( typeDeclContext); @@ -1096,9 +1097,29 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody( os << " void* opaqueError = nullptr;\n"; os << " void* _ctx = nullptr;\n"; } + Optional indirectFunctionVar; + if (dispatchInfo && + dispatchInfo->getKind() != + IRABIDetailsProvider::MethodDispatchInfo::Kind::Direct) { + assert(dispatchInfo->getKind() == IRABIDetailsProvider::MethodDispatchInfo:: + Kind::IndirectVTableStaticOffset); + auto vtableBitOffset = dispatchInfo->getStaticBitOffset(); + + os << "void ***selfPtr_ = reinterpret_cast( " + "::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));\n"; + os << "void **vtable_ = *selfPtr_;\n"; + os << "using FType = decltype(" << cxx_synthesis::getCxxImplNamespaceName() + << "::" << swiftSymbolName << ");\n"; + os << "FType *fptr_ = reinterpret_cast(*(vtable_ + " + << (vtableBitOffset / 8) << "));\n"; // FIXME: not 8 + indirectFunctionVar = StringRef("fptr_"); + } auto printCallToCFunc = [&](Optional additionalParam) { - os << cxx_synthesis::getCxxImplNamespaceName() << "::" << swiftSymbolName - << '('; + if (indirectFunctionVar) + os << "(* " << *indirectFunctionVar << ')'; + else + os << cxx_synthesis::getCxxImplNamespaceName() << "::" << swiftSymbolName; + os << '('; bool needsComma = false; size_t paramIndex = 1; @@ -1329,7 +1350,8 @@ static StringRef getConstructorName(const AbstractFunctionDecl *FD) { void DeclAndTypeClangFunctionPrinter::printCxxMethod( const NominalTypeDecl *typeDeclContext, const AbstractFunctionDecl *FD, const LoweredFunctionSignature &signature, StringRef swiftSymbolName, - Type resultTy, bool isStatic, bool isDefinition) { + Type resultTy, bool isStatic, bool isDefinition, + Optional dispatchInfo) { bool isConstructor = isa(FD); os << " "; @@ -1357,10 +1379,11 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod( os << " {\n"; // FIXME: should it be objTy for resultTy? - printCxxThunkBody( - FD, signature, swiftSymbolName, typeDeclContext, FD->getModuleContext(), - resultTy, FD->getParameters(), FD->hasThrows(), - FD->getInterfaceType()->castTo(), isStatic); + printCxxThunkBody(FD, signature, swiftSymbolName, typeDeclContext, + FD->getModuleContext(), resultTy, FD->getParameters(), + FD->hasThrows(), + FD->getInterfaceType()->castTo(), isStatic, + dispatchInfo); os << " }\n"; } diff --git a/lib/PrintAsClang/PrintClangFunction.h b/lib/PrintAsClang/PrintClangFunction.h index 4ff67283c412c..e788144af5b2e 100644 --- a/lib/PrintAsClang/PrintClangFunction.h +++ b/lib/PrintAsClang/PrintClangFunction.h @@ -18,6 +18,7 @@ #include "swift/Basic/LLVM.h" #include "swift/ClangImporter/ClangImporter.h" #include "swift/IRGen/GenericRequirement.h" +#include "swift/IRGen/IRABIDetailsProvider.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/Optional.h" @@ -107,22 +108,21 @@ class DeclAndTypeClangFunctionPrinter { /// Print the body of the inline C++ function thunk that calls the underlying /// Swift function. - void printCxxThunkBody(const AbstractFunctionDecl *FD, - const LoweredFunctionSignature &signature, - StringRef swiftSymbolName, - const NominalTypeDecl *typeDeclContext, - const ModuleDecl *moduleContext, Type resultTy, - const ParameterList *params, bool hasThrows = false, - const AnyFunctionType *funcType = nullptr, - bool isStaticMethod = false); + void printCxxThunkBody( + const AbstractFunctionDecl *FD, const LoweredFunctionSignature &signature, + StringRef swiftSymbolName, const NominalTypeDecl *typeDeclContext, + const ModuleDecl *moduleContext, Type resultTy, + const ParameterList *params, bool hasThrows = false, + const AnyFunctionType *funcType = nullptr, bool isStaticMethod = false, + Optional dispatchInfo = None); /// Print the Swift method as C++ method declaration/definition, including /// constructors. - void printCxxMethod(const NominalTypeDecl *typeDeclContext, - const AbstractFunctionDecl *FD, - const LoweredFunctionSignature &signature, - StringRef swiftSymbolName, Type resultTy, bool isStatic, - bool isDefinition); + void printCxxMethod( + const NominalTypeDecl *typeDeclContext, const AbstractFunctionDecl *FD, + const LoweredFunctionSignature &signature, StringRef swiftSymbolName, + Type resultTy, bool isStatic, bool isDefinition, + Optional dispatchInfo); /// Print the C++ getter/setter method signature. void printCxxPropertyAccessorMethod(const NominalTypeDecl *typeDeclContext, diff --git a/test/Interop/SwiftToCxx/class/swift-class-virtual-method-dispatch-execution.cpp b/test/Interop/SwiftToCxx/class/swift-class-virtual-method-dispatch-execution.cpp new file mode 100644 index 0000000000000..bde3fe8bcfc3f --- /dev/null +++ b/test/Interop/SwiftToCxx/class/swift-class-virtual-method-dispatch-execution.cpp @@ -0,0 +1,94 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-frontend %S/swift-class-virtual-method-dispatch.swift -typecheck -module-name Class -clang-header-expose-decls=all-public -emit-clang-header-path %t/class.h + +// RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-class-execution.o +// RUN: %target-interop-build-swift %S/swift-class-virtual-method-dispatch.swift -o %t/swift-class-execution -Xlinker %t/swift-class-execution.o -module-name Class -Xfrontend -entry-point-function-name -Xfrontend swiftMain + +// RUN: %target-codesign %t/swift-class-execution +// RUN: %target-run %t/swift-class-execution | %FileCheck %s + +// FIXME: pointer signing support. +// UNSUPPORTED: CPU=arm64e + +#include "class.h" +#include +#include + +using namespace Class; + +int main() { + auto base = returnBaseClass(); + auto derived = returnDerivedClass(); + BaseClass derivedAsBase = derived; + auto derivedDerived = returnDerivedDerivedClass(); + BaseClass derivedDerivedAsBase = derivedDerived; + DerivedClass derivedDerivedAsDerived = derivedDerived; + + { + base.virtualMethod(); +// CHECK: BaseClass.virtualMethod + puts("after base invoke"); + derived.virtualMethod(); + derivedAsBase.virtualMethod(); +// CHECK-NEXT: after base invoke +// CHECK-NEXT: DerivedClass.virtualMethod +// CHECK-NEXT: DerivedClass.virtualMethod + puts("after derived invoke"); + derivedDerived.virtualMethod(); + derivedDerivedAsBase.virtualMethod(); +// CHECK-NEXT: after derived invoke +// CHECK-NEXT: DerivedDerivedClass.virtualMethod +// CHECK-NEXT: DerivedDerivedClass.virtualMethod + } + + { + swift::Int x; + x = base.virtualMethodIntInt(5); + assert(x == 5); + + x = derived.virtualMethodIntInt(5); + assert(x == -5); + x = derivedAsBase.virtualMethodIntInt(-13); + assert(x == 13); + + x = derivedDerived.virtualMethodIntInt(789); + assert(x == -789); + x = derivedDerivedAsBase.virtualMethodIntInt(76); + assert(x == -76); + } + + { + swift::Int x; + x = base.finalMethodInBase(5); + assert(x == 10); + + x = derived.finalMethodInBase(10); + assert(x == 20); + x = derivedAsBase.finalMethodInBase(30); + assert(x == 60); + + x = derivedDerived.finalMethodInBase(11); + assert(x == 22); + x = derivedDerivedAsBase.finalMethodInBase(-22); + assert(x == -44); + } + + { + auto obj = derived.virtualMethodInDerived(base); + obj.virtualMethod(); +// CHECK-NEXT: BaseClass.virtualMethod + obj = derivedDerived.virtualMethodInDerived(base); + obj.virtualMethod(); +// CHECK-NEXT: DerivedDerivedClass.virtualMethod + obj = derivedDerivedAsDerived.virtualMethodInDerived(base); + obj.virtualMethod(); +// CHECK-NEXT: DerivedDerivedClass.virtualMethod + } + + { + derivedDerived.methodInDerivedDerived(); +// CHECK-NEXT: DerivedDerivedClass.methodInDerivedDerived + } + return 0; +} diff --git a/test/Interop/SwiftToCxx/class/swift-class-virtual-method-dispatch.swift b/test/Interop/SwiftToCxx/class/swift-class-virtual-method-dispatch.swift new file mode 100644 index 0000000000000..589684adcb949 --- /dev/null +++ b/test/Interop/SwiftToCxx/class/swift-class-virtual-method-dispatch.swift @@ -0,0 +1,121 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend %s -typecheck -module-name Class -clang-header-expose-decls=all-public -emit-clang-header-path %t/class.h +// RUN: %FileCheck %s < %t/class.h + +// RUN: %check-interop-cxx-header-in-clang(%t/class.h) + +public class BaseClass { + var field: Int64 + + init() { + field = 0 + } + public func virtualMethod() { + print("BaseClass.virtualMethod") + } + public func virtualMethodIntInt(_ x: Int) -> Int { + return x + } + public final func finalMethodInBase(_ x: Int) -> Int { + return x * 2 + } +} + +public class DerivedClass: BaseClass { + override init() { + super.init() + } + override public func virtualMethod() { + print("DerivedClass.virtualMethod") + } + override public func virtualMethodIntInt(_ x: Int) -> Int { + return -x + } + public func virtualMethodInDerived(_ x: BaseClass) -> BaseClass { + return x + } +} + +public final class DerivedDerivedClass: DerivedClass { + override init() { + super.init() + } + override public func virtualMethod() { + print("DerivedDerivedClass.virtualMethod") + } + override public func virtualMethodInDerived(_ x: BaseClass) -> BaseClass { + return self + } + public func methodInDerivedDerived() { + print("DerivedDerivedClass.methodInDerivedDerived") + } +} + +public func returnBaseClass() -> BaseClass { + return BaseClass() +} + +public func returnDerivedClass() -> DerivedClass { + return DerivedClass() +} + +public func returnDerivedDerivedClass() -> DerivedDerivedClass { + return DerivedDerivedClass() +} + +// CHECK: void BaseClass::virtualMethod() { +// CHECK-NEXT: void ***selfPtr_ = reinterpret_cast( ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)); +// CHECK-NEXT: void **vtable_ = *selfPtr_; +// CHECK-NEXT: using FType = decltype(_impl::$s5Class04BaseA0C13virtualMethodyyF); +// CHECK-NEXT: FType *fptr_ = reinterpret_cast(*(vtable_ + [[#VM1:]])); +// CHECK-NEXT: return (* fptr_)(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)); +// CHECK-NEXT: } + +// CHECK: swift::Int BaseClass::virtualMethodIntInt(swift::Int x) { +// CHECK-NEXT: void ***selfPtr_ = reinterpret_cast( ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)); +// CHECK-NEXT: void **vtable_ = *selfPtr_; +// CHECK-NEXT: using FType = decltype(_impl::$s5Class04BaseA0C016virtualMethodIntE0yS2iF); +// CHECK-NEXT: FType *fptr_ = reinterpret_cast(*(vtable_ + [[#VM1 + 1]])); +// CHECK-NEXT: return (* fptr_)(x, ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)); +// CHECK-NEXT: } + +// CHECK: swift::Int BaseClass::finalMethodInBase(swift::Int x) { +// CHECK-NEXT: return _impl::$s5Class04BaseA0C013finalMethodInB0yS2iF(x, ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)); +// CHECK-NEXT: } + +// CHECK: void DerivedClass::virtualMethod() { +// CHECK-NEXT: void ***selfPtr_ = reinterpret_cast( ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)); +// CHECK-NEXT: void **vtable_ = *selfPtr_; +// CHECK-NEXT: using FType = decltype(_impl::$s5Class07DerivedA0C13virtualMethodyyF); +// CHECK-NEXT: FType *fptr_ = reinterpret_cast(*(vtable_ + [[#VM1]])); +// CHECK-NEXT: return (* fptr_)(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)); +// CHECK-NEXT: } + +// CHECK: swift::Int DerivedClass::virtualMethodIntInt(swift::Int x) { +// CHECK-NEXT: void ***selfPtr_ = reinterpret_cast( ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)); +// CHECK-NEXT: void **vtable_ = *selfPtr_; +// CHECK-NEXT: using FType = decltype(_impl::$s5Class07DerivedA0C016virtualMethodIntE0yS2iF); +// CHECK-NEXT: FType *fptr_ = reinterpret_cast(*(vtable_ + [[#VM1 + 1]])); +// CHECK-NEXT: return (* fptr_)(x, ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)); +// CHECK-NEXT: } + +// CHECK: BaseClass DerivedClass::virtualMethodInDerived(const BaseClass& x) { +// CHECK-NEXT: void ***selfPtr_ = reinterpret_cast( ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)); +// CHECK-NEXT: void **vtable_ = *selfPtr_; +// CHECK-NEXT: using FType = decltype(_impl::$s5Class07DerivedA0C015virtualMethodInB0yAA04BaseA0CAFF); +// CHECK-NEXT: FType *fptr_ = reinterpret_cast(*(vtable_ + [[#VM1 + 2]])); +// CHECK-NEXT: return _impl::_impl_BaseClass::makeRetained((* fptr_)(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(x), ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this))); +// CHECK-NEXT: } + +// CHECK: void DerivedDerivedClass::virtualMethod() { +// CHECK-NEXT: return _impl::$s5Class07DerivedbA0C13virtualMethodyyF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)); +// CHECK-NEXT: } + +// CHECK: BaseClass DerivedDerivedClass::virtualMethodInDerived(const BaseClass& x) { +// CHECK-NEXT: return _impl::_impl_BaseClass::makeRetained(_impl::$s5Class07DerivedbA0C015virtualMethodInB0yAA04BaseA0CAFF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(x), ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this))); +// CHECK-NEXT: } + +// CHECK: void DerivedDerivedClass::methodInDerivedDerived() { +// CHECK-NEXT: return _impl::$s5Class07DerivedbA0C08methodInbB0yyF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)); +// CHECK-NEXT: } + From eb69afcf5dfd0f1ca3f603c360358b0f056c3c2b Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Thu, 9 Feb 2023 21:53:35 -0800 Subject: [PATCH 92/98] [SIL] Fix printing of type erased layout constraints (#63561) When type erased, the constraint should be printed as the layout name (e.g. `_Class` and not `AnyObject`). --- lib/SIL/IR/SILPrinter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 0e481fb3aa8e6..6e1d04d6813a3 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -4096,7 +4096,9 @@ void SILSpecializeAttr::print(llvm::raw_ostream &OS) const { } else { Requirement ReqWithDecls(req.getKind(), FirstTy, req.getLayoutConstraint()); - ReqWithDecls.print(OS, SubPrinter); + auto SubPrinterCopy = SubPrinter; + SubPrinterCopy.PrintClassLayoutName = erased; + ReqWithDecls.print(OS, SubPrinterCopy); } }, [&] { OS << ", "; }); From 04eca73d60d6f2aa4859b267e068498f46baca8b Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Thu, 9 Feb 2023 21:59:45 -0800 Subject: [PATCH 93/98] [Macros] Stop parsing the ": " syntax. We always require parameters --- include/swift/AST/Decl.h | 9 ++-- lib/AST/Decl.cpp | 6 ++- lib/IDE/CompletionLookup.cpp | 2 +- lib/Parse/ParseDecl.cpp | 48 ++++++++------------- test/IDE/complete_pound_expr_macros.swift | 4 +- test/Macros/macro_availability_macosx.swift | 8 ++-- test/Macros/macros_diagnostics.swift | 16 +++---- test/Macros/parsing.swift | 16 +++---- test/ModuleInterface/macros.swift | 8 ++-- 9 files changed, 54 insertions(+), 63 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 491739cdaa31d..1dbde95e2a3fc 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -8358,12 +8358,11 @@ class MacroDecl : public GenericContext, public ValueDecl { /// The location of the 'macro' keyword. SourceLoc macroLoc; - /// The parameter list for a function-like macro. + /// The parameter list. ParameterList *parameterList; - /// Where the '->' or ':' is located, for a function- or value-like macro, - /// respectively. - SourceLoc arrowOrColonLoc; + /// Where the '->' is located, if present. + SourceLoc arrowLoc; /// The result type. TypeLoc resultType; @@ -8375,7 +8374,7 @@ class MacroDecl : public GenericContext, public ValueDecl { MacroDecl(SourceLoc macroLoc, DeclName name, SourceLoc nameLoc, GenericParamList *genericParams, ParameterList *parameterList, - SourceLoc arrowOrColonLoc, + SourceLoc arrowLoc, TypeRepr *resultType, Expr *definition, DeclContext *parent); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index b34dcae7e1dc4..8ac8f9fd8fdad 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -9820,14 +9820,14 @@ MacroDecl::MacroDecl( SourceLoc macroLoc, DeclName name, SourceLoc nameLoc, GenericParamList *genericParams, ParameterList *parameterList, - SourceLoc arrowOrColonLoc, + SourceLoc arrowLoc, TypeRepr *resultType, Expr *definition, DeclContext *parent ) : GenericContext(DeclContextKind::MacroDecl, parent, genericParams), ValueDecl(DeclKind::Macro, parent, name, nameLoc), macroLoc(macroLoc), parameterList(parameterList), - arrowOrColonLoc(arrowOrColonLoc), + arrowLoc(arrowLoc), resultType(resultType), definition(definition) { @@ -9849,6 +9849,8 @@ SourceRange MacroDecl::getSourceRange() const { SourceLoc endLoc = getNameLoc(); if (parameterList) endLoc = parameterList->getEndLoc(); + if (resultType.getSourceRange().isValid()) + endLoc = resultType.getSourceRange().End; if (definition) endLoc = definition->getEndLoc(); if (auto trailing = getTrailingWhereClause()) diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index 94193214f3ac3..a5220f92b677d 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -1793,7 +1793,7 @@ void CompletionLookup::addMacroExpansion(const MacroDecl *MD, addValueBaseName(Builder, MD->getBaseIdentifier()); Type macroType = MD->getInterfaceType(); - if (MD->parameterList && macroType->is()) { + if (MD->parameterList && MD->parameterList->size() > 0) { Builder.addLeftParen(); addCallArgumentPatterns(Builder, macroType->castTo(), MD->parameterList, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 1c0d2d2fc9343..a255b3289a609 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -9487,40 +9487,30 @@ ParserResult Parser::parseDeclMacro(DeclAttributes &attributes) { // Parse the macro signature. ParameterList *parameterList = nullptr; - SourceLoc arrowOrColonLoc; + SourceLoc arrowLoc; TypeRepr *resultType = nullptr; DeclName macroFullName; - if (consumeIf(tok::colon, arrowOrColonLoc)) { - // Value-like macros. - auto type = parseType(diag::expected_macro_value_type); - status |= type; + + // Parameter list. + SmallVector namePieces; + auto parameterResult = parseSingleParameterClause( + ParameterContextKind::Macro, &namePieces, nullptr); + status |= parameterResult; + parameterList = parameterResult.getPtrOrNull(); + + // -> + if (consumeIf(tok::arrow, arrowLoc)) { + // Result type. + auto parsedResultType = + parseDeclResultType(diag::expected_type_macro_result); + resultType = parsedResultType.getPtrOrNull(); + status |= parsedResultType; if (status.isErrorOrHasCompletion()) return status; - - resultType = type.getPtrOrNull(); - macroFullName = macroName; - } else { - // Parameter list. - SmallVector namePieces; - auto parameterResult = parseSingleParameterClause( - ParameterContextKind::Macro, &namePieces, nullptr); - status |= parameterResult; - parameterList = parameterResult.getPtrOrNull(); - - // -> - if (consumeIf(tok::arrow, arrowOrColonLoc)) { - // Result type. - auto parsedResultType = - parseDeclResultType(diag::expected_type_macro_result); - resultType = parsedResultType.getPtrOrNull(); - status |= parsedResultType; - if (status.isErrorOrHasCompletion()) - return status; - } - - macroFullName = DeclName(Context, macroName, namePieces); } + macroFullName = DeclName(Context, macroName, namePieces); + // Parse '=' Expr *definition = nullptr; if (consumeIf(tok::equal)) { @@ -9534,7 +9524,7 @@ ParserResult Parser::parseDeclMacro(DeclAttributes &attributes) { // Create the macro declaration. auto *macro = new (Context) MacroDecl( macroLoc, macroFullName, macroNameLoc, genericParams, parameterList, - arrowOrColonLoc, resultType, definition, CurDeclContext); + arrowLoc, resultType, definition, CurDeclContext); macro->getAttrs() = attributes; // Parse a 'where' clause if present. diff --git a/test/IDE/complete_pound_expr_macros.swift b/test/IDE/complete_pound_expr_macros.swift index 902120bfde1f9..690534d46cbae 100644 --- a/test/IDE/complete_pound_expr_macros.swift +++ b/test/IDE/complete_pound_expr_macros.swift @@ -5,8 +5,8 @@ import ObjectiveC -macro myLine: Int = A.B -macro myFilename: T = A.B +macro myLine() -> Int = A.B +macro myFilename() -> T = A.B macro myStringify(_: T) -> (T, String) = A.B struct Color { } diff --git a/test/Macros/macro_availability_macosx.swift b/test/Macros/macro_availability_macosx.swift index 080e5ee5fc87e..7aac86aa401d7 100644 --- a/test/Macros/macro_availability_macosx.swift +++ b/test/Macros/macro_availability_macosx.swift @@ -4,9 +4,9 @@ @available(macOS 12.0, *) struct X { } -@freestanding(expression) macro m1: X = #externalMacro(module: "A", type: "B") // expected-error{{'X' is only available in macOS 12.0 or newer}} -// expected-warning@-1{{external macro implementation type 'A.B' could not be found for macro 'm1'}} +@freestanding(expression) macro m1() -> X = #externalMacro(module: "A", type: "B") // expected-error{{'X' is only available in macOS 12.0 or newer}} +// expected-warning@-1{{external macro implementation type 'A.B' could not be found for macro 'm1()'}} @available(macOS 12.0, *) -@freestanding(expression) macro m2: X = #externalMacro(module: "A", type: "B") -// expected-warning@-1{{external macro implementation type 'A.B' could not be found for macro 'm2'}} +@freestanding(expression) macro m2() -> X = #externalMacro(module: "A", type: "B") +// expected-warning@-1{{external macro implementation type 'A.B' could not be found for macro 'm2()'}} diff --git a/test/Macros/macros_diagnostics.swift b/test/Macros/macros_diagnostics.swift index a399b6c56fd4a..081b70c00aaf5 100644 --- a/test/Macros/macros_diagnostics.swift +++ b/test/Macros/macros_diagnostics.swift @@ -18,18 +18,18 @@ protocol P { } internal struct X { } // expected-note{{type declared here}} -@freestanding(expression) public macro createAnX: X = #externalMacro(module: "BuiltinMacros", type: "Blah") +@freestanding(expression) public macro createAnX() -> X = #externalMacro(module: "BuiltinMacros", type: "Blah") // expected-error@-1{{macro cannot be declared public because its result type uses an internal type}} // expected-warning@-2{{external macro implementation type}} -@freestanding(expression) macro m1: Int = #externalMacro(module: "BuiltinMacros", type: "Blah") +@freestanding(expression) macro m1() -> Int = #externalMacro(module: "BuiltinMacros", type: "Blah") // expected-warning@-1{{external macro implementation type}} -@freestanding(expression) macro m1: Float = #externalMacro(module: "BuiltinMacros", type: "Blah") +@freestanding(expression) macro m1() -> Float = #externalMacro(module: "BuiltinMacros", type: "Blah") // expected-warning@-1{{external macro implementation type}} -@freestanding(expression) macro m2: Int = #externalMacro(module: "BuiltinMacros", type: "Blah") // expected-note{{'m2' previously declared here}} +@freestanding(expression) macro m2() -> Int = #externalMacro(module: "BuiltinMacros", type: "Blah") // expected-note{{'m2()' previously declared here}} // expected-warning@-1{{external macro implementation type}} -@freestanding(expression) macro m2: Int = #externalMacro(module: "BuiltinMacros", type: "Blah") // expected-error{{invalid redeclaration of 'm2'}} +@freestanding(expression) macro m2() -> Int = #externalMacro(module: "BuiltinMacros", type: "Blah") // expected-error{{invalid redeclaration of 'm2()'}} // expected-warning@-1{{external macro implementation type}} @freestanding(expression) macro m3(_: Int) -> Int = #externalMacro(module: "BuiltinMacros", type: "Blah") @@ -43,9 +43,9 @@ internal struct X { } // expected-note{{type declared here}} // expected-warning@-1{{external macro implementation type}} struct ZZZ { - macro m5: Int = #externalMacro(module: "BuiltinMacros", type: "Blah") - // expected-error@-1{{macro 'm5' can only be declared at file scope}} - // expected-error@-2{{macro 'm5' must declare its applicable roles}} + macro m5() -> Int = #externalMacro(module: "BuiltinMacros", type: "Blah") + // expected-error@-1{{macro 'm5()' can only be declared at file scope}} + // expected-error@-2{{macro 'm5()' must declare its applicable roles}} // expected-warning@-3{{external macro implementation type}} } diff --git a/test/Macros/parsing.swift b/test/Macros/parsing.swift index f03cd9d5a4dae..131efb2847669 100644 --- a/test/Macros/parsing.swift +++ b/test/Macros/parsing.swift @@ -2,14 +2,14 @@ protocol P { } protocol Q { associatedtype Assoc } -@freestanding(expression) macro m1: Int = #externalMacro(module: "A", type: "M1") -// expected-warning@-1{{external macro implementation type 'A.M1' could not be found for macro 'm1'; the type must be public and provided via '-load-plugin-library'}} +@freestanding(expression) macro m1() -> Int = #externalMacro(module: "A", type: "M1") +// expected-warning@-1{{external macro implementation type 'A.M1' could not be found for macro 'm1()'; the type must be public and provided via '-load-plugin-library'}} @freestanding(expression) macro m2(_: Int) = #externalMacro(module: "A", type: "M2") // expected-warning@-1{{external macro implementation type 'A.M2' could not be found for macro 'm2'; the type must be public and provided via '-load-plugin-library'}} @freestanding(expression) macro m3(a b: Int) -> Int = #externalMacro(module: "A", type: "M3") // expected-warning@-1{{external macro implementation type 'A.M3' could not be found for macro 'm3(a:)'; the type must be public and provided via '-load-plugin-library'}} -@freestanding(expression) macro m4: T = #externalMacro(module: "A", type: "M4") where T.Assoc: P -// expected-warning@-1{{external macro implementation type 'A.M4' could not be found for macro 'm4'; the type must be public and provided via '-load-plugin-library'}} +@freestanding(expression) macro m4() -> T = #externalMacro(module: "A", type: "M4") where T.Assoc: P +// expected-warning@-1{{external macro implementation type 'A.M4' could not be found for macro 'm4()'; the type must be public and provided via '-load-plugin-library'}} @freestanding(expression) macro m5(_: T) = #externalMacro(module: "A", type: "M4") // expected-warning@-1{{external macro implementation type 'A.M4' could not be found for macro 'm5'; the type must be public and provided via '-load-plugin-library'}} @@ -36,8 +36,8 @@ macro m10(_: String) = #externalMacro(module: "A", type: "M4") accessor, names: overloaded, arbitrary, named(hello), prefixed(_), suffixed(_favorite) ) -macro am1: Void -// expected-error@-1{{macro 'am1' requires a definition}} +macro am1() +// expected-error@-1{{macro 'am1()' requires a definition}} @attached( accessor, @@ -46,5 +46,5 @@ macro am1: Void named, // expected-error{{introduced name kind 'named' requires a single argument '(name)'}} arbitrary(a) // expected-error{{introduced name kind 'arbitrary' must not have an argument}} ) -macro am2: Void -// expected-error@-1{{macro 'am2' requires a definition}} +macro am2() -> Void +// expected-error@-1{{macro 'am2()' requires a definition}} diff --git a/test/ModuleInterface/macros.swift b/test/ModuleInterface/macros.swift index 3f7bc525281fc..fecd9be7c5916 100644 --- a/test/ModuleInterface/macros.swift +++ b/test/ModuleInterface/macros.swift @@ -12,14 +12,14 @@ @freestanding(expression) public macro publicStringify(_ value: T) -> (T, String) = #externalMacro(module: "SomeModule", type: "StringifyMacro") // CHECK: #if compiler(>=5.3) && $FreestandingExpressionMacros && $Macros -// CHECK: @freestanding(expression) public macro publicLine: T = #externalMacro(module: "SomeModule", type: "Line") where T : Swift.ExpressibleByIntegerLiteral +// CHECK: @freestanding(expression) public macro publicLine() -> T = #externalMacro(module: "SomeModule", type: "Line") where T : Swift.ExpressibleByIntegerLiteral // CHECK-NEXT: #endif -@freestanding(expression) public macro publicLine: T = #externalMacro(module: "SomeModule", type: "Line") +@freestanding(expression) public macro publicLine() -> T = #externalMacro(module: "SomeModule", type: "Line") // CHECK: #if compiler(>=5.3) && $Macros -// CHECK: @attached(accessor) public macro myWrapper: Swift.Void = #externalMacro(module: "SomeModule", type: "Wrapper") +// CHECK: @attached(accessor) public macro myWrapper() -> () = #externalMacro(module: "SomeModule", type: "Wrapper") // CHECK-NEXT: #endif -@attached(accessor) public macro myWrapper: Void = #externalMacro(module: "SomeModule", type: "Wrapper") +@attached(accessor) public macro myWrapper() = #externalMacro(module: "SomeModule", type: "Wrapper") // CHECK-NOT: internalStringify @freestanding(expression) macro internalStringify(_ value: T) -> (T, String) = #externalMacro(module: "SomeModule", type: "StringifyMacro") From 4edc24189b3a17d779a7718b9134efe4c5785d28 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 10 Feb 2023 09:29:17 -0800 Subject: [PATCH 94/98] Fix Lock and mark a few tests unsupported --- .../Sources/_Runtime/Utils/Lock.swift | 44 +++++++------------ .../Sources/_Runtime/Utils/PointerStuff.swift | 2 +- .../Sources/_Runtime/Utils/TypeCache.swift | 4 +- .../ContextDescriptor/StructDescriptor.swift | 2 + .../_Runtime/Metadata/FunctionMetadata.swift | 2 + test/stdlib/_Runtime/Metadata/Metadata.swift | 2 + .../_Runtime/Metadata/MetatypeMetadata.swift | 2 + .../_Runtime/Metadata/StructMetadata.swift | 2 + .../_Runtime/Metadata/TupleMetadata.swift | 2 + .../_Runtime/Metadata/TypeMetadata.swift | 2 + .../_Runtime/Metadata/ValueWitnessTable.swift | 2 + 11 files changed, 35 insertions(+), 31 deletions(-) diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/Lock.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/Lock.swift index 12294705ef8f0..9ecfb855202a9 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/Lock.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/Lock.swift @@ -23,41 +23,29 @@ func _lockLock(_: UnsafeRawPointer) @_silgen_name("_swift_reflection_lock_unlock") func _lockUnlock(_: UnsafeRawPointer) -class Lock { - var value: T +struct Lock { + var storage: ManagedBuffer - var mutex: UnsafeRawPointer { - UnsafeRawPointer( - Builtin.projectTailElems(self, UnsafeRawPointer.self) - ) - } - - init(_ x: T) { - value = x - } - - static func create(with initialValue: T) -> Lock { - let lock = Builtin.allocWithTailElems_1( - Lock.self, - _lockSize()._builtinWordValue, - UnsafeRawPointer.self - ) - - lock.value = initialValue + init(initialValue: T) { + storage = .create(minimumCapacity: _lockSize()) { + $0.withUnsafeMutablePointerToElements { + _lockInit(UnsafeRawPointer($0)) + } - _lockInit(lock.mutex) - - return lock + return initialValue + } } func withLock(_ body: @Sendable (inout T) throws -> U) rethrows -> U { - _lockLock(mutex) + try storage.withUnsafeMutablePointers { header, elements in + _lockLock(UnsafeRawPointer(elements)) - defer { - _lockUnlock(mutex) - } + defer { + _lockUnlock(UnsafeRawPointer(elements)) + } - return try body(&value) + return try body(&header.pointee) + } } } diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift index 3f1a2bdf501ba..a3f8043d32e29 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/PointerStuff.swift @@ -12,7 +12,7 @@ import Swift @_alwaysEmitIntoClient -func unsafeBitCast(_ x: T, to type: U.Type = U.self) -> U { +func unsafeBitCast(_ x: T, toDefault type: U.Type = U.self) -> U { Swift.unsafeBitCast(x, to: type) } diff --git a/stdlib/public/Reflection/Sources/_Runtime/Utils/TypeCache.swift b/stdlib/public/Reflection/Sources/_Runtime/Utils/TypeCache.swift index 77809bd4d2b77..c8a835e1ae1fd 100644 --- a/stdlib/public/Reflection/Sources/_Runtime/Utils/TypeCache.swift +++ b/stdlib/public/Reflection/Sources/_Runtime/Utils/TypeCache.swift @@ -17,7 +17,7 @@ struct TypeCache { @available(SwiftStdlib 5.9, *) init() { - cache = Lock.create(with: [:]) + cache = Lock<[Key: Any.Type?]>(initialValue: [:]) } } @@ -192,7 +192,7 @@ var typeCache: TypeCache = { var result = TypeCache() result.cache.withLock { - $0.reserveCapacity(50) + $0.reserveCapacity(25) } return result diff --git a/test/stdlib/_Runtime/ContextDescriptor/StructDescriptor.swift b/test/stdlib/_Runtime/ContextDescriptor/StructDescriptor.swift index 18fc668ab83de..731f603c18a82 100644 --- a/test/stdlib/_Runtime/ContextDescriptor/StructDescriptor.swift +++ b/test/stdlib/_Runtime/ContextDescriptor/StructDescriptor.swift @@ -1,6 +1,8 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +// UNSUPPORTED: freestanding + import StdlibUnittest import _Runtime diff --git a/test/stdlib/_Runtime/Metadata/FunctionMetadata.swift b/test/stdlib/_Runtime/Metadata/FunctionMetadata.swift index f0fcfda70fe83..eb87538ef04f5 100644 --- a/test/stdlib/_Runtime/Metadata/FunctionMetadata.swift +++ b/test/stdlib/_Runtime/Metadata/FunctionMetadata.swift @@ -1,6 +1,8 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +// UNSUPPORTED: freestanding + import StdlibUnittest import _Runtime diff --git a/test/stdlib/_Runtime/Metadata/Metadata.swift b/test/stdlib/_Runtime/Metadata/Metadata.swift index c40405d5730a2..b477d9a06865f 100644 --- a/test/stdlib/_Runtime/Metadata/Metadata.swift +++ b/test/stdlib/_Runtime/Metadata/Metadata.swift @@ -1,6 +1,8 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +// UNSUPPORTED: freestanding + import StdlibUnittest import _Runtime diff --git a/test/stdlib/_Runtime/Metadata/MetatypeMetadata.swift b/test/stdlib/_Runtime/Metadata/MetatypeMetadata.swift index c19efbbabedb6..b2566947c5872 100644 --- a/test/stdlib/_Runtime/Metadata/MetatypeMetadata.swift +++ b/test/stdlib/_Runtime/Metadata/MetatypeMetadata.swift @@ -1,6 +1,8 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +// UNSUPPORTED: freestanding + import StdlibUnittest import _Runtime diff --git a/test/stdlib/_Runtime/Metadata/StructMetadata.swift b/test/stdlib/_Runtime/Metadata/StructMetadata.swift index 6666e4431d75e..5ac4ba9a32d2b 100644 --- a/test/stdlib/_Runtime/Metadata/StructMetadata.swift +++ b/test/stdlib/_Runtime/Metadata/StructMetadata.swift @@ -1,6 +1,8 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +// UNSUPPORTED: freestanding + import StdlibUnittest import _Runtime diff --git a/test/stdlib/_Runtime/Metadata/TupleMetadata.swift b/test/stdlib/_Runtime/Metadata/TupleMetadata.swift index ba289d79698c3..8b866acf8f392 100644 --- a/test/stdlib/_Runtime/Metadata/TupleMetadata.swift +++ b/test/stdlib/_Runtime/Metadata/TupleMetadata.swift @@ -1,6 +1,8 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +// UNSUPPORTED: freestanding + import StdlibUnittest import _Runtime diff --git a/test/stdlib/_Runtime/Metadata/TypeMetadata.swift b/test/stdlib/_Runtime/Metadata/TypeMetadata.swift index c6f298db72a92..b8037165e3232 100644 --- a/test/stdlib/_Runtime/Metadata/TypeMetadata.swift +++ b/test/stdlib/_Runtime/Metadata/TypeMetadata.swift @@ -1,6 +1,8 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +// UNSUPPORTED: freestanding + import StdlibUnittest import _Runtime diff --git a/test/stdlib/_Runtime/Metadata/ValueWitnessTable.swift b/test/stdlib/_Runtime/Metadata/ValueWitnessTable.swift index de64660fb4c4a..46d3ee685b7b6 100644 --- a/test/stdlib/_Runtime/Metadata/ValueWitnessTable.swift +++ b/test/stdlib/_Runtime/Metadata/ValueWitnessTable.swift @@ -1,6 +1,8 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test +// UNSUPPORTED: freestanding + import StdlibUnittest import _Runtime From 13e1aa4467a48739c6fcf295ebcb2a5c38587332 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 6 Feb 2023 23:10:49 -0800 Subject: [PATCH 95/98] Add OwnershipLiveness utilities Encapsulate all the complexity of reborrows and guaranteed phi in 3 ownership liveness interfaces: LinerLiveness, InteriorLiveness, and ExtendedLiveness. --- include/swift/SIL/OwnershipLiveness.h | 299 +++++++ include/swift/SIL/OwnershipUseVisitor.h | 475 +++++++++++ include/swift/SIL/OwnershipUtils.h | 3 - .../SILOptimizer/Utils/OwnershipOptUtils.h | 4 + lib/SIL/Utils/CMakeLists.txt | 1 + lib/SIL/Utils/OwnershipLiveness.cpp | 357 +++++++++ .../UtilityPasses/UnitTestRunner.cpp | 75 ++ test/SILOptimizer/ownership_liveness_unit.sil | 737 ++++++++++++++++++ 8 files changed, 1948 insertions(+), 3 deletions(-) create mode 100644 include/swift/SIL/OwnershipLiveness.h create mode 100644 include/swift/SIL/OwnershipUseVisitor.h create mode 100644 lib/SIL/Utils/OwnershipLiveness.cpp create mode 100644 test/SILOptimizer/ownership_liveness_unit.sil diff --git a/include/swift/SIL/OwnershipLiveness.h b/include/swift/SIL/OwnershipLiveness.h new file mode 100644 index 0000000000000..95f50612c397b --- /dev/null +++ b/include/swift/SIL/OwnershipLiveness.h @@ -0,0 +1,299 @@ +//===--- OwnershipLiveness.h ---------------------------------*- C++ -*----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// +/// Terminology: +/// +/// Linear lifetime: All paths through the value's definition to the +/// function exit pass through exactly one lifetime-ending operation. +/// +/// Complete OSSA: All owned values and borrow introducers have linear lifetime. +/// +/// Borrow introducer: defines a guaranteed SILValue and an associated explicit +/// borrow scope: begin_borrow, load_borrow, store_borrow, and reborrow. A +/// "reborrow" is a guaranteed phi for which `isGuaranteedForwarding` returns +/// false. (Eventually, the phi will know whether it is a reborrow based on a +/// flag or subclass). Guaranteed function arguments do conceptually introduce +/// borrow scopes, but their scopes are implied by the function entry and +/// return points. +/// +/// Completing an OSSA lifetime is the process of giving an owned value or a +/// borrow introducer a linear lifetime. This process may require phi creation +/// whenever a nested scope involves a guaranteed phi that is not dominated by +/// the outer scope's definition. A new phi will be created that ends the outer +/// lifetime and begins a new lifetime in the successor block. This new phi will +/// either forward an owned value or reborrow the outer borrow scope. The new +/// phi's lifetime will then be recursively extended over the lifetime of the +/// original guaranteed phi, which we refer to as its inner adjacent phi. +/// +//===----------------------------------------------------------------------===// +/// +/// Linear SSA liveness: +/// +/// - Liveness for a single "defining" owned value or borrow introducing value +/// assuming the value's lifetime is already linear. +/// +/// - The definition dominates all use points (phis do not extend the lifetime) +/// +/// - Only lifetime-ending operations generate liveness +/// +/// - Oblivious to pointer escapes within the lifetime +/// +/// +/// Interior SSA liveness: +/// +/// - Liveness for a single "defining" value with any ownership. +/// +/// - The definition dominates all use points (phis do not extend the lifetime) +/// +/// - Does not assume the current lifetime is linear. Transitively follows +/// guaranteed forwarding and address uses within the current scope. +/// +/// - Liveness cannot extend beyond lifetime-ending operations +/// (a.k.a. affine lifetimes). +/// +/// - Assumes inner scopes *are* linear, including borrow and address scopes +/// (e.g. begin_borrow, load_borrow, begin_apply, store_borrow, begin_access) +/// A callback may be used to complete inner scopes before updating liveness. +/// +/// - Only returns AddressUseKind::PointerEscape if one of the uses of the +/// outer value has OperandOwnership::PointerEscape or +/// OperandOwnership::BitwiseEscape. An inner scope's guaranteed value may +/// escape without causing the outer scope's value to escape. +/// +/// - Insulates outer scopes from inner scope details. Maintains the +/// invariant that inlining cannot pessimize optimization. +/// +/// - Interior SSA liveness is used to complete (linearize) an OSSA lifetime +/// +/// Interior liveness example: +/// +/// %struct = struct ... +/// %f = struct_extract %s // defines a guaranteed value (%f) +/// %b = begin_borrow %field +/// %a = ref_element_addr %b +/// _ = address_to_pointer %a +/// end_borrow %b // the only interior use of %f +/// +/// When computing interior liveness for %f, %b is an inner scope. Because inner +/// scopes are complete, the only relevant use is end_borrow %b. Despite the +/// address_to_pointer instruction, %f does not escape any dependent address. +/// +/// Transitive SSA liveness +/// +/// - Similar to Interior SSA liveness, but does not assume that any lifetimes +/// are linear. Transitively follows uses within inner scopes, recursively +/// through nested scopes, including forwarding operations and address uses. +/// +/// - Much more likely to return AddressUseKind::PointerEscape +/// +/// Transitive liveness example: +/// +/// %struct = struct ... +/// %f = struct_extract %s // defines a guaranteed value (%f) +/// %b = begin_borrow %field +/// %a = ref_element_addr %b +/// _ = address_to_pointer %a // a transitive use of %f escapes +/// +/// When computing transitive liveness for %f, %b is an inner scope. Liveness +/// does not assume that an end_borrow exists. Instead it transitively considers +/// all uses of %b. As a result, %f escapes. +/// +/// - This header provides no interface for transitive liveness because it is no +/// longer needed with complete OSSA lifetimes +/// +/// Extended liveness +/// +/// - Liveness of a single "defining" value extended beyond its lifetime-ending +/// operations. +/// +/// - May refer to copy-extension, phi-extension, or extension over barriers +/// such as access markers. +/// +/// - For phi-extension, the definition no longer dominates the use +/// points. MultiDefPrunedLiveness must be used. Each phi is added as a new +/// definition. +/// +/// Extended linear liveness +/// +/// - Extended liveness that only considers lifetime-ending operations. Assumes +/// the definition already has a linear lifetime and that any phis that end +/// the current lifetime also have linear lifetimes. +/// +/// Extended interior liveness +/// +/// - Like interior SSA liveness, does not assume the current lifetime is +/// linear. Transitively follows guaranteed forwarding and address uses within +/// the current scope. Assumes inner scopes *are* linear. +/// +/// - Interior copy-extension is used to canonicalize an OSSA lifetime +/// +/// - This header provides no interface for extended interior liveness because +/// it is highly specific to a particular task. +/// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_SIL_OWNERSHIPLIVENESS_H +#define SWIFT_SIL_OWNERSHIPLIVENESS_H + +#include "swift/Basic/Debug.h" +#include "swift/Basic/LLVM.h" +#include "swift/SIL/PrunedLiveness.h" +#include "swift/SIL/OwnershipUseVisitor.h" +#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILBasicBlock.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILValue.h" +#include "llvm/ADT/SmallVector.h" + +namespace swift { + +/// Analyze SSA liveness of values that introduce an OSSA live range: +/// +/// 1. Owned non-phi values +/// 2. Owned phi values +/// 3. Borrow scope introducers: begin_borrow/load_borrow +/// 4. Reborrows: guaranteed phis that end their operands' borrow scopes and +/// require their own post-dominating end_borrows. +/// +/// Used for OSSA lifetime completion. +class OSSALiveness { +protected: + SILValue ownershipDef; + + // MARK: Results. + + SmallVector discoveredBlocks; + SSAPrunedLiveness liveness; + + OSSALiveness(const OSSALiveness &) = delete; + OSSALiveness &operator=(const OSSALiveness &) = delete; + +public: + OSSALiveness(SILValue def): ownershipDef(def), liveness(&discoveredBlocks) {} + + const SSAPrunedLiveness &getLiveness() const { return liveness; } + + ArrayRef getDiscoveredBlocks() const { + return discoveredBlocks; + } + + void print(llvm::raw_ostream &OS) const; + void dump() const; +}; + +// Internal implementation +struct LinearLivenessVisitor; + +/// Compute ownershipDef's lifetime based on it's lifetime-ending uses, assuming +/// it is already complete/linear. ownershipDef must be either an owned value or +/// a local borrow scope introduced (begin_borrow, load_borrow, or +/// store_borrow). +/// +/// This is the simplest OSSA liveness analysis, but is not appropriate for +/// fixing OSSA lifetimes after a transformation and cannot tell you whether +/// pointer escapes occur. +class LinearLiveness : public OSSALiveness { + friend LinearLivenessVisitor; + +public: + LinearLiveness(SILValue def); + + void compute(); +}; + +// Internal implementation +struct InteriorLivenessVisitor; + +/// Compute ownershipDef's lifetime based on all its uses (except those +/// already enclosed by inner scopes). Returns AddressUseKind::PointerEscape +/// if a pointer to ownershipDef escapes (and is not already enclosed by an +/// inner scope). +class InteriorLiveness : public OSSALiveness { + friend InteriorLivenessVisitor; + + // Handle inner scopes. Called for inner reborrows, inner adjacent reborrows, + // and address scopes. + // + // This may add uses to the inner scope, but it may not modify a use-list + // in any outer scopes. + using InnerScopeHandlerRef = llvm::function_ref; + +public: + // Summarize address uses + AddressUseKind addressUseKind = AddressUseKind::Unknown; + + // Record any guaranteed phi uses that are not already enclosed by an outer + // adjacent phi. + SmallVector unenclosedPhis; + +public: + InteriorLiveness(SILValue def): OSSALiveness(def) {} + + void compute(const DominanceInfo *domInfo, + InnerScopeHandlerRef handleInnerScope = InnerScopeHandlerRef()); + + AddressUseKind getAddressUseKind() const { return addressUseKind; } + + ArrayRef getUnenclosedPhis() const { return unenclosedPhis; } + + void print(llvm::raw_ostream &OS) const; + void dump() const; +}; + +// Internal implementation +struct ExtendedLinearLivenessVisitor; + +/// Analyze liveness of values that introduce an OSSA live range. This computes +/// the phi-extended live range for these four categories of live range +/// introducing values: +/// +/// 1. Owned non-phi values +/// 2. Owned phi values +/// 3. Borrow scope introducers: begin_borrow/load_borrow +/// 4. Reborrows: guaranteed phis that end their incoming borrow scopes and +/// begin a new borrow scope +class ExtendedLinearLiveness { + friend ExtendedLinearLivenessVisitor; + + SILValue ownershipDef; + + // MARK: Results. + + // Because of reborrows, the ssa def may not dominate all + // uses. Consider the reborrows to be separate defs. + SmallVector discoveredBlocks; + MultiDefPrunedLiveness liveness; + + ExtendedLinearLiveness(const ExtendedLinearLiveness &) = delete; + ExtendedLinearLiveness &operator=(const ExtendedLinearLiveness &) = delete; + +public: + ExtendedLinearLiveness(SILValue def); + + void compute(); + + /// The array of defs. The first element is ownershipDef. The remaining + /// elements are outer reborrows discovered during computation. + /// + /// TODO: These are always SILValues. Convert the iterator. + NodeSetVector::iterator defBegin() const { return liveness.defBegin(); } + NodeSetVector::iterator defEnd() const { return liveness.defBegin(); } + + const MultiDefPrunedLiveness &getLiveness() const { return liveness; } + + void print(llvm::raw_ostream &OS) const; + void dump() const; +}; + +} // namespace swift + +#endif diff --git a/include/swift/SIL/OwnershipUseVisitor.h b/include/swift/SIL/OwnershipUseVisitor.h new file mode 100644 index 0000000000000..856e88ae9f12a --- /dev/null +++ b/include/swift/SIL/OwnershipUseVisitor.h @@ -0,0 +1,475 @@ +//===--- OwnershipUseVisitor.h -------------------------------*- C++ -*----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +/// +/// A visitor that classifies the uses of an OSSA value. The main entry points +/// are: +/// +/// bool OwnershipUseVisitor::visitLifetimeEndingUses(SILValue ssaDef) +/// +/// bool OwnershipUseVisitor::visitInteriorUses(SILValue ssaDef) +/// +/// Extensions of the visitor determine how to handle pointer escapes, +/// reborrows, inner borrows, and scoped addresses. + +#ifndef SWIFT_SIL_OWNERSHIPUSEVISITOR_H +#define SWIFT_SIL_OWNERSHIPUSEVISITOR_H + +#include "swift/SIL/NodeBits.h" +#include "swift/SIL/OwnershipUtils.h" +#include "swift/SIL/ScopedAddressUtils.h" +#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILBasicBlock.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILValue.h" + +namespace swift { + +enum class OwnershipUseKind { LifetimeEnding, NonLifetimeEnding }; + +/// Impl provides: +/// +/// - NodeSet visited (if interior uses are visited) +/// +/// - 'bool handleUsePoint(Operand *use, UseLifetimeConstraint)' +/// +/// Where 'use->get()' is either the original SSA def, or the SILValue that +/// introduces an inner scope for which 'use' ends the scope. +/// +/// Impl overrides: +/// +/// - 'bool handlePointerEscape(Operand *use)' (default false) +/// +/// - bool handleOwnedPhi(Operand *phiOper) { return true; } +/// +/// - 'bool handleOuterReborrow(Operand *phiOper)' (default true) +/// +/// - 'bool handleGuaranteedForward(Operand *use)' (default true) +/// +/// - 'bool handleInnerBorrow(BorrowingOperand)' (default true) +/// +/// - 'bool handleInnerAdjacentReborrow(SILArgument *reborrow)' (default true) +/// +/// - 'bool handleInnerReborrow(Operand *use)' (default true) +/// +/// - 'bool handleScopedAddress(ScopedAddressValue)' (default true) +/// +/// These may be overridden with a transformation that adds uses to the use's +/// instruction or phi, but it may not modify the use-list containing \p +/// use or \p phiOper or in any outer scopes. +template +class OwnershipUseVisitorBase { +protected: + /// If this returns true, then handleUsePoint will be called as if the pointer + /// escape is an instantaneous use. The implementation may decide to track + /// AddressUseKind::PointerEscaping. Bail-out by default for safety. + bool handlePointerEscape(Operand *use) { + return false; + } + + /// Handle a owned forwarding phi of the original SSA def. Later, the phi + /// will itself be visited as a use but its scope's uses will not be + /// transitively visited. The implementation may track owned phis to + /// continue processing extended liveness. + /// + /// Return true to continue visiting other uses. + bool handleOwnedPhi(Operand *phiOper) { return true; } + + /// Handle a transitive reborrow of the original SSA def. Later, the reborrow + /// will itself be visited as a use but its scope's uses will not be + /// transitively visited. The implementation may track outer reborrows to + /// continue processing extended liveness. + /// + /// Return true to continue visiting other uses. + bool handleOuterReborrow(Operand *phiOper) { return true; } + + /// Handle a guaranteed forwarding instruction. After the returns, this will + /// itself be visited as a use, but its uses will not be transitively + /// visited. The implementation may transtitively follow uses or track + /// unenclosed guaranteed phis to insert missing reborrows. + /// + /// Return true to continue visiting other uses. + bool handleGuaranteedForwardingPhi(Operand *phiOper) { return true; } + + /// If this returns true, then handleUsePoint will be called on the scope + /// ending operands. + /// + /// Handles begin_borrow, load_borrow, store_borrow, begin_apply + /// + /// Allows the implementation to complete inner scopes before considering + /// their scope ending operations as uses of the outer scope. + bool handleInnerBorrow(BorrowingOperand borrowingOperand) { + return true; + } + + /// Handles an inner adjacent phi where ownershipDef is a phi in the same + /// block. + /// + /// If this returns true, then handleUsePoint will be called on the + /// reborrow's immediate scope ending uses are visited, along with + /// handleInnerBorrow for any uses that are also reborrows. + bool handleInnerAdjacentReborrow(SILArgument *reborrow) { + return true; + } + + /// Handle an inner reborrow. Later, the reborrow will itself be visited as a + /// use, but its scope's uses will not be transitively visited. The + /// implementation may track the reborrow to insert missing reborrows or to + /// continue processing extended liveness. + /// + /// Return true to continue visiting other uses. + bool handleInnerReborrow(Operand *phiOper) { return true; } + + /// If this returns true, then handleUsePoint will be called on the scope + /// ending operands. + /// + /// Allows the implementation to complete inner scopes before considering + /// their scope ending operations as uses of the outer scope. + /// + /// This may add uses to the inner scope, but it may not modify the use-list + /// containing \p scopedAddress or in any outer scopes. + bool handleScopedAddress(ScopedAddressValue scopedAddress) { + return true; + } +}; + +/// Visit uses relevant to liveness of a single OSSA value. +template +class OwnershipUseVisitor : OwnershipUseVisitorBase { +protected: + Impl &asImpl() { return static_cast(*this); } + + bool handleUsePoint(Operand *use, UseLifetimeConstraint useConstraint) { + asImpl().handleUsePoint(use, useConstraint); + return true; + } + +public: + /// Assumes that ssaDef's lifetime is complete (linear). + bool visitLifetimeEndingUses(SILValue ssaDef); + + /// Does not assume that ssaDef's lifetime is complete. + /// + /// If ssaDef is either an owned phi or reborrow, then find inner adjacent + /// phis and treat them just like inner borrow scopes. + bool visitInteriorUses(SILValue ssaDef); + +protected: + bool visitConsumes(SILValue ssaDef); + + bool visitOuterBorrow(SILValue borrowBegin); + + bool visitOuterBorrowScopeEnd(Operand *borrowEnd); + + bool visitInnerBorrow(Operand *borrowingOperand); + + bool visitInnerAdjacentReborrow(SILArgument *reborrow); + + bool visitInnerBorrowScopeEnd(Operand *borrowEnd); + + bool visitOwnedUse(Operand *use); + + bool visitGuaranteedUses(SILValue guaranteedValue) { + for (Operand *use : guaranteedValue->getUses()) { + if (!visitGuaranteedUse(use)) + return false; + } + return true; + } + + bool visitGuaranteedUse(Operand *use); + + bool visitInteriorPointerUses(Operand *use); +}; + +/// Recursively visit all lifetime-ending uses that contribute to the ownership +/// live range of \p ssaDef. This assumes that ssaDef has a complete +/// lifetime and ignores non-lifetime-ending uses. +template +bool OwnershipUseVisitor::visitLifetimeEndingUses(SILValue ssaDef) { + switch (ssaDef->getOwnershipKind()) { + case OwnershipKind::Owned: + return visitConsumes(ssaDef); + + case OwnershipKind::Guaranteed: + return visitOuterBorrow(ssaDef); + + case OwnershipKind::Any: + case OwnershipKind::None: + case OwnershipKind::Unowned: + llvm_unreachable("requires an owned or guaranteed orignalDef"); + } + return true; +} + +template +bool OwnershipUseVisitor::visitConsumes(SILValue ssaDef) { + for (Operand *use : ssaDef->getUses()) { + if (use->isConsuming()) { + if (PhiOperand(use) && !asImpl().handleOwnedPhi(use)) + return false; + + if (!handleUsePoint(use, UseLifetimeConstraint::LifetimeEnding)) + return false; + } + } + return true; +} + +template +bool OwnershipUseVisitor::visitOuterBorrow(SILValue borrowBegin) { + BorrowedValue borrow(borrowBegin); + assert(borrow && "guaranteed values have no lifetime ending uses"); + return borrow.visitLocalScopeEndingUses([this](Operand *borrowEnd) { + return visitOuterBorrowScopeEnd(borrowEnd); + }); +} + +template +bool OwnershipUseVisitor::visitOuterBorrowScopeEnd(Operand *borrowEnd) { + switch (borrowEnd->getOperandOwnership()) { + case OperandOwnership::EndBorrow: + return handleUsePoint(borrowEnd, UseLifetimeConstraint::LifetimeEnding); + + case OperandOwnership::Reborrow: + if (!asImpl().handleOuterReborrow(borrowEnd)) + return false; + + return handleUsePoint(borrowEnd, UseLifetimeConstraint::LifetimeEnding); + + default: + llvm_unreachable("expected borrow scope end"); + } +} + +/// Visit the lifetime-ending uses of borrow scope +/// (begin_borrow, load_borrow, or begin_apply). +template +bool OwnershipUseVisitor::visitInnerBorrow(Operand *borrowingOperand) { + if (!asImpl().handleInnerBorrow(borrowingOperand)) + return false; + + return BorrowingOperand(borrowingOperand) + .visitScopeEndingUses([&](Operand *borrowEnd) { + return visitInnerBorrowScopeEnd(borrowEnd); + }); +} + +template +bool OwnershipUseVisitor:: +visitInnerAdjacentReborrow(SILArgument *reborrow) { + if (!asImpl().handleInnerAdjacentReborrow(reborrow)) + return false; + + return + BorrowedValue(reborrow).visitLocalScopeEndingUses([&](Operand *borrowEnd) { + return visitInnerBorrowScopeEnd(borrowEnd); + }); +} + +/// Note: borrowEnd->get() may be a borrow introducer for an inner scope, or a +/// borrow scopes that does not introduce a borrowed value (begin_apply). +template +bool OwnershipUseVisitor::visitInnerBorrowScopeEnd(Operand *borrowEnd) { + switch (borrowEnd->getOperandOwnership()) { + case OperandOwnership::EndBorrow: + return handleUsePoint(borrowEnd, UseLifetimeConstraint::NonLifetimeEnding); + + case OperandOwnership::Reborrow: + if (!asImpl().handleInnerReborrow(borrowEnd)) + return false; + + return handleUsePoint(borrowEnd, UseLifetimeConstraint::NonLifetimeEnding); + + default: + llvm_unreachable("expected borrow scope end"); + } +} + +/// Recursively visit all uses that contribute to the ownership live range of \p +/// ssaDef. This does not assume that ssaDef has a complete lifetime +/// and visits non-lifetime-ending uses. +/// +/// If ssaDef is a phi (owned or reborrowed), then find its inner adjacent phis +/// and treat them like inner borrows. +template +bool OwnershipUseVisitor::visitInteriorUses(SILValue ssaDef) { + // Inner adjacent reborrows are considered inner borrow scopes. + if (auto phi = SILArgument::asPhi(ssaDef)) { + if (!visitInnerAdjacentPhis(phi, [&](SILArgument *innerPhi) { + // TODO: Remove this call to isGuaranteedForwarding. + // The phi itself should know if it is a reborrow. + if (isGuaranteedForwarding(innerPhi)) { + return visitGuaranteedUses(innerPhi); + } else { + return visitInnerAdjacentReborrow(innerPhi); + } + })) { + return false; + } + } + switch (ssaDef->getOwnershipKind()) { + case OwnershipKind::Owned: { + for (Operand *use : ssaDef->getUses()) { + if (!visitOwnedUse(use)) + return false; + } + return true; + } + case OwnershipKind::Guaranteed: { + return visitGuaranteedUses(ssaDef); + } + + case OwnershipKind::Any: + case OwnershipKind::None: + case OwnershipKind::Unowned: + llvm_unreachable("requires an owned or guaranteed orignalDef"); + } +} + +template +bool OwnershipUseVisitor::visitOwnedUse(Operand *use) { + switch (use->getOperandOwnership()) { + case OperandOwnership::NonUse: + return true; + + case OperandOwnership::ForwardingConsume: + case OperandOwnership::DestroyingConsume: + if (auto phiOper = PhiOperand(use)) { + if (!asImpl().handleOwnedPhi(use)) + return false; + } + return handleUsePoint(use, UseLifetimeConstraint::LifetimeEnding); + + case OperandOwnership::PointerEscape: + if (!asImpl().handlePointerEscape(use)) + return false; + + LLVM_FALLTHROUGH; + case OperandOwnership::InstantaneousUse: + case OperandOwnership::ForwardingUnowned: + case OperandOwnership::UnownedInstantaneousUse: + case OperandOwnership::BitwiseEscape: + return handleUsePoint(use, UseLifetimeConstraint::NonLifetimeEnding); + + case OperandOwnership::Borrow: + return visitInnerBorrow(use); + + // TODO: Eventually, handle owned InteriorPointers as implicit borrows. + case OperandOwnership::InteriorPointer: + case OperandOwnership::TrivialUse: + case OperandOwnership::EndBorrow: + case OperandOwnership::Reborrow: + case OperandOwnership::GuaranteedForwarding: + llvm_unreachable("ownership incompatible with an owned value"); + } +} + +template +bool OwnershipUseVisitor::visitGuaranteedUse(Operand *use) { + switch (use->getOperandOwnership()) { + case OperandOwnership::NonUse: + return true; + + case OperandOwnership::PointerEscape: + if (!asImpl().handlePointerEscape(use)) + return false; + + LLVM_FALLTHROUGH; + case OperandOwnership::InstantaneousUse: + case OperandOwnership::ForwardingUnowned: + case OperandOwnership::UnownedInstantaneousUse: + case OperandOwnership::BitwiseEscape: + case OperandOwnership::EndBorrow: + return handleUsePoint(use, UseLifetimeConstraint::NonLifetimeEnding); + + case OperandOwnership::Reborrow: + if (!asImpl().handleOuterReborrow(use)) + return false; + + return handleUsePoint(use, UseLifetimeConstraint::LifetimeEnding); + + case OperandOwnership::GuaranteedForwarding: + if (!handleUsePoint(use, UseLifetimeConstraint::NonLifetimeEnding)) + return false; + + if (PhiOperand(use)) { + return asImpl().handleGuaranteedForwardingPhi(use); + } + + if (!asImpl().visited.insert(use->getUser()->asSILNode())) + return true; + + return ForwardingOperand(use).visitForwardedValues([&](SILValue result) { + // Do not include transitive uses with 'none' ownership + if (result->getOwnershipKind() != OwnershipKind::None) { + return visitGuaranteedUses(result); + } + return true; + }); + + case OperandOwnership::Borrow: + return visitInnerBorrow(use); + + case OperandOwnership::InteriorPointer: + return visitInteriorPointerUses(use); + + case OperandOwnership::TrivialUse: + case OperandOwnership::ForwardingConsume: + case OperandOwnership::DestroyingConsume: + llvm_unreachable("ownership incompatible with a guaranteed value"); + } +} + +template +bool OwnershipUseVisitor::visitInteriorPointerUses(Operand *use) { + assert(use->getOperandOwnership() == OperandOwnership::InteriorPointer); + + if (auto scopedAddress = ScopedAddressValue::forUse(use)) { + // e.g. client may need to insert end_borrow if scopedAddress is a store_borrow. + if (!asImpl().handleScopedAddress(scopedAddress)) + return false; + + return scopedAddress.visitScopeEndingUses([this](Operand *end) { + return handleUsePoint(end, UseLifetimeConstraint::NonLifetimeEnding); + }); + } + handleUsePoint(use, UseLifetimeConstraint::NonLifetimeEnding); + + auto interiorPtrOp = InteriorPointerOperand(use); + if (interiorPtrOp.getProjectedAddress()->use_empty()) { + // findTransitiveUses reports PointerEscape for a dead interior address. But + // the use-point was already recorded above. So it's safe to simply return. + return true; + } + + // TODO: findTransitiveUses should be a visitor so we're not recursively + // allocating use vectors and potentially merging the use points. + // + // TODO: handleInnerBorrow needs to be called for any transitive load_borrow + // uses to ensure their lifetimes are complete. For now, findTransitiveUses + // just assumes that all scopes are incomplete. + SmallVector interiorUses; + auto useKind = InteriorPointerOperand(use).findTransitiveUses(&interiorUses); + if (useKind == AddressUseKind::PointerEscape) { + if (!asImpl().handlePointerEscape(use)) + return false; + } + for (auto *interiorUse : interiorUses) { + if (!handleUsePoint(interiorUse, UseLifetimeConstraint::NonLifetimeEnding)) + return false; + } + return true; +} + +} // namespace swift + +#endif diff --git a/include/swift/SIL/OwnershipUtils.h b/include/swift/SIL/OwnershipUtils.h index ed8aa8f7bad35..fdd9c9a1fcd07 100644 --- a/include/swift/SIL/OwnershipUtils.h +++ b/include/swift/SIL/OwnershipUtils.h @@ -275,9 +275,6 @@ class ForwardingOperand { SILValue getSingleForwardedValue() const; }; -/// Returns true if the instruction is a 'reborrow'. -bool isReborrowInstruction(const SILInstruction *inst); - class BorrowingOperandKind { public: enum Kind : uint8_t { diff --git a/include/swift/SILOptimizer/Utils/OwnershipOptUtils.h b/include/swift/SILOptimizer/Utils/OwnershipOptUtils.h index bceddf29711bf..9a09eb7a863b7 100644 --- a/include/swift/SILOptimizer/Utils/OwnershipOptUtils.h +++ b/include/swift/SILOptimizer/Utils/OwnershipOptUtils.h @@ -35,6 +35,10 @@ inline bool requiresOSSACleanup(SILValue v) { v->getOwnershipKind() != OwnershipKind::Unowned; } +//===----------------------------------------------------------------------===// +// Basic scope and lifetime extension API +//===----------------------------------------------------------------------===// + /// Rewrite the lifetime of \p ownedValue to match \p lifetimeBoundary. This may /// insert copies at forwarding consumes, including phis. /// diff --git a/lib/SIL/Utils/CMakeLists.txt b/lib/SIL/Utils/CMakeLists.txt index 41cb154b21418..7580bca4ef331 100644 --- a/lib/SIL/Utils/CMakeLists.txt +++ b/lib/SIL/Utils/CMakeLists.txt @@ -11,6 +11,7 @@ target_sources(swiftSIL PRIVATE MemAccessUtils.cpp MemoryLocations.cpp OptimizationRemark.cpp + OwnershipLiveness.cpp OwnershipUtils.cpp PrettyStackTrace.cpp Projection.cpp diff --git a/lib/SIL/Utils/OwnershipLiveness.cpp b/lib/SIL/Utils/OwnershipLiveness.cpp new file mode 100644 index 0000000000000..2c35b44501370 --- /dev/null +++ b/lib/SIL/Utils/OwnershipLiveness.cpp @@ -0,0 +1,357 @@ +//===--- OwnershipLiveness.cpp --------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/Basic/Debug.h" +#include "swift/Basic/Defer.h" +#include "swift/Basic/LLVM.h" +#include "swift/SIL/Dominance.h" +#include "swift/SIL/OwnershipLiveness.h" +#include "swift/SIL/PrunedLiveness.h" +#include "swift/SIL/SILArgument.h" +#include "swift/SIL/SILBasicBlock.h" +#include "swift/SIL/SILInstruction.h" +#include "swift/SIL/SILValue.h" +#include "llvm/ADT/SmallVector.h" + +namespace swift { + +void OSSALiveness::print(llvm::raw_ostream &OS) const { liveness.print(OS); } + +void OSSALiveness::dump() const { print(llvm::dbgs()); } + +struct LinearLivenessVisitor : + public OwnershipUseVisitor { + + LinearLiveness &linearLiveness; + + LinearLivenessVisitor(LinearLiveness &linearLiveness): + linearLiveness(linearLiveness){} + + bool handleUsePoint(Operand *use, UseLifetimeConstraint useConstraint) { + linearLiveness.liveness.updateForUse( + use->getUser(), useConstraint == UseLifetimeConstraint::LifetimeEnding); + return true; + } + + bool handlePointerEscape(Operand *use) { + llvm_unreachable("a pointer escape cannot end a linear lifetime"); + } + + // handleOwnedPhi and handleOuterReborrow ends the linear lifetime. + // By default, they are treated like a normal lifetime-ending use. + + bool handleGuaranteedForwardingPhi(Operand *use) { + llvm_unreachable("guaranteed forwarding phi cannot end a linear lifetime"); + } + + bool handleInnerBorrow(BorrowingOperand borrowingOperand) { + llvm_unreachable("an inner borrow cannot end a linear lifetime"); + } + + bool handleInnerAdjacentReborrow(SILArgument *reborrow) { + llvm_unreachable("inner adjacent reborrows are not visited"); + } + + bool handleInnerReborrow(BorrowingOperand borrowingOperand) { + llvm_unreachable("an inner borrow cannot end a linear lifetime"); + } + + bool handleScopedAddress(ScopedAddressValue scopedAddress) { + llvm_unreachable("an scoped address cannot end a linear lifetime"); + } +}; + +LinearLiveness::LinearLiveness(SILValue def): OSSALiveness(def) { + if (def->getOwnershipKind() != OwnershipKind::Owned) { + BorrowedValue borrowedValue(def); + assert(borrowedValue && borrowedValue.isLocalScope()); + (void)borrowedValue; + } +} + +void LinearLiveness::compute() { + liveness.initializeDef(ownershipDef); + LinearLivenessVisitor(*this).visitLifetimeEndingUses(ownershipDef); +} + +struct InteriorLivenessVisitor : + public OwnershipUseVisitor { + + InteriorLiveness &interiorLiveness; + const DominanceInfo *domInfo = nullptr; + + /// handleInnerScopeCallback may add uses to the inner scope, but it may not + /// modify the use-list containing \p borrowingOperand. This callback can be + /// used to ensure that the inner scope is complete before visiting its scope + /// ending operands. + /// + /// An inner scope encapsulates any pointer escapes so visiting its interior + /// uses is not necessary when visiting the outer scope's interior uses. + InteriorLiveness::InnerScopeHandlerRef handleInnerScopeCallback; + + // State local to an invocation of + // OwnershipUseVisitor::visitOwnershipUses(). + NodeSet visited; + + InteriorLivenessVisitor( + InteriorLiveness &interiorLiveness, + const DominanceInfo *domInfo, + InteriorLiveness::InnerScopeHandlerRef handleInnerScope) + : interiorLiveness(interiorLiveness), + domInfo(domInfo), + handleInnerScopeCallback(handleInnerScope), + visited(interiorLiveness.ownershipDef->getFunction()) {} + + bool handleUsePoint(Operand *use, UseLifetimeConstraint useConstraint) { + interiorLiveness.liveness.updateForUse( + use->getUser(), useConstraint == UseLifetimeConstraint::LifetimeEnding); + return true; + } + + bool handlePointerEscape(Operand *use) { + interiorLiveness.addressUseKind = AddressUseKind::PointerEscape; + return true; + } + + // handleOwnedPhi and handleOuterReborrow ends the linear lifetime. + // By default, they are treated like a normal lifetime-ending use. + + bool handleGuaranteedForwardingPhi(Operand *use) { + recursivelyVisitInnerGuaranteedPhi(PhiOperand(use), /*reborrow*/false); + return true; + } + + /// After this returns true, handleUsePoint will be called on the scope + /// ending operands. + /// + /// Handles begin_borrow, load_borrow, store_borrow, begin_apply. + bool handleInnerBorrow(BorrowingOperand borrowingOperand) { + if (handleInnerScopeCallback) { + handleInnerScopeCallback( + borrowingOperand.getBorrowIntroducingUserResult().value); + } + return true; + } + + bool handleInnerAdjacentReborrow(SILArgument *reborrow) { + if (handleInnerScopeCallback) { + handleInnerScopeCallback(reborrow); + } + return true; + } + + bool handleInnerReborrow(Operand *phiOper) { + recursivelyVisitInnerGuaranteedPhi(PhiOperand(phiOper), /*reborrow*/true); + return true; + } + + /// After this returns true, handleUsePoint will be called on the scope + /// ending operands. + /// + /// Handles store_borrow, begin_access. + bool handleScopedAddress(ScopedAddressValue scopedAddress) { + if (handleInnerScopeCallback) { + handleInnerScopeCallback(scopedAddress.value); + } + return true; + } + + void recursivelyVisitInnerGuaranteedPhi(PhiOperand phiOper, bool isReborrow); +}; + +// Dominating ownershipDef example: handleReborrow must continue visiting phi +// uses: +// +// bb0: +// d1 = ... +// cond_br bb1, bb2 +// bb1: +// b1 = borrow d1 +// br bb3(b1) +// bb2: +// b2 = borrow d1 +// br bb3(b2) +// bb3(reborrow): +// u1 = d1 +// u2 = reborrow +// // can't move destroy above u2 +// destroy_value d1 +// +// Dominating ownershipDef example: handleGuaranteedForwardingPhi must continue +// visiting phi uses: +// +// bb0: +// b1 = borrow d1 +// cond_br bb1, bb2 +// bb1: +// p1 = projection b1 +// br bb3(p1) +// bb2: +// p1 = projection b1 +// br bb3(p2) +// bb3(forwardingPhi): +// u1 = b1 +// u2 = forwardingPhi +// // can't move end_borrow above u2 +// end_borrow b1 +// +// TODO: when phi's have a reborrow flag, remove \p isReborrow. +void InteriorLivenessVisitor:: +recursivelyVisitInnerGuaranteedPhi(PhiOperand phiOper, bool isReborrow) { + SILValue phiValue = phiOper.getValue(); + if (!visited.insert(phiValue)) + return; + + if (!visitEnclosingDefs(phiValue, [this](SILValue enclosingDef){ + // If the enclosing def is \p ownershipDef, return false to check + // dominance. + if (enclosingDef == interiorLiveness.ownershipDef) + return false; + + // Otherwise, phiValue is enclosed by an outer adjacent phi, so its scope + // does not contribute to the outer liveness. This phi will be recorded as a + // regular use by the visitor, and this enclosing def will be visited as + // separate lifetime-ending-use use. Return true to continue checking if any + // other enclosing defs do not have an outer adjacent reborrow. + return true; + })) { + // At least one enclosing def is ownershipDef. If ownershipDef dominates + // phiValue, then this is consistent with a well-formed linear lifetime, and + // the phi's uses directly contribute to ownershipDef's liveness. + if (domInfo->dominates(interiorLiveness.ownershipDef->getParentBlock(), + phiValue->getParentBlock())) { + if (isReborrow) { + visitInnerBorrow(phiOper.getOperand()); + } else { + visitInteriorUses(phiValue); + } + return; + } + // ownershipDef does not dominate this phi. Record it so the liveness + // client can use this information to insert the missing outer adjacent phi. + interiorLiveness.unenclosedPhis.push_back(phiValue); + } +} + +void InteriorLiveness::compute(const DominanceInfo *domInfo, InnerScopeHandlerRef handleInnerScope) { + liveness.initializeDef(ownershipDef); + addressUseKind = AddressUseKind::NonEscaping; + InteriorLivenessVisitor(*this, domInfo, handleInnerScope) + .visitInteriorUses(ownershipDef); +} + +void InteriorLiveness::print(llvm::raw_ostream &OS) const { + OSSALiveness::print(OS); + + switch (getAddressUseKind()) { + case AddressUseKind::NonEscaping: + OS << "Complete liveness\n"; + break; + case AddressUseKind::PointerEscape: + OS << "Incomplete liveness: Escaping address\n"; + break; + case AddressUseKind::Unknown: + OS << "Incomplete liveness: Unknown address use\n"; + break; + } + OS << "Unenclosed phis {\n"; + for (SILValue phi : getUnenclosedPhis()) { + OS << " " << phi; + } + OS << "}\n"; +} + +void InteriorLiveness::dump() const { print(llvm::dbgs()); } + +// ============================================================================= +// ExtendedLinearLiveness +// ============================================================================= + +struct ExtendedLinearLivenessVisitor + : public OwnershipUseVisitor { + + ExtendedLinearLiveness &extendedLiveness; + + // State local to an invocation of + // OwnershipUseVisitor::visitOwnershipUses(). + InstructionSet visited; + + ExtendedLinearLivenessVisitor(ExtendedLinearLiveness &extendedLiveness) + : extendedLiveness(extendedLiveness), + visited(extendedLiveness.ownershipDef->getFunction()) {} + + bool handleUsePoint(Operand *use, UseLifetimeConstraint useConstraint) { + extendedLiveness.liveness.updateForUse( + use->getUser(), useConstraint == UseLifetimeConstraint::LifetimeEnding); + return true; + } + + bool handlePointerEscape(Operand *use) { + llvm_unreachable("a pointer escape cannot end a linear lifetime"); + } + + bool handleOwnedPhi(Operand *phiOper) { + extendedLiveness.liveness.initializeDef(PhiOperand(phiOper).getValue()); + return true; + } + + bool handleOuterReborrow(Operand *phiOper) { + extendedLiveness.liveness.initializeDef(PhiOperand(phiOper).getValue()); + return true; + } + + bool handleGuaranteedForwardingPhi(Operand *use) { + llvm_unreachable("guaranteed forwarding phi cannot end a linear lifetime"); + } + + bool handleInnerBorrow(BorrowingOperand borrowingOperand) { + llvm_unreachable("an inner borrow cannot end a linear lifetime"); + } + + bool handleInnerAdjacentReborrow(SILArgument *reborrow) { + llvm_unreachable("inner adjacent reborrows are not visited"); + } + + bool handleInnerReborrow(BorrowingOperand borrowingOperand) { + llvm_unreachable("an inner borrow cannot end a linear lifetime"); + } + + bool handleScopedAddress(ScopedAddressValue scopedAddress) { + llvm_unreachable("an scoped address cannot end a linear lifetime"); + } +}; + +ExtendedLinearLiveness::ExtendedLinearLiveness(SILValue def) + : ownershipDef(def), liveness(def->getFunction(), &discoveredBlocks) { + if (def->getOwnershipKind() != OwnershipKind::Owned) { + BorrowedValue borrowedValue(def); + assert(borrowedValue && borrowedValue.isLocalScope()); + (void)borrowedValue; + } +} + +void ExtendedLinearLiveness::compute() { + liveness.initializeDef(ownershipDef); + for (auto defIter = liveness.defBegin(); defIter != liveness.defEnd(); + ++defIter) { + auto *def = cast(*defIter); + ExtendedLinearLivenessVisitor(*this).visitLifetimeEndingUses(def); + } +} + +void ExtendedLinearLiveness::print(llvm::raw_ostream &OS) const { + liveness.print(OS); +} + +void ExtendedLinearLiveness::dump() const { print(llvm::dbgs()); } + +} // namespace swift diff --git a/lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp b/lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp index b3e3fcf5304ce..c88faa65b9287 100644 --- a/lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp +++ b/lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp @@ -69,6 +69,7 @@ #include "swift/AST/Type.h" #include "swift/Basic/TaggedUnion.h" #include "swift/SIL/MemAccessUtils.h" +#include "swift/SIL/OwnershipLiveness.h" #include "swift/SIL/PrunedLiveness.h" #include "swift/SIL/SILArgumentArrayRef.h" #include "swift/SIL/SILBasicBlock.h" @@ -514,6 +515,77 @@ struct FindBorrowIntroducers : UnitTest { } }; +// Arguments: +// - SILValue: value +// Dumps: +// - function +// - the computed pruned liveness +// - the liveness boundary +struct LinearLivenessTest : UnitTest { + LinearLivenessTest(UnitTestRunner *pass) : UnitTest(pass) {} + void invoke(Arguments &arguments) override { + SILValue value = arguments.takeValue(); + getFunction()->dump(); + llvm::dbgs() << "Linear liveness: " << value; + LinearLiveness liveness(value); + liveness.compute(); + liveness.print(llvm::outs()); + + PrunedLivenessBoundary boundary; + liveness.getLiveness().computeBoundary(boundary); + boundary.print(llvm::outs()); + } +}; + +// Arguments: +// - SILValue: value +// Dumps: +// - function +// - the computed pruned liveness +// - the liveness boundary +struct InteriorLivenessTest : UnitTest { + InteriorLivenessTest(UnitTestRunner *pass) : UnitTest(pass) {} + void invoke(Arguments &arguments) override { + SILValue value = arguments.takeValue(); + getFunction()->dump(); + llvm::dbgs() << "Interior liveness: " << value; + auto *dominanceAnalysis = getAnalysis(); + DominanceInfo *domTree = dominanceAnalysis->get(getFunction()); + InteriorLiveness liveness(value); + auto handleInnerScope = [](SILValue innerBorrow) { + llvm::outs() << "Inner scope: " << innerBorrow; + }; + liveness.compute(domTree, handleInnerScope); + liveness.print(llvm::outs()); + + PrunedLivenessBoundary boundary; + liveness.getLiveness().computeBoundary(boundary); + boundary.print(llvm::outs()); + } +}; + +// Arguments: +// - SILValue: value +// Dumps: +// - function +// - the computed pruned liveness +// - the liveness boundary +struct ExtendedLinearLivenessTest : UnitTest { + ExtendedLinearLivenessTest(UnitTestRunner *pass) : UnitTest(pass) {} + void invoke(Arguments &arguments) override { + SILValue value = arguments.takeValue(); + getFunction()->dump(); + llvm::dbgs() << "Extended liveness: " << value; + ExtendedLinearLiveness liveness(value); + liveness.compute(); + liveness.print(llvm::outs()); + + PrunedLivenessBoundary boundary; + liveness.getLiveness().computeBoundary(boundary); + boundary.print(llvm::outs()); + } +}; + //===----------------------------------------------------------------------===// // MARK: SimplifyCFG Unit Tests //===----------------------------------------------------------------------===// @@ -681,10 +753,13 @@ void UnitTestRunner::withTest(StringRef name, Doit doit) { ADD_UNIT_TEST_SUBCLASS("canonicalize-borrow-scope", CanonicalizeBorrowScopeTest) ADD_UNIT_TEST_SUBCLASS("dump-function", DumpFunction) + ADD_UNIT_TEST_SUBCLASS("extended-liveness", ExtendedLinearLivenessTest) ADD_UNIT_TEST_SUBCLASS("find-borrow-introducers", FindBorrowIntroducers) ADD_UNIT_TEST_SUBCLASS("find-enclosing-defs", FindEnclosingDefsTest) ADD_UNIT_TEST_SUBCLASS("function-get-self-argument-index", FunctionGetSelfArgumentIndex) + ADD_UNIT_TEST_SUBCLASS("interior-liveness", InteriorLivenessTest) ADD_UNIT_TEST_SUBCLASS("is-deinit-barrier", IsDeinitBarrierTest) + ADD_UNIT_TEST_SUBCLASS("linear-liveness", LinearLivenessTest) ADD_UNIT_TEST_SUBCLASS("multidef-liveness", MultiDefLivenessTest) 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/test/SILOptimizer/ownership_liveness_unit.sil b/test/SILOptimizer/ownership_liveness_unit.sil new file mode 100644 index 0000000000000..95bd5514823a2 --- /dev/null +++ b/test/SILOptimizer/ownership_liveness_unit.sil @@ -0,0 +1,737 @@ +// RUN: %target-sil-opt -unit-test-runner %s -o /dev/null 2>&1 | %FileCheck %s +// +// TODO: when complete lifetime verification become the default for SIL verification, +// then consider moving all tests with 'unreachable' into a separate file with the flag +// -disable-ownership-verification + +sil_stage canonical + +import Builtin + +enum FakeOptional { +case none +case some(T) +} + +class C {} +class D { + var object: C + @_borrowed @_hasStorage var borrowed: C { get set } +} + +struct PairC { + var first: C + var second: C +} + +sil @getC : $@convention(thin) () -> @owned C + +// ============================================================================= +// LinearLiveness +// ============================================================================= + +// CHECK-LABEL: begin running test 1 of 1 on testLinearRefElementEscape: linear-liveness with: @trace[0] +// CHECK-LABEL: Linear liveness: +// CHECK: lifetime-ending user: +// CHECK-SAME: end_borrow +// CHECK-NEXT: last user: end_borrow +// CHECK-NEXT: end running test 1 of 1 on testLinearRefElementEscape +sil [ossa] @testLinearRefElementEscape : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + test_specification "linear-liveness @trace[0]" + %borrow = begin_borrow %0 : $C + debug_value [trace] %borrow : $C + cond_br undef, bb1, bb2 + +bb1: + %d1 = unchecked_ref_cast %0 : $C to $D + %f1 = ref_element_addr %d1 : $D, #D.object + %p1 = address_to_pointer %f1 : $*C to $Builtin.RawPointer + br bb3(%d1 : $D) + +bb2: + %d2 = unchecked_ref_cast %0 : $C to $D + %f2 = ref_element_addr %d2 : $D, #D.object + %p2 = address_to_pointer %f2 : $*C to $Builtin.RawPointer + br bb3(%d2 : $D) + +bb3(%phi : @guaranteed $D): + end_borrow %borrow : $C + %99 = tuple() + return %99 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on testLinearBitwiseEscape: linear-liveness with: @trace[0] +// CHECK: Linear liveness: +// CHECK: lifetime-ending user: +// CHECK-SAME: end_borrow +// CHECK-NEXT: last user: end_borrow +// CHECK-NEXT: end running test 1 of 1 on testLinearBitwiseEscape +sil [ossa] @testLinearBitwiseEscape : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + test_specification "linear-liveness @trace[0]" + %borrow = begin_borrow %0 : $C + debug_value [trace] %borrow : $C + cond_br undef, bb1, bb2 + +bb1: + %d1 = unchecked_bitwise_cast %0 : $C to $D + br bb3(%d1 : $D) + +bb2: + %d2 = unchecked_bitwise_cast %0 : $C to $D + br bb3(%d2 : $D) + +bb3(%phi : @unowned $D): + end_borrow %borrow : $C + %99 = tuple() + return %99 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on testLinearInnerReborrow: linear-liveness with: @argument[0] +// CHECK: Linear liveness: +// CHECK: lifetime-ending user: +// CHECK-SAME: destroy_value %0 +// CHECK-NEXT: last user: destroy_value %0 +// CHECK-NEXT: end running test 1 of 1 on testLinearInnerReborrow +sil [ossa] @testLinearInnerReborrow : $@convention(thin) (@owned C) -> () { +bb0(%0 : @owned $C): + test_specification "linear-liveness @argument[0]" + cond_br undef, bb1, bb2 + +bb1: + %borrow1 = begin_borrow %0 : $C + br bb3(%borrow1 : $C) + +bb2: + %borrow2 = begin_borrow %0 : $C + br bb3(%borrow2 : $C) + +bb3(%phi : @guaranteed $C): + end_borrow %phi : $C + destroy_value %0 : $C + %99 = tuple() + return %99 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on testLinearAdjacentReborrow: linear-liveness with: @trace[0] +// CHECK: lifetime-ending user: +// CHECK-SAME: br bb3 +// CHECK-NEXT: lifetime-ending user: br bb3 +// CHECK-NEXT: last user: br bb3 +// CHECK-NEXT: last user: br bb3 +// CHECK-NEXT: end running test 1 of 1 on testLinearAdjacentReborrow +sil [ossa] @testLinearAdjacentReborrow : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + test_specification "linear-liveness @trace[0]" + %borrow = begin_borrow %0 : $C + debug_value [trace] %borrow : $C + cond_br undef, bb1, bb2 + +bb1: + %borrow1 = begin_borrow %borrow : $C + br bb3(%borrow : $C, %borrow1 : $C) + +bb2: + %borrow2 = begin_borrow %borrow : $C + br bb3(%borrow : $C, %borrow2 : $C) + +bb3(%outer : @guaranteed $C, %inner : @guaranteed $C): + end_borrow %inner : $C + end_borrow %outer : $C + %99 = tuple() + return %99 : $() +} + +// ============================================================================= +// visitInnerAdjacentPhis +// ============================================================================= + +// CHECK-LABEL: begin running test 1 of 1 on pay_the_phi: visit-inner-adjacent-phis with: @trace[0] +// CHECK: sil [ossa] @pay_the_phi : {{.*}} { +// CHECK: [[C:%[^,]+]] = apply +// CHECK: [[BORROW1:%[^,]+]] = begin_borrow %1 +// CHECK: [[BORROW2:%[^,]+]] = begin_borrow %1 +// CHECK: br [[EXIT:bb[0-9]+]]([[C]] : $C, [[BORROW1]] : $C, [[BORROW2]] : +// CHECK: [[EXIT]]({{%[^,]+}} : @owned $C, [[GUARANTEED1:%[^,]+]] : @guaranteed $C, [[GUARANTEED2:%[^,]+]] : +// CHECK: } // end sil function 'pay_the_phi' +// +// CHECK:[[GUARANTEED1]] = argument of [[EXIT]] +// CHECK:[[GUARANTEED2]] = argument of [[EXIT]] +// CHECK-LABEL: end running test 1 of 1 on pay_the_phi: visit-inner-adjacent-phis with: @trace[0] +sil [ossa] @pay_the_phi : $@convention(thin) () -> () { +entry: + %getC = function_ref @getC : $@convention(thin) () -> @owned C + %c = apply %getC() : $@convention(thin) () -> @owned C + %borrow1 = begin_borrow %c : $C + %borrow2 = begin_borrow %c : $C + %borrow3 = begin_borrow %borrow2 : $C + br exit(%c : $C, %borrow1 : $C, %borrow2 : $C, %borrow3 : $C) + +exit(%owned : @owned $C, %guaranteed_1 : @guaranteed $C, %guaranteed_2 : @guaranteed $C, %guaranteed_3 : @guaranteed $C): + test_specification "visit-inner-adjacent-phis @trace[0]" + debug_value [trace] %owned : $C + end_borrow %guaranteed_3 : $C + end_borrow %guaranteed_2 : $C + end_borrow %guaranteed_1 : $C + destroy_value %owned : $C + %retval = tuple () + return %retval : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on pay_the_phi_forward: visit-inner-adjacent-phis with: @trace[0] +// CHECK: bb1(%{{.*}} : @guaranteed $C, [[INNER:%.*]] : @guaranteed $D): // Preds: bb0 +// CHECK: } // end sil function 'pay_the_phi_forward' +// CHECK: [[INNER]] = argument of bb1 : $D +// CHECK: end running test 1 of 1 on pay_the_phi_forward: visit-inner-adjacent-phis with: @trace[0] +sil [ossa] @pay_the_phi_forward : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + %borrow0 = begin_borrow %0 : $C + %d0 = unchecked_ref_cast %borrow0 : $C to $D + br exit(%borrow0 : $C, %d0 : $D) + +exit(%reborrow : @guaranteed $C, %phi : @guaranteed $D): + test_specification "visit-inner-adjacent-phis @trace[0]" + debug_value [trace] %reborrow : $C + %f = ref_element_addr %phi : $D, #D.object + %o = load [copy] %f : $*C + destroy_value %o : $C + end_borrow %reborrow : $C + %retval = tuple () + return %retval : $() +} + +// ============================================================================= +// InteriorLiveness and visitAdjacentPhis +// ============================================================================= + +// CHECK-LABEL: begin running test 1 of 1 on testInteriorRefElementEscape: interior-liveness with: @trace[0] +// CHECK: Incomplete liveness: Escaping address +// CHECK: last user: %{{.*}} = address_to_pointer +// CHECK-NEXT: end running test 1 of 1 on testInteriorRefElementEscape: +sil [ossa] @testInteriorRefElementEscape : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + test_specification "interior-liveness @trace[0]" + %d = unchecked_ref_cast %0 : $C to $D + debug_value [trace] %d : $D + %f1 = ref_element_addr %d : $D, #D.object + %p1 = address_to_pointer %f1 : $*C to $Builtin.RawPointer + %99 = tuple() + return %99 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on testInteriorReborrow: interior-liveness with: @trace[0] +// CHECK: Complete liveness +// CHECK-NEXT: Unenclosed phis { +// CHECK-NEXT: } +// CHECK-NEXT: last user: br bb1 +// CHECK-NEXT: end running test 1 of 1 on testInteriorReborrow: interior-liveness with: @trace[0] +sil [ossa] @testInteriorReborrow : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + test_specification "interior-liveness @trace[0]" + %borrow = begin_borrow %0 : $C + debug_value [trace] %borrow : $C + br bb1(%borrow : $C) + +bb1(%reborrow : @guaranteed $C): + end_borrow %reborrow : $C + %99 = tuple() + return %99 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on testInteriorDominatedGuaranteedForwardingPhi: interior-liveness with: @argument[0] +// CHECK: Complete liveness +// CHECK: last user: %{{.*}} load [copy] +// CHECK-NEXT: end running test 1 of 1 on testInteriorDominatedGuaranteedForwardingPhi: +sil [ossa] @testInteriorDominatedGuaranteedForwardingPhi : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + test_specification "interior-liveness @argument[0]" + cond_br undef, bb1, bb2 + +bb1: + %d1 = unchecked_ref_cast %0 : $C to $D + br bb3(%d1 : $D) + +bb2: + %d2 = unchecked_ref_cast %0 : $C to $D + br bb3(%d2 : $D) + +bb3(%phi : @guaranteed $D): + %f = ref_element_addr %phi : $D, #D.object + %o = load [copy] %f : $*C + destroy_value %o : $C + %99 = tuple() + return %99 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on testInteriorNondominatedGuaranteedForwardingPhi: interior-liveness with: @trace[0] +// CHECK: Complete liveness +// CHECK: Unenclosed phis { +// CHECK-NEXT: } +// CHECK-NEXT: last user: br bb3( +// CHECK-NEXT: end running test 1 of 1 on testInteriorNondominatedGuaranteedForwardingPhi: interior-liveness with: @trace[0] +sil [ossa] @testInteriorNondominatedGuaranteedForwardingPhi : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + cond_br undef, bb1, bb2 + +bb1: + %borrow1 = begin_borrow %0 : $C + debug_value [trace] %borrow1 : $C + test_specification "interior-liveness @trace[0]" + %d1 = unchecked_ref_cast %borrow1 : $C to $D + br bb3(%borrow1 : $C, %d1 : $D) + +bb2: + %borrow2 = begin_borrow %0 : $C + %d2 = unchecked_ref_cast %borrow2 : $C to $D + br bb3(%borrow2 : $C, %d2 : $D) + +bb3(%reborrow : @guaranteed $C, %phi : @guaranteed $D): + %f = ref_element_addr %phi : $D, #D.object + %o = load [copy] %f : $*C + destroy_value %o : $C + end_borrow %reborrow : $C + %99 = tuple() + return %99 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on testInnerDominatedReborrow: interior-liveness with: @argument[0] +// CHECK: Interior liveness: +// CHECK: Inner scope: %{{.*}} = begin_borrow %0 : $C +// CHECK: Complete liveness +// CHECK: Unenclosed phis { +// CHECK-NEXT: } +// CHECK-NEXT: last user: end_borrow +// CHECK-NEXT: end running test 1 of 1 on testInnerDominatedReborrow: interior-liveness with: @argument[0] +sil [ossa] @testInnerDominatedReborrow : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + test_specification "interior-liveness @argument[0]" + %borrow = begin_borrow %0 : $C + br bb1(%borrow : $C) + +bb1(%reborrow : @guaranteed $C): + end_borrow %reborrow : $C + %99 = tuple() + return %99 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on testInnerDominatedReborrow2: interior-liveness with: @trace[0] +// CHECK: Inner scope: %{{.*}} = begin_borrow %1 : $C +// CHECK-NEXT: Inner scope: %{{.*}} = argument of bb3 : $C +// CHECK: Complete liveness +// CHECK-NEXT: Unenclosed phis { +// CHECK-NEXT: } +// CHECK-NEXT: last user: destroy_value +// CHECK-NEXT: end running test 1 of 1 on testInnerDominatedReborrow2: interior-liveness with: @trace[0] +sil [ossa] @testInnerDominatedReborrow2 : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + %copy0a = copy_value %0 : $C + %copy0b = copy_value %0 : $C + test_specification "interior-liveness @trace[0]" + debug_value [trace] %copy0a : $C + cond_br undef, bb1, bb2 + +bb1: + %borrow1 = begin_borrow %copy0a : $C + br bb3(%borrow1 : $C) + +bb2: + %borrow2 = begin_borrow %copy0b : $C + br bb3(%borrow2 : $C) + +bb3(%reborrow : @guaranteed $C): + end_borrow %reborrow : $C + destroy_value %copy0a : $C + destroy_value %copy0b : $C + %99 = tuple() + return %99 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on testInnerNonDominatedReborrow: interior-liveness with: @trace[0] +// CHECK: Inner scope: [[BORROW:%.*]] = begin_borrow [[DEF:%.*]] : $D +// CHECK: Complete liveness +// CHECK: Unenclosed phis { +// CHECK-NEXT: } +// CHECK-NEXT: last user: br bb3([[DEF]] : $D, [[BORROW]] : $D) +// CHECK-NEXT: end running test 1 of 1 on testInnerNonDominatedReborrow: interior-liveness with: @trace[0] +sil [ossa] @testInnerNonDominatedReborrow : $@convention(thin) (@guaranteed D) -> () { +bb0(%0 : @guaranteed $D): + cond_br undef, bb1, bb2 + +bb1: + test_specification "interior-liveness @trace[0]" + %borrow1 = begin_borrow %0 : $D + debug_value [trace] %borrow1 : $D + %inner1 = begin_borrow %borrow1 : $D + br bb3(%borrow1 : $D, %inner1 : $D) + +bb2: + %borrow2 = begin_borrow %0 : $D + %inner2 = begin_borrow %borrow2 : $D + br bb3(%borrow2 : $D, %inner2 : $D) + +bb3(%outer : @guaranteed $D, %inner : @guaranteed $D): + %f = ref_element_addr %inner : $D, #D.object + %o = load [copy] %f : $*C + destroy_value %o : $C + end_borrow %inner : $D + end_borrow %outer : $D + %99 = tuple() + return %99 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on testInnerAdjacentReborrow1: interior-liveness with: @trace[0] +// CHECK: Interior liveness: [[DEF:%.*]] = argument of bb3 : $D +// CHECK: Inner scope: %{{.*}} = argument of bb3 : $D +// CHECK: Inner scope: %{{.*}} = argument of bb4 : $D +// CHECK: regular user: end_borrow +// CHECK-NEXT: regular user: br bb4( +// CHECK-NEXT: Complete liveness +// CHECK-NEXT: Unenclosed phis { +// CHECK-NEXT: } +// CHECK-NEXT: last user: end_borrow +// CHECK-NEXT: end running test 1 of 1 on testInnerAdjacentReborrow1: interior-liveness with: @trace[0] +sil [ossa] @testInnerAdjacentReborrow1 : $@convention(thin) (@guaranteed D) -> () { +bb0(%0 : @guaranteed $D): + cond_br undef, bb1, bb2 + +bb1: + %copy1 = copy_value %0 : $D + %borrow1 = begin_borrow %copy1 : $D + br bb3(%copy1 : $D, %borrow1 : $D) + +bb2: + %copy2 = copy_value %0 : $D + %borrow2 = begin_borrow %0 : $D + br bb3(%copy2 : $D, %borrow2 : $D) + +bb3(%outer3 : @owned $D, %inner3 : @guaranteed $D): + test_specification "interior-liveness @trace[0]" + debug_value [trace] %outer3 : $D + br bb4(%inner3 : $D) + +bb4(%inner4 : @guaranteed $D): + %f = ref_element_addr %inner4 : $D, #D.object + %o = load [copy] %f : $*C + destroy_value %o : $C + end_borrow %inner4 : $D + unreachable +} + +// CHECK-LABEL: begin running test 1 of 1 on testInnerAdjacentReborrow2: interior-liveness with: @trace[0] +// CHECK: Interior liveness: [[DEF:%.*]] = argument of bb3 : $D +// CHECK: Inner scope: %{{.*}} = argument of bb3 : $D +// CHECK: Inner scope: %{{.*}} = argument of bb4 : $D +// CHECK: regular user: end_borrow +// CHECK-NEXT: regular user: br bb4( +// CHECK-NEXT: Complete liveness +// CHECK-NEXT: Unenclosed phis { +// CHECK-NEXT: } +// CHECK-NEXT: last user: end_borrow +// CHECK-NEXT: end running test 1 of 1 on testInnerAdjacentReborrow2: interior-liveness with: @trace[0] +sil [ossa] @testInnerAdjacentReborrow2 : $@convention(thin) (@guaranteed D) -> () { +bb0(%0 : @guaranteed $D): + cond_br undef, bb1, bb2 + +bb1: + %copy1 = copy_value %0 : $D + %borrow1 = begin_borrow %copy1 : $D + br bb3(%copy1 : $D, %borrow1 : $D) + +bb2: + %copy2 = copy_value %0 : $D + %borrow2 = begin_borrow %0 : $D + br bb3(%copy2 : $D, %borrow2 : $D) + +bb3(%outer3 : @owned $D, %inner3 : @guaranteed $D): + test_specification "interior-liveness @trace[0]" + debug_value [trace] %outer3 : $D + br bb4(%inner3 : $D) + +bb4(%inner4 : @guaranteed $D): + %f = ref_element_addr %inner4 : $D, #D.object + %o = load [copy] %f : $*C + destroy_value %o : $C + end_borrow %inner4 : $D + unreachable +} + +// CHECK-LABEL: begin running test 1 of 1 on testInnerNonAdjacentReborrow: interior-liveness with: @trace[0] +// CHECK: Interior liveness: [[DEF:%.*]] = argument of bb3 : $D +// CHECK-NOT: Inner scope +// CHECK-NOT: lifetime-ending user +// CHECK: Complete liveness +// CHECK-NEXT: Unenclosed phis { +// CHECK-NEXT: } +// CHECK-NEXT: dead def: [[DEF]] = argument of bb3 : $D +// CHECK-NEXT: end running test 1 of 1 on testInnerNonAdjacentReborrow: interior-liveness with: @trace[0] +sil [ossa] @testInnerNonAdjacentReborrow : $@convention(thin) (@guaranteed D) -> () { +bb0(%0 : @guaranteed $D): + cond_br undef, bb1, bb2 + +bb1: + %copy1 = copy_value %0 : $D + %borrow1 = begin_borrow %0 : $D + br bb3(%copy1 : $D, %borrow1 : $D) + +bb2: + %copy2 = copy_value %0 : $D + %borrow2 = begin_borrow %0 : $D + br bb3(%copy2 : $D, %borrow2 : $D) + +bb3(%outer3 : @owned $D, %inner3 : @guaranteed $D): + test_specification "interior-liveness @trace[0]" + debug_value [trace] %outer3 : $D + br bb4(%inner3 : $D) + +bb4(%inner4 : @guaranteed $D): + %f = ref_element_addr %inner4 : $D, #D.object + %o = load [copy] %f : $*C + destroy_value %o : $C + end_borrow %inner4 : $D + unreachable +} + +// CHECK-LABEL: begin running test 1 of 1 on testInnerAdjacentPhi1: interior-liveness with: @trace[0] +// CHECK: Interior liveness: %{{.*}} = argument of bb3 : $C +// CHECK: Complete liveness +// CHECK: Unenclosed phis { +// CHECK-NEXT: } +// CHECK-NEXT: last user: %{{.*}} = load [copy] +// CHECK-NEXT: end running test 1 of 1 on testInnerAdjacentPhi1: interior-liveness with: @trace[0] +sil [ossa] @testInnerAdjacentPhi1 : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + cond_br undef, bb1, bb2 + +bb1: + %copy1 = copy_value %0 : $C + %borrow1 = begin_borrow %copy1 : $C + %d1 = unchecked_ref_cast %borrow1 : $C to $D + br bb3(%copy1 : $C, %borrow1 : $C, %d1 :$D) + +bb2: + %copy2 = copy_value %0 : $C + %borrow2 = begin_borrow %copy2 : $C + %d2 = unchecked_ref_cast %0 : $C to $D + br bb3(%copy2 : $C, %borrow2 : $C, %d2 :$D) + +bb3(%outer3 : @owned $C, %inner3 : @guaranteed $C, %phi3 : @guaranteed $D): + test_specification "interior-liveness @trace[0]" + debug_value [trace] %inner3 : $C + br bb4(%phi3 : $D) + +bb4(%phi4 : @guaranteed $D): + %f = ref_element_addr %phi4 : $D, #D.object + %o = load [copy] %f : $*C + destroy_value %o : $C + unreachable +} + +// CHECK-LABEL: begin running test 1 of 1 on testInnerAdjacentPhi2: interior-liveness with: @trace[0] +// CHECK: Interior liveness: %{{.*}} = argument of bb3 : $C +// CHECK: Complete liveness +// CHECK: Unenclosed phis { +// CHECK-NEXT: } +// CHECK-NEXT: last user: %{{.*}} = load [copy] +// CHECK-NEXT: end running test 1 of 1 on testInnerAdjacentPhi2: interior-liveness with: @trace[0] +sil [ossa] @testInnerAdjacentPhi2 : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + cond_br undef, bb1, bb2 + +bb1: + %copy1 = copy_value %0 : $C + %borrow1 = begin_borrow %copy1 : $C + %d1 = unchecked_ref_cast %0 : $C to $D + br bb3(%copy1 : $C, %borrow1 : $C, %d1 :$D) + +bb2: + %copy2 = copy_value %0 : $C + %borrow2 = begin_borrow %copy2 : $C + %d2 = unchecked_ref_cast %borrow2 : $C to $D + br bb3(%copy2 : $C, %borrow2 : $C, %d2 :$D) + +bb3(%outer3 : @owned $C, %inner3 : @guaranteed $C, %phi3 : @guaranteed $D): + test_specification "interior-liveness @trace[0]" + debug_value [trace] %inner3 : $C + br bb4(%phi3 : $D) + +bb4(%phi4 : @guaranteed $D): + %f = ref_element_addr %phi4 : $D, #D.object + %o = load [copy] %f : $*C + destroy_value %o : $C + unreachable +} + +// CHECK-LABEL: begin running test 1 of 1 on testInnerNonAdjacentPhi: interior-liveness with: @trace[0] +// CHECK: Interior liveness: [[DEF:%.*]] = argument of bb3 : $C +// CHECK: Complete liveness +// CHECK: Unenclosed phis { +// CHECK-NEXT: } +// CHECK-NEXT: dead def: [[DEF]] = argument of bb3 : $C +// CHECK-NEXT: end running test 1 of 1 on testInnerNonAdjacentPhi: interior-liveness with: @trace[0] +sil [ossa] @testInnerNonAdjacentPhi : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + cond_br undef, bb1, bb2 + +bb1: + %copy1 = copy_value %0 : $C + %borrow1 = begin_borrow %copy1 : $C + %d1 = unchecked_ref_cast %0 : $C to $D + br bb3(%copy1 : $C, %borrow1 : $C, %d1 :$D) + +bb2: + %copy2 = copy_value %0 : $C + %borrow2 = begin_borrow %copy2 : $C + %d2 = unchecked_ref_cast %0 : $C to $D + br bb3(%copy2 : $C, %borrow2 : $C, %d2 :$D) + +bb3(%outer3 : @owned $C, %inner3 : @guaranteed $C, %phi3 : @guaranteed $D): + test_specification "interior-liveness @trace[0]" + debug_value [trace] %inner3 : $C + br bb4(%phi3 : $D) + +bb4(%phi4 : @guaranteed $D): + %f = ref_element_addr %phi4 : $D, #D.object + %o = load [copy] %f : $*C + destroy_value %o : $C + unreachable +} + +// CHECK-LABEL: begin running test 1 of 1 on testScopedAddress: interior-liveness with: @argument[0] +// CHECK: Complete liveness +// CHECK-NEXT: Unenclosed phis { +// CHECK-NEXT: } +// CHECK-NEXT: last user: end_access +// CHECK-NEXT: end running test 1 of 1 on testScopedAddress: interior-liveness with: @argument[0] +sil [ossa] @testScopedAddress : $@convention(thin) (@guaranteed D) -> () { +bb0(%0 : @guaranteed $D): + test_specification "interior-liveness @argument[0]" + %f = ref_element_addr %0 : $D, #D.object + %access = begin_access [read] [static] %f : $*C + %o = load [copy] %access : $*C + end_access %access : $*C + destroy_value %o : $C + %99 = tuple() + return %99 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on testUnenclosedReborrow: interior-liveness with: @trace[0] +// CHECK: Interior liveness: [[DEF:%.*]] = copy_value %0 : $C +// CHECK-NEXT: Inner scope: [[BORROW:%.*]] = begin_borrow [[DEF]] : $C +// CHECK: Complete liveness +// CHECK: Unenclosed phis { +// CHECK-NEXT: %{{.*}} = argument of bb3 : $C +// CHECK-NEXT: } +// CHECK-NEXT: last user: br bb3([[BORROW]] : $C) +// CHECK-NEXT: end running test 1 of 1 on testUnenclosedReborrow: interior-liveness with: @trace[0] +sil [ossa] @testUnenclosedReborrow : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + cond_br undef, bb1, bb2 + +bb1: + test_specification "interior-liveness @trace[0]" + %copy0a = copy_value %0 : $C + debug_value [trace] %copy0a : $C + %borrow1 = begin_borrow %copy0a : $C + br bb3(%borrow1 : $C) + +bb2: + %copy0b = copy_value %0 : $C + %borrow2 = begin_borrow %copy0b : $C + br bb3(%borrow2 : $C) + +bb3(%reborrow : @guaranteed $C): + end_borrow %reborrow : $C + unreachable +} + +// CHECK-LABEL: begin running test 1 of 1 on testUnenclosedGuaranteedPhi: interior-liveness with: @trace[0] +// CHECK: Interior liveness: %{{.*}} = begin_borrow +// CHECK: Complete liveness +// CHECK: Unenclosed phis { +// CHECK-NEXT: %{{.*}} = argument of bb3 : $D +// CHECK-NEXT: } +// CHECK-NEXT: last user: br bb3( +// CHECK-NEXT: end running test 1 of 1 on testUnenclosedGuaranteedPhi: interior-liveness with: @trace[0] +sil [ossa] @testUnenclosedGuaranteedPhi : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + cond_br undef, bb1, bb2 + +bb1: + test_specification "interior-liveness @trace[0]" + %copy0a = copy_value %0 : $C + %borrow1 = begin_borrow %copy0a : $C + debug_value [trace] %borrow1 : $C + %d1 = unchecked_ref_cast %borrow1 : $C to $D + br bb3(%d1 : $D) + +bb2: + %copy0b = copy_value %0 : $C + %borrow2 = begin_borrow %copy0b : $C + %d2 = unchecked_ref_cast %borrow2 : $C to $D + br bb3(%d2 : $D) + +bb3(%phi : @guaranteed $D): + unreachable +} + +// ============================================================================= +// ExtendedLiveness +// ============================================================================= + +// CHECK-LABEL: begin running test 1 of 1 on testExtendedPhi: extended-liveness with: @trace[0] +// CHECK: Extended liveness: %{{.*}} = copy_value %0 : $C +// CHECK: lifetime-ending user: br bb3( +// CHECK: lifetime-ending user: destroy_value +// CHECK: last user: br bb3( +// CHECK: last user: destroy_value +// CHECK: end running test 1 of 1 on testExtendedPhi: extended-liveness with: @trace[0] +sil [ossa] @testExtendedPhi : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + cond_br undef, bb1, bb2 + +bb1: + test_specification "extended-liveness @trace[0]" + %copy1 = copy_value %0 : $C + debug_value [trace] %copy1 : $C + br bb3(%copy1 : $C) + +bb2: + %copy2 = copy_value %0 : $C + br bb3(%copy2 : $C) + +bb3(%phi : @owned $C): + destroy_value %phi : $C + %99 = tuple() + return %99 : $() +} + +// CHECK-LABEL: begin running test 1 of 1 on testExtendedReborrow: extended-liveness with: @trace[0] +// CHECK: Extended liveness: %{{.*}} = begin_borrow +// CHECK: lifetime-ending user: br bb3( +// CHECK: lifetime-ending user: end_borrow +// CHECK: last user: br bb3( +// CHECK: last user: end_borrow +// CHECK: end running test 1 of 1 on testExtendedReborrow: extended-liveness with: @trace[0] +sil [ossa] @testExtendedReborrow : $@convention(thin) (@guaranteed C) -> () { +bb0(%0 : @guaranteed $C): + cond_br undef, bb1, bb2 + +bb1: + %copy1 = copy_value %0 : $C + test_specification "extended-liveness @trace[0]" + %borrow1 = begin_borrow %copy1 : $C + debug_value [trace] %borrow1 : $C + br bb3(%copy1 : $C, %borrow1 : $C) + +bb2: + %copy2 = copy_value %0 : $C + %borrow2 = begin_borrow %copy2 : $C + br bb3(%copy2 : $C, %borrow2 : $C) + +bb3(%phi : @owned $C, %reborrow : @guaranteed $C): + end_borrow %reborrow : $C + destroy_value %phi : $C + %99 = tuple() + return %99 : $() +} From ff8f632f2b851d9da6f323e350946ce42565aa7d Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Fri, 10 Feb 2023 11:39:19 -0800 Subject: [PATCH 96/98] [Test] Temporarily disable failing test to unblock CI --- test/SourceKit/CursorInfo/static_vs_class_spelling.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/SourceKit/CursorInfo/static_vs_class_spelling.swift b/test/SourceKit/CursorInfo/static_vs_class_spelling.swift index 420122bc8d8bc..74a1c24e97733 100644 --- a/test/SourceKit/CursorInfo/static_vs_class_spelling.swift +++ b/test/SourceKit/CursorInfo/static_vs_class_spelling.swift @@ -1,3 +1,5 @@ +// REQUIRES: rdar105287822 + // RUN: %empty-directory(%t) // RUN: %empty-directory(%t/Modules) // RUN: %{python} %utils/split_file.py -o %t %s From 0dcdeeb7b0f6d45c724a61d32fac851831e9f86a Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 11 Feb 2023 06:35:27 +0000 Subject: [PATCH 97/98] [wasm] Enable SWIFT_ENABLE_EXPERIMENTAL_REFLECTION for stdlib to fix test failures --- utils/webassembly/build-toolchain.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/webassembly/build-toolchain.sh b/utils/webassembly/build-toolchain.sh index eb71722e06955..6662e5c6bd39b 100755 --- a/utils/webassembly/build-toolchain.sh +++ b/utils/webassembly/build-toolchain.sh @@ -149,6 +149,7 @@ build_target_toolchain() { -D SWIFT_ENABLE_EXPERIMENTAL_DIFFERENTIABLE_PROGRAMMING=YES \ -D SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED=YES \ -D SWIFT_ENABLE_EXPERIMENTAL_STRING_PROCESSING=YES \ + -D SWIFT_ENABLE_EXPERIMENTAL_REFLECTION=YES \ -D SWIFT_PATH_TO_SWIFT_SYNTAX_SOURCE="$SOURCE_PATH/swift-syntax" \ -D SWIFT_PATH_TO_STRING_PROCESSING_SOURCE="$SOURCE_PATH/swift-experimental-string-processing" \ -G Ninja \ From 134fe4fe9c0042414a7b3992a4451262f83271c9 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 11 Feb 2023 12:31:52 +0000 Subject: [PATCH 98/98] [wasm] XFAIL _Runtime tests for now --- test/stdlib/Reflection/PartialType.swift | 1 + test/stdlib/_Runtime/Metadata/StructMetadata.swift | 1 + test/stdlib/_Runtime/Metadata/TupleMetadata.swift | 1 + test/stdlib/_Runtime/Metadata/ValueWitnessTable.swift | 1 + 4 files changed, 4 insertions(+) diff --git a/test/stdlib/Reflection/PartialType.swift b/test/stdlib/Reflection/PartialType.swift index 49f7a2f61428e..3019d7b2257a4 100644 --- a/test/stdlib/Reflection/PartialType.swift +++ b/test/stdlib/Reflection/PartialType.swift @@ -1,6 +1,7 @@ // RUN: %target-run-simple-swift // REQUIRES: executable_test // UNSUPPORTED: freestanding +// XFAIL: OS=wasi import StdlibUnittest import _Runtime diff --git a/test/stdlib/_Runtime/Metadata/StructMetadata.swift b/test/stdlib/_Runtime/Metadata/StructMetadata.swift index 5ac4ba9a32d2b..cbcad0934aa37 100644 --- a/test/stdlib/_Runtime/Metadata/StructMetadata.swift +++ b/test/stdlib/_Runtime/Metadata/StructMetadata.swift @@ -2,6 +2,7 @@ // REQUIRES: executable_test // UNSUPPORTED: freestanding +// XFAIL: OS=wasi import StdlibUnittest import _Runtime diff --git a/test/stdlib/_Runtime/Metadata/TupleMetadata.swift b/test/stdlib/_Runtime/Metadata/TupleMetadata.swift index 8b866acf8f392..020a676ee0029 100644 --- a/test/stdlib/_Runtime/Metadata/TupleMetadata.swift +++ b/test/stdlib/_Runtime/Metadata/TupleMetadata.swift @@ -2,6 +2,7 @@ // REQUIRES: executable_test // UNSUPPORTED: freestanding +// XFAIL: OS=wasi import StdlibUnittest import _Runtime diff --git a/test/stdlib/_Runtime/Metadata/ValueWitnessTable.swift b/test/stdlib/_Runtime/Metadata/ValueWitnessTable.swift index 46d3ee685b7b6..1a444e9751588 100644 --- a/test/stdlib/_Runtime/Metadata/ValueWitnessTable.swift +++ b/test/stdlib/_Runtime/Metadata/ValueWitnessTable.swift @@ -2,6 +2,7 @@ // REQUIRES: executable_test // UNSUPPORTED: freestanding +// XFAIL: OS=wasi import StdlibUnittest import _Runtime