From 6c066502a240efb2757aecb18a33e9c4f3ee120d Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 6 Mar 2023 23:49:31 -0500 Subject: [PATCH 1/7] Add an API to map contextual types from the pack to element environment I'm not really convinced that the existing implementation here is correct in general; it might work for the type checker's use cases, but I don't think we can rely on not seeing opened element archetypes from other expansions in the type we're processing here. But we can at least tread water while offering a more convenient API. --- include/swift/AST/GenericEnvironment.h | 12 ++++++++++-- lib/AST/GenericEnvironment.cpp | 23 +++++++++++++++++++++++ lib/IRGen/GenPack.cpp | 7 ++----- lib/SILGen/SILGenApply.cpp | 4 ++-- lib/SILGen/SILGenPack.cpp | 7 ++----- 5 files changed, 39 insertions(+), 14 deletions(-) diff --git a/include/swift/AST/GenericEnvironment.h b/include/swift/AST/GenericEnvironment.h index 32c98bd393fbd..2554f1bbcca4f 100644 --- a/include/swift/AST/GenericEnvironment.h +++ b/include/swift/AST/GenericEnvironment.h @@ -277,10 +277,18 @@ class alignas(1 << DeclAlignInBits) GenericEnvironment final /// Map a generic parameter type to a contextual type. Type mapTypeIntoContext(GenericTypeParamType *type) const; - /// Map a type containing parameter packs to a contextual type - /// in the opened element generic context. + /// Map an interface type containing parameter packs to a contextual + /// type in the opened element generic context. Type mapPackTypeIntoElementContext(Type type) const; + /// Map a contextual type containing parameter packs to a contextual + /// type in the opened element generic context. + Type mapContextualPackTypeIntoElementContext(Type type) const; + + /// Map a contextual type containing parameter packs to a contextual + /// type in the opened element generic context. + CanType mapContextualPackTypeIntoElementContext(CanType type) const; + /// Map a type containing pack element type parameters to a contextual /// type in the pack generic context. Type mapElementTypeIntoPackContext(Type type) const; diff --git a/lib/AST/GenericEnvironment.cpp b/lib/AST/GenericEnvironment.cpp index 895f006a61f8a..ea33258eb6e9b 100644 --- a/lib/AST/GenericEnvironment.cpp +++ b/lib/AST/GenericEnvironment.cpp @@ -583,6 +583,29 @@ Type GenericEnvironment::mapTypeIntoContext(GenericTypeParamType *type) const { return result; } +Type +GenericEnvironment::mapContextualPackTypeIntoElementContext(Type type) const { + if (!type->hasArchetype()) return type; + + // FIXME: this is potentially wrong if there are multiple + // openings in play at once, because we really shouldn't touch + // other element archetypes. + return mapPackTypeIntoElementContext(type->mapTypeOutOfContext()); +} + +CanType +GenericEnvironment::mapContextualPackTypeIntoElementContext(CanType type) const { + if (!type->hasArchetype()) return type; + + // FIXME: this is potentially wrong if there are multiple + // openings in play at once, because we really shouldn't touch + // other element archetypes. + // FIXME: if we do this properly, there's no way for this rewrite + // to produce a non-canonical type. + return mapPackTypeIntoElementContext(type->mapTypeOutOfContext()) + ->getCanonicalType(); +} + Type GenericEnvironment::mapPackTypeIntoElementContext(Type type) const { assert(getKind() == Kind::OpenedElement); diff --git a/lib/IRGen/GenPack.cpp b/lib/IRGen/GenPack.cpp index 56af7ebb934b8..e893a023b5d08 100644 --- a/lib/IRGen/GenPack.cpp +++ b/lib/IRGen/GenPack.cpp @@ -328,8 +328,7 @@ static llvm::Value *emitPackExpansionElementMetadata( // Replace pack archetypes with element archetypes in the pattern type. auto instantiatedPatternTy = context.environment - ->mapPackTypeIntoElementContext(patternTy->mapTypeOutOfContext()) - ->getCanonicalType(); + ->mapContextualPackTypeIntoElementContext(patternTy); // Emit the element metadata. auto element = IGF.emitTypeMetadataRef(instantiatedPatternTy, request) @@ -505,9 +504,7 @@ static llvm::Value *emitPackExpansionElementWitnessTable( // Replace pack archetypes with element archetypes in the pattern type. auto instantiatedPatternTy = - context.environment - ->mapPackTypeIntoElementContext(patternTy->mapTypeOutOfContext()) - ->getCanonicalType(); + context.environment->mapContextualPackTypeIntoElementContext(patternTy); auto instantiatedConformance = context.environment->getGenericSignature()->lookupConformance( instantiatedPatternTy, conformance.getRequirement()); diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 27231bddebcd5..4078e71b2e561 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -3837,8 +3837,8 @@ class ArgEmitter { auto loweredPatternType = expectedParamType.castTo().getPatternType(); auto loweredElementType = - openedElementEnv->mapPackTypeIntoElementContext( - loweredPatternType->mapTypeOutOfContext())->getCanonicalType(); + openedElementEnv->mapContextualPackTypeIntoElementContext( + loweredPatternType); return SILType::getPrimitiveAddressType(loweredElementType); }(); diff --git a/lib/SILGen/SILGenPack.cpp b/lib/SILGen/SILGenPack.cpp index bf446616bea34..0b5484bc796c7 100644 --- a/lib/SILGen/SILGenPack.cpp +++ b/lib/SILGen/SILGenPack.cpp @@ -290,9 +290,7 @@ deriveOpenedElementTypeForPackExpansion(SILGenModule &SGM, OpenedElementContext::createForContextualExpansion(SGM.getASTContext(), expansion); auto elementType = - context.environment->mapPackTypeIntoElementContext( - patternType->mapTypeOutOfContext()) - ->getCanonicalType(); + context.environment->mapContextualPackTypeIntoElementContext(patternType); return std::make_pair(context.environment, SILType::getPrimitiveAddressType(elementType)); } @@ -485,8 +483,7 @@ void InPlacePackExpansionInitialization:: // This AST-level transformation is fine on lowered types because // we're just replacing pack archetypes with element archetypes. loweredPatternTy = - env->mapPackTypeIntoElementContext( - loweredPatternTy->mapTypeOutOfContext())->getCanonicalType(); + env->mapContextualPackTypeIntoElementContext(loweredPatternTy); } auto eltAddrTy = SILType::getPrimitiveAddressType(loweredPatternTy); From bb05a0edc13615d9b264654e82a4fc027fbd595d Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 6 Mar 2023 23:54:42 -0500 Subject: [PATCH 2/7] Add a convenience API to build canonical pack types from slices of canonical tuple type elements. This has come up more than you might think. --- include/swift/AST/Types.h | 1 + lib/AST/ASTContext.cpp | 6 ++++++ lib/AST/ParameterPack.cpp | 9 +-------- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/swift/AST/Types.h b/include/swift/AST/Types.h index 615a2d0e7cef6..7795464604794 100644 --- a/include/swift/AST/Types.h +++ b/include/swift/AST/Types.h @@ -6829,6 +6829,7 @@ class PackType final : public TypeBase, public llvm::FoldingSetNode, }; BEGIN_CAN_TYPE_WRAPPER(PackType, Type) static CanPackType get(const ASTContext &ctx, ArrayRef elements); + static CanPackType get(const ASTContext &ctx, CanTupleEltTypeArrayRef elts); CanType getElementType(unsigned elementNo) const { return CanType(getPointer()->getElementType(elementNo)); diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index bc5822a21da6d..d49af97f6639a 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3303,6 +3303,12 @@ CanPackType CanPackType::get(const ASTContext &C, ArrayRef elements) { return CanPackType(PackType::get(C, ncElements)); } +CanPackType CanPackType::get(const ASTContext &C, + CanTupleEltTypeArrayRef elements) { + SmallVector ncElements(elements.begin(), elements.end()); + return CanPackType(PackType::get(C, ncElements)); +} + PackType *PackType::get(const ASTContext &C, ArrayRef elements) { RecursiveTypeProperties properties; bool isCanonical = true; diff --git a/lib/AST/ParameterPack.cpp b/lib/AST/ParameterPack.cpp index 567996ec89fc3..ef195c934c77a 100644 --- a/lib/AST/ParameterPack.cpp +++ b/lib/AST/ParameterPack.cpp @@ -478,13 +478,6 @@ bool SILPackType::containsPackExpansionType() const { CanPackType CanTupleType::getInducedPackTypeImpl(CanTupleType tuple, unsigned start, unsigned count) { assert(start + count <= tuple->getNumElements() && "range out of range"); - auto &ctx = tuple->getASTContext(); - if (count == 0) return CanPackType::get(ctx, {}); - - SmallVector eltTypes; - eltTypes.reserve(count); - for (unsigned i = start, e = start + count; i != e; ++i) - eltTypes.push_back(tuple.getElementType(i)); - return CanPackType::get(ctx, eltTypes); + return CanPackType::get(ctx, tuple.getElementTypes().slice(start, count)); } From 70e83b530427b8c04f2a59c8762451af0eb10a5b Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 6 Mar 2023 23:59:36 -0500 Subject: [PATCH 3/7] Make it easier to define iterator ranges for APIs like getFoo(index). We'll use this in AbstractionPattern. --- include/swift/Basic/IndexedViewRange.h | 100 +++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 include/swift/Basic/IndexedViewRange.h diff --git a/include/swift/Basic/IndexedViewRange.h b/include/swift/Basic/IndexedViewRange.h new file mode 100644 index 0000000000000..ee23f141c4ccc --- /dev/null +++ b/include/swift/Basic/IndexedViewRange.h @@ -0,0 +1,100 @@ +//===- IndexedViewRange.h - Iterators for indexed projections ---*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 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 IndexedViewRange, a template class which makes it +// easy to define a range for a "collection" that is normally just vended +// with an indexed accessor. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BASIC_INDEXEDVIEWRANGE_H +#define SWIFT_BASIC_INDEXEDVIEWRANGE_H + +#include +#include +#include "llvm/ADT/iterator_range.h" + +namespace swift { + +/// An iterator over a range of values provided by an indexed accessor +/// on a base type. +template +class IndexedViewIterator { +public: + using value_type = ProjectedType; + using reference = ProjectedType; + using pointer = void; + using difference_type = ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; +private: + BaseType Base; + size_t Index; +public: + IndexedViewIterator(BaseType base, size_t index) + : Base(base), Index(index) {} +public: + ProjectedType operator*() const { return Project(Base, Index); } + ProjectedType operator->() const { return Project(Base, Index); } + IndexedViewIterator &operator++() { Index++; return *this; } + IndexedViewIterator operator++(int) { return iterator(Base, Index++); } + IndexedViewIterator &operator--() { Index--; return *this; } + IndexedViewIterator operator--(int) { return iterator(Base, Index--); } + bool operator==(IndexedViewIterator rhs) const { return Index == rhs.Index; } + bool operator!=(IndexedViewIterator rhs) const { return Index != rhs.Index; } + + IndexedViewIterator &operator+=(difference_type i) { + Index += i; + return *this; + } + IndexedViewIterator operator+(difference_type i) const { + return IndexedViewIterator(Base, Index + i); + } + friend IndexedViewIterator operator+(difference_type i, + IndexedViewIterator rhs) { + return IndexedViewIterator(rhs.Base, rhs.Index + i); + } + IndexedViewIterator &operator-=(difference_type i) { + Index -= i; + return *this; + } + IndexedViewIterator operator-(difference_type i) const { + return IndexedViewIterator(Base, Index - i); + } + difference_type operator-(IndexedViewIterator rhs) const { + return Index - rhs.Index; + } + ProjectedType operator[](difference_type i) const { + return Project(Base, Index + i); + } + bool operator<(IndexedViewIterator rhs) const { + return Index < rhs.Index; + } + bool operator<=(IndexedViewIterator rhs) const { + return Index <= rhs.Index; + } + bool operator>(IndexedViewIterator rhs) const { + return Index > rhs.Index; + } + bool operator>=(IndexedViewIterator rhs) const { + return Index >= rhs.Index; + } +}; + +template +using IndexedViewRange = + llvm::iterator_range>; + +} // end namespace swift + +#endif // SWIFT_BASIC_INDEXEDVIEWRANGE_H From 81d9e6865a44a0f7b421f2fc9a835900254f3835 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 6 Mar 2023 23:56:45 -0500 Subject: [PATCH 4/7] Add a couple convenience APIs for working with abstraction patterns --- include/swift/SIL/AbstractionPattern.h | 14 ++++++++++++++ lib/SIL/IR/AbstractionPattern.cpp | 9 +++++++++ lib/SILGen/SILGenApply.cpp | 7 ++----- lib/SILGen/SILGenStmt.cpp | 6 +----- 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/include/swift/SIL/AbstractionPattern.h b/include/swift/SIL/AbstractionPattern.h index 3097b7181a072..fbb5a6391c62b 100644 --- a/include/swift/SIL/AbstractionPattern.h +++ b/include/swift/SIL/AbstractionPattern.h @@ -18,6 +18,7 @@ #ifndef SWIFT_SIL_ABSTRACTIONPATTERN_H #define SWIFT_SIL_ABSTRACTIONPATTERN_H +#include "swift/Basic/IndexedViewRange.h" #include "swift/AST/Decl.h" #include "swift/AST/Types.h" @@ -1332,6 +1333,17 @@ class AbstractionPattern { llvm_unreachable("bad kind"); } + static AbstractionPattern + projectTupleElementType(const AbstractionPattern *base, size_t index) { + return base->getTupleElementType(index); + } + + IndexedViewRange getTupleElementTypes() const { + assert(isTuple()); + return { { this, 0 }, { this, getNumTupleElements() } }; + } + /// Is the given pack type a valid substitution of this abstraction /// pattern? bool matchesPack(CanPackType substType); @@ -1472,6 +1484,8 @@ class AbstractionPattern { void forEachPackExpandedComponent( llvm::function_ref fn) const; + SmallVector getPackExpandedComponents() const; + /// If this pattern refers to a foreign ObjC method that was imported as /// async, return the bridged-back-to-ObjC completion handler type. CanType getObjCMethodAsyncCompletionHandlerForeignType( diff --git a/lib/SIL/IR/AbstractionPattern.cpp b/lib/SIL/IR/AbstractionPattern.cpp index 6dbc452a997c7..c366bf0f4a3ca 100644 --- a/lib/SIL/IR/AbstractionPattern.cpp +++ b/lib/SIL/IR/AbstractionPattern.cpp @@ -588,6 +588,15 @@ AbstractionPattern AbstractionPattern::getPackExpansionCountType() const { llvm_unreachable("bad kind"); } +SmallVector +AbstractionPattern::getPackExpandedComponents() const { + SmallVector result; + forEachPackExpandedComponent([&](AbstractionPattern pattern) { + result.push_back(pattern); + }); + return result; +} + void AbstractionPattern::forEachPackExpandedComponent( llvm::function_ref fn) const { assert(isPackExpansion()); diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 4078e71b2e561..1d68e8dddf4a1 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -3229,11 +3229,8 @@ class ArgEmitter { // Otherwise we need to emit a pack argument. } else { - SmallVector origPackEltPatterns; - origFormalParamType.forEachPackExpandedComponent( - [&](AbstractionPattern pattern) { - origPackEltPatterns.push_back(pattern); - }); + auto origPackEltPatterns = + origFormalParamType.getPackExpandedComponents(); auto argSourcesSlice = argSources.slice(nextArgSourceIndex, origPackEltPatterns.size()); diff --git a/lib/SILGen/SILGenStmt.cpp b/lib/SILGen/SILGenStmt.cpp index 23078530bd453..6c55b2aecfbd2 100644 --- a/lib/SILGen/SILGenStmt.cpp +++ b/lib/SILGen/SILGenStmt.cpp @@ -504,11 +504,7 @@ preparePackResultInit(SILGenFunction &SGF, SILLocation loc, SmallVectorImpl &cleanups, SmallVectorImpl &inits) { assert(origExpansionType.isPackExpansion()); - SmallVector origComponentTypes; - origExpansionType.forEachPackExpandedComponent( - [&](AbstractionPattern component) { - origComponentTypes.push_back(component); - }); + auto origComponentTypes = origExpansionType.getPackExpandedComponents(); auto loweredPackType = packAddr->getType().castTo(); assert(loweredPackType->getNumElements() == origComponentTypes.size() && From 8bfc18bd31d580fde95b38a6d531af9c40a055a9 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 7 Mar 2023 00:02:25 -0500 Subject: [PATCH 5/7] Generalize the operation to get an opened environment for value operations on a type --- lib/SILGen/SILGenFunction.h | 6 ++++++ lib/SILGen/SILGenPack.cpp | 19 ++++++++----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index fcaba246836b3..acb69bda8d79a 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -2532,6 +2532,12 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction return SGM.getAccessorDeclRef(accessor, F.getResilienceExpansion()); } + /// Given a lowered pack expansion type, produce a generic environment + /// sufficient for doing value operations on it and map the type into + /// the environment. + std::pair + createOpenedElementValueEnvironment(SILType packExpansionTy); + /// Emit a dynamic loop over a single pack-expansion component of a pack. /// /// \param formalPackType - a pack type with the right shape for the diff --git a/lib/SILGen/SILGenPack.cpp b/lib/SILGen/SILGenPack.cpp index 0b5484bc796c7..753f062ca5798 100644 --- a/lib/SILGen/SILGenPack.cpp +++ b/lib/SILGen/SILGenPack.cpp @@ -270,9 +270,10 @@ static bool isPatternInvariantToExpansion(CanType patternType, }); } -static std::pair -deriveOpenedElementTypeForPackExpansion(SILGenModule &SGM, - CanPackExpansionType expansion) { +std::pair +SILGenFunction::createOpenedElementValueEnvironment(SILType expansionTy) { + auto expansion = expansionTy.castTo(); + // If the pattern type is invariant to the expansion, we don't need // to open anything. auto countArchetype = cast(expansion.getCountType()); @@ -300,10 +301,9 @@ void SILGenFunction::emitPartialDestroyPack(SILLocation loc, SILValue packAddr, unsigned componentIndex, SILValue limitWithinComponent) { auto packTy = packAddr->getType().castTo(); - auto packExpansionTy = - cast(packTy->getElementType(componentIndex)); - auto result = deriveOpenedElementTypeForPackExpansion(SGM, packExpansionTy); + auto result = createOpenedElementValueEnvironment( + packTy->getSILElementType(componentIndex)); auto elementEnv = result.first; auto elementTy = result.second; @@ -322,11 +322,8 @@ void SILGenFunction::emitPartialDestroyTuple(SILLocation loc, CanPackType inducedPackType, unsigned componentIndex, SILValue limitWithinComponent) { - auto tupleTy = tupleAddr->getType().castTo(); - auto packExpansionTy = - cast(tupleTy.getElementType(componentIndex)); - - auto result = deriveOpenedElementTypeForPackExpansion(SGM, packExpansionTy); + auto result = createOpenedElementValueEnvironment( + tupleAddr->getType().getTupleElementType(componentIndex)); auto elementEnv = result.first; auto elementTy = result.second; From 6b57f7313902fc30bde0129656853fe28b1d79f3 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 7 Mar 2023 00:04:07 -0500 Subject: [PATCH 6/7] Fix a copy-and-paste mistake blocking some other code from working I hadn't bothered testing that code yet, but Holly had, and she found this; oops. --- lib/SILGen/SILGenPack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SILGen/SILGenPack.cpp b/lib/SILGen/SILGenPack.cpp index 753f062ca5798..59e3c37032ae2 100644 --- a/lib/SILGen/SILGenPack.cpp +++ b/lib/SILGen/SILGenPack.cpp @@ -573,7 +573,7 @@ TuplePackExpansionInitialization::create(SILGenFunction &SGF, CanPackExpansionType TuplePackExpansionInitialization::getLoweredExpansionType() const { - auto loweredTupleTy = TupleAddr->getType().castTo(); + auto loweredTupleTy = TupleAddr->getType().castTo(); auto loweredComponentTy = loweredTupleTy.getElementType(ComponentIndex); return cast(loweredComponentTy); } From 81f11c19abe73e1800b069afaf7bb9a5c9cbecfb Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 7 Mar 2023 03:03:53 -0500 Subject: [PATCH 7/7] Implement the caller side of return types containing variadic packs This is all relatively nicely abstracted, which is not to say that it didn't take an awful lot of plumbing to get it to work. The basic problem here is inherent: we need to do component-specific setup and teardown, and unfortunately in the current representation we have to do that with separate loops and without any dominance relationships. (This is the same thing preventing us from doing borrows in the general case.) The result is that the general case of result emission is to emit every element of the expansion into a temporary tuple (requiring a pack loop before the call to initialize the pack), then process those elements in the body of a second pack loop after the call. And that's terrible enough that we really have to do the work to try to avoid it, which makes all the APIs more complicated. Anyway, most of the way through the basic plumbing for variadic generics now. Next is reabstraction, I think, which I hope will mostly mean fixing bugs in the infrastructure I've already written. --- lib/SILGen/Initialization.h | 32 ++ lib/SILGen/ResultPlan.cpp | 393 ++++++++++++++++++--- lib/SILGen/ResultPlan.h | 20 +- lib/SILGen/SILGenApply.cpp | 7 +- lib/SILGen/SILGenPack.cpp | 53 ++- test/SILGen/variadic-generic-results.swift | 131 +++++++ 6 files changed, 583 insertions(+), 53 deletions(-) diff --git a/lib/SILGen/Initialization.h b/lib/SILGen/Initialization.h index 1734bcc3c489d..c48ec5db14b82 100644 --- a/lib/SILGen/Initialization.h +++ b/lib/SILGen/Initialization.h @@ -122,6 +122,31 @@ class Initialization { llvm_unreachable("Must implement if canPerformPackExpansionInitialization" "returns true"); } + + /// Given that this supports pack expansion initialization, can it + /// perform *in place* pack expansion initialization by producing + /// a pack element of the given type? + /// + /// The dominance relationship gets a little screwed up here; only + /// return true if it's okay for the address to be written into a + /// pack and then initialized later. + virtual bool + canPerformInPlacePackInitialization(GenericEnvironment *env, + SILType eltAddrTy) const { + return false; + } + + /// Given that this supports in-place pack expansion initialization, + /// return the address of the storage. + /// + /// For convenience, the same element type that was accepted before + /// is passed again. + virtual SILValue getAddressForInPlacePackInitialization(SILGenFunction &SGF, + SILLocation loc, + SILType eltAddrTy) { + llvm_unreachable("Must implement if canPerformInPlacePackInitialization" + "returns true"); + } /// Return true if we can get the addresses of elements with the /// 'splitIntoTupleElements' method. Subclasses can override this to @@ -365,6 +390,13 @@ class InPlacePackExpansionInitialization : public Initialization { SILValue indexWithinComponent, llvm::function_ref fn) override; + bool canPerformInPlacePackInitialization(GenericEnvironment *env, + SILType eltAddrTy) const override; + + SILValue getAddressForInPlacePackInitialization(SILGenFunction &SGF, + SILLocation loc, + SILType eltAddrTy) override; + virtual CanPackExpansionType getLoweredExpansionType() const = 0; virtual CleanupHandle enterPartialDestroyCleanup(SILGenFunction &SGF, SILValue indexWithinComponent) = 0; diff --git a/lib/SILGen/ResultPlan.cpp b/lib/SILGen/ResultPlan.cpp index cddc16f9acf3c..dc136773fc369 100644 --- a/lib/SILGen/ResultPlan.cpp +++ b/lib/SILGen/ResultPlan.cpp @@ -36,7 +36,7 @@ class InPlaceInitializationResultPlan final : public ResultPlan { public: InPlaceInitializationResultPlan(Initialization *init) : init(init) {} - RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, + RValue finish(SILGenFunction &SGF, SILLocation loc, ArrayRef &directResults, SILValue bridgedForeignError) override { init->finishInitialization(SGF); @@ -201,7 +201,7 @@ class IndirectOpenedSelfResultPlan final : public ResultPlan { outList.emplace_back(resultBuf); } - RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, + RValue finish(SILGenFunction &SGF, SILLocation loc, ArrayRef &directResults, SILValue bridgedForeignError) override { assert(resultBox && "never emitted temporary?!"); @@ -232,22 +232,21 @@ class IndirectOpenedSelfResultPlan final : public ResultPlan { class ScalarResultPlan final : public ResultPlan { std::unique_ptr temporary; AbstractionPattern origType; + CanType substType; Initialization *init; SILFunctionTypeRepresentation rep; public: ScalarResultPlan(std::unique_ptr &&temporary, - AbstractionPattern origType, Initialization *init, + AbstractionPattern origType, CanType substType, + Initialization *init, SILFunctionTypeRepresentation rep) - : temporary(std::move(temporary)), origType(origType), init(init), - rep(rep) {} + : temporary(std::move(temporary)), origType(origType), + substType(substType), init(init), rep(rep) {} - RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, + RValue finish(SILGenFunction &SGF, SILLocation loc, ArrayRef &directResults, SILValue bridgedForeignError) override { - // Lower the unabstracted result type. - auto &substTL = SGF.getTypeLowering(substType); - // Claim the value: ManagedValue value; @@ -258,6 +257,8 @@ class ScalarResultPlan final : public ResultPlan { temporary->finishInitialization(SGF); value = temporary->getManagedAddress(); + auto &substTL = SGF.getTypeLowering(value.getType()); + // If the value isn't address-only, go ahead and load. if (!substTL.isAddressOnly()) { auto load = substTL.emitLoad(SGF.B, loc, value.forward(SGF), @@ -271,9 +272,17 @@ class ScalarResultPlan final : public ResultPlan { directResults = directResults.slice(1); } + return finish(SGF, loc, value, origType, substType, init, rep); + } + + static RValue finish(SILGenFunction &SGF, SILLocation loc, + ManagedValue value, + AbstractionPattern origType, CanType substType, + Initialization *init, + SILFunctionTypeRepresentation rep) { // Reabstract the value if the types don't match. This can happen // due to either substitution reabstractions or bridging. - SILType loweredResultTy = substTL.getLoweredType(); + SILType loweredResultTy = SGF.getLoweredType(substType); if (value.getType().hasAbstractionDifference(rep, loweredResultTy)) { Conversion conversion = [&] { // Assume that a C-language API doesn't have substitution @@ -343,10 +352,10 @@ class InitValueFromTemporaryResultPlan final : public ResultPlan { : init(init), subPlan(std::move(subPlan)), temporary(std::move(temporary)) {} - RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, + RValue finish(SILGenFunction &SGF, SILLocation loc, ArrayRef &directResults, SILValue bridgedForeignError) override { - RValue subResult = subPlan->finish(SGF, loc, substType, directResults, + RValue subResult = subPlan->finish(SGF, loc, directResults, bridgedForeignError); assert(subResult.isInContext() && "sub-plan didn't emit into context?"); (void)subResult; @@ -375,10 +384,10 @@ class InitValueFromRValueResultPlan final : public ResultPlan { InitValueFromRValueResultPlan(Initialization *init, ResultPlanPtr &&subPlan) : init(init), subPlan(std::move(subPlan)) {} - RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, + RValue finish(SILGenFunction &SGF, SILLocation loc, ArrayRef &directResults, SILValue bridgedForeignError) override { - RValue subResult = subPlan->finish(SGF, loc, substType, directResults, + RValue subResult = subPlan->finish(SGF, loc, directResults, bridgedForeignError); ManagedValue value = std::move(subResult).getAsSingleValue(SGF, loc); @@ -395,14 +404,166 @@ class InitValueFromRValueResultPlan final : public ResultPlan { } }; +/// A result plan which breaks a @pack_out result into some number of +/// components. +class PackExpansionResultPlan : public ResultPlan { + SILValue PackAddr; + SmallVector ComponentPlans; + +public: + PackExpansionResultPlan(ResultPlanBuilder &builder, + SILValue packAddr, + MutableArrayRef inits, + ArrayRef origTypes, + CanTupleEltTypeArrayRef substEltTypes) + : PackAddr(packAddr) { + auto packTy = packAddr->getType().castTo(); + auto formalPackType = + CanPackType::get(packTy->getASTContext(), substEltTypes); + + ComponentPlans.reserve(inits.size()); + for (auto i : indices(inits)) { + auto &init = inits[i]; + auto origType = origTypes[i]; + CanType substEltType = substEltTypes[i]; + + if (isa(substEltType)) { + ComponentPlans.emplace_back( + builder.buildPackExpansionIntoPack(packAddr, formalPackType, i, + init.get(), origType)); + } else { + ComponentPlans.emplace_back( + builder.buildScalarIntoPack(packAddr, formalPackType, i, + init.get(), origType)); + } + } + } + + RValue finish(SILGenFunction &SGF, SILLocation loc, + ArrayRef &directResults, + SILValue bridgedForeignError) override { + for (auto &componentPlan : ComponentPlans) { + auto componentRV = componentPlan->finish(SGF, loc, directResults, + bridgedForeignError); + assert(componentRV.isInContext()); (void) componentRV; + } + return RValue::forInContext(); + } + + void gatherIndirectResultAddrs(SILGenFunction &SGF, SILLocation loc, + SmallVectorImpl &outList) const override { + outList.push_back(PackAddr); + } +}; + +/// A result plan which transforms a pack expansion component. +class PackTransformResultPlan final : public ResultPlan { + /// The address of the pack. The addresses of the tuple elements + /// have been written into the pack elements for the given component. + SILValue PackAddr; + + /// A formal pack type with the same shape as the pack. + CanPackType FormalPackType; + + /// The index of the pack expansion component within the pack. + unsigned ComponentIndex; + + /// An initialization that the expansion elements should be fed into. + Initialization *EmitInto; + + /// The abstraction pattern of the expansion type of the expansion. + AbstractionPattern OrigPatternType; + + SILFunctionTypeRepresentation Rep; + +public: + PackTransformResultPlan(SILValue packAddr, CanPackType formalPackType, + unsigned componentIndex, Initialization *init, + AbstractionPattern origType, + SILFunctionTypeRepresentation rep) + : PackAddr(packAddr), FormalPackType(formalPackType), + ComponentIndex(componentIndex), EmitInto(init), + OrigPatternType(origType), Rep(rep) {} + + void gatherIndirectResultAddrs(SILGenFunction &SGF, SILLocation loc, + SmallVectorImpl &outList) const override { + llvm_unreachable("should not be gathering from an expansion plan"); + } + + RValue finish(SILGenFunction &SGF, SILLocation loc, + ArrayRef &directResults, + SILValue bridgedForeignError) override { + // We opened a generic environment for the loop prior to the call + // which wrote element addresses into the pack. We can't open the + // same environment twice in a function, though, so we need a new + // environment. + auto eltPatternTy = + PackAddr->getType().castTo() + ->getSILElementType(ComponentIndex); + auto result = SGF.createOpenedElementValueEnvironment(eltPatternTy); + auto openedEnv = result.first; + auto eltAddrTy = result.second; + + // Loop over the pack, initializing each value with the appropriate + // element. + SGF.emitDynamicPackLoop(loc, FormalPackType, ComponentIndex, + /*limit*/SILValue(), openedEnv, + /*reverse*/false, + [&](SILValue indexWithinComponent, + SILValue expansionIndex, + SILValue packIndex) { + EmitInto->performPackExpansionInitialization(SGF, loc, + indexWithinComponent, + [&](Initialization *eltInit) { + // Pull the element address out of the pack, which is cheaper + // than re-projecting it from the tuple. + auto eltAddr = + SGF.B.createPackElementGet(loc, packIndex, PackAddr, eltAddrTy); + + // Move the value into the destination. + ManagedValue eltMV = [&] { + auto &eltTL = SGF.getTypeLowering(eltAddrTy); + if (!eltTL.isAddressOnly()) { + auto load = eltTL.emitLoad(SGF.B, loc, eltAddr, + LoadOwnershipQualifier::Take); + eltMV = SGF.emitManagedRValueWithCleanup(load, eltTL); + } else { + eltMV = SGF.emitManagedBufferWithCleanup(eltAddr, eltTL); + } + return eltMV; + }(); + + // Map the formal type into the generic environment. + auto substType = FormalPackType.getElementType(ComponentIndex); + substType = cast(substType).getPatternType(); + if (openedEnv) { + substType = openedEnv->mapContextualPackTypeIntoElementContext( + substType); + } + + // Finish in the normal way for scalar results. + RValue rvalue = + ScalarResultPlan::finish(SGF, loc, eltMV, OrigPatternType, + substType, eltInit, Rep); + assert(rvalue.isInContext()); (void) rvalue; + }); + }); + + EmitInto->finishInitialization(SGF); + return RValue::forInContext(); + } +}; + /// A result plan which produces a larger RValue from a bunch of /// components. class TupleRValueResultPlan final : public ResultPlan { + CanTupleType substType; SmallVector eltPlans; public: TupleRValueResultPlan(ResultPlanBuilder &builder, AbstractionPattern origType, - CanTupleType substType) { + CanTupleType substType) + : substType(substType) { // Create plans for all the elements. eltPlans.reserve(substType->getNumElements()); for (auto i : indices(substType->getElementTypes())) { @@ -412,18 +573,15 @@ class TupleRValueResultPlan final : public ResultPlan { } } - RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, + RValue finish(SILGenFunction &SGF, SILLocation loc, ArrayRef &directResults, SILValue bridgedForeignError) override { RValue tupleRV(substType); // Finish all the component tuples. - auto substTupleType = cast(substType); - assert(substTupleType.getElementTypes().size() == eltPlans.size()); - for (auto i : indices(substTupleType.getElementTypes())) { + for (auto &plan : eltPlans) { RValue eltRV = - eltPlans[i]->finish(SGF, loc, substTupleType.getElementType(i), - directResults, bridgedForeignError); + plan->finish(SGF, loc, directResults, bridgedForeignError); tupleRV.addElement(std::move(eltRV)); } @@ -453,30 +611,45 @@ class TupleInitializationResultPlan final : public ResultPlan { AbstractionPattern origType, CanTupleType substType) : tupleInit(tupleInit) { - // Get the sub-initializations. eltInits = tupleInit->splitIntoTupleElements(builder.SGF, builder.loc, substType, eltInitsBuffer); // Create plans for all the sub-initializations. - eltPlans.reserve(substType->getNumElements()); - for (auto i : indices(substType->getElementTypes())) { - AbstractionPattern origEltType = origType.getTupleElementType(i); - CanType substEltType = substType.getElementType(i); - Initialization *eltInit = eltInits[i].get(); - eltPlans.push_back(builder.build(eltInit, origEltType, substEltType)); + eltPlans.reserve(origType.getNumTupleElements()); + + auto substEltTypes = substType.getElementTypes(); + + size_t nextSubstEltIndex = 0; + + for (auto origEltType : origType.getTupleElementTypes()) { + if (origEltType.isPackExpansion()) { + auto origComponentTypes = origEltType.getPackExpandedComponents(); + auto numComponents = origComponentTypes.size(); + auto i = nextSubstEltIndex; + nextSubstEltIndex += numComponents; + auto componentInits = eltInits.slice(i, numComponents); + auto substComponentTypes = substEltTypes.slice(i, numComponents); + eltPlans.push_back(builder.buildForPackExpansion(componentInits, + origComponentTypes, + substComponentTypes)); + } else { + auto i = nextSubstEltIndex++; + CanType substEltType = substEltTypes[i]; + Initialization *eltInit = eltInits[i].get(); + eltPlans.push_back(builder.build(eltInit, origEltType, substEltType)); + } } + + assert(nextSubstEltIndex == substType->getNumElements()); } - RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, + RValue finish(SILGenFunction &SGF, SILLocation loc, ArrayRef &directResults, SILValue bridgedForeignError) override { - auto substTupleType = cast(substType); - assert(substTupleType.getElementTypes().size() == eltPlans.size()); - for (auto i : indices(substTupleType.getElementTypes())) { - auto eltType = substTupleType.getElementType(i); - RValue eltRV = eltPlans[i]->finish(SGF, loc, eltType, directResults, - bridgedForeignError); + for (auto &plan : eltPlans) { + RValue eltRV = plan->finish(SGF, loc, directResults, + bridgedForeignError); assert(eltRV.isInContext()); (void)eltRV; } @@ -604,7 +777,7 @@ class ForeignAsyncInitializationPlan final : public ResultPlan { breadcrumb = std::move(crumb); } - RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, + RValue finish(SILGenFunction &SGF, SILLocation loc, ArrayRef &directResults, SILValue bridgedForeignError) override { // There should be no direct results from the call. @@ -802,11 +975,10 @@ class ForeignErrorInitializationPlan final : public ResultPlan { subPlan->deferExecutorBreadcrumb(std::move(breadcrumb)); } - RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, + RValue finish(SILGenFunction &SGF, SILLocation loc, ArrayRef &directResults, SILValue bridgedForeignError) override { - return subPlan->finish(SGF, loc, substType, directResults, - bridgedForeignError); + return subPlan->finish(SGF, loc, directResults, bridgedForeignError); } void @@ -920,9 +1092,19 @@ ResultPlanPtr ResultPlanBuilder::build(Initialization *init, return buildForTuple(init, origType, cast(substType)); } + assert(!origType.isPackExpansion() && + "should've been handled when destructuring tuples"); + // Otherwise, grab the next result. auto result = allResults.pop_back_val(); + return buildForScalar(init, origType, substType, result); +} + +ResultPlanPtr ResultPlanBuilder::buildForScalar(Initialization *init, + AbstractionPattern origType, + CanType substType, + SILResultInfo result) { auto calleeTy = calleeTypeInfo.substFnType; // If the result is indirect, and we have an address to emit into, and @@ -965,7 +1147,134 @@ ResultPlanPtr ResultPlanBuilder::build(Initialization *init, } return ResultPlanPtr(new ScalarResultPlan( - std::move(temporary), origType, init, calleeTypeInfo.getOverrideRep())); + std::move(temporary), origType, substType, init, + calleeTypeInfo.getOverrideRep())); +} + +ResultPlanPtr ResultPlanBuilder:: + buildForPackExpansion(MutableArrayRef inits, + ArrayRef origTypes, + CanTupleEltTypeArrayRef substTypes) { + assert(inits.size() == origTypes.size() && + inits.size() == substTypes.size()); + + // Pack expansions in the original result type always turn into + // a single @pack_out result. + auto result = allResults.pop_back_val(); + assert(result.isPack()); + auto packTy = + result.getSILStorageType(SGF.SGM.M, calleeTypeInfo.substFnType, + SGF.getTypeExpansionContext()); + assert(packTy.castTo()->getNumElements() == inits.size()); + + // TODO: try to just forward a single pack + + // Allocate a pack to serve as the element. + auto packAddr = + SGF.emitTemporaryPackAllocation(loc, packTy.getObjectType()); + + return ResultPlanPtr(new PackExpansionResultPlan(*this, packAddr, inits, + origTypes, substTypes)); +} + +ResultPlanPtr +ResultPlanBuilder::buildPackExpansionIntoPack(SILValue packAddr, + CanPackType formalPackType, + unsigned componentIndex, + Initialization *init, + AbstractionPattern origType) { + assert(origType.isPackExpansion()); + assert(init && init->canPerformPackExpansionInitialization()); + + // Create an opened-element environment sufficient for working with + // values of the pack expansion type. + auto packTy = packAddr->getType().castTo(); + auto result = SGF.createOpenedElementValueEnvironment( + packTy->getSILElementType(componentIndex)); + auto openedEnv = result.first; + auto eltTy = result.second; + + // This code would be much easier to write, and more efficient + // dynamically, if we could form packs by pack-applying a coroutine. + // Instead, we have to initialize a tuple if we don't fall into the + // (narrow but important) special case where we can just forward + // addresses into the pack. + + // If the expansion addresses can just be forwarded into the pack, + // we can emit a dynamic loop to do that now. + if (init->canPerformInPlacePackInitialization(openedEnv, eltTy)) { + SGF.emitDynamicPackLoop(loc, formalPackType, componentIndex, + /*limit*/ SILValue(), openedEnv, + /*reverse*/ false, + [&](SILValue indexWithinComponent, + SILValue expansionPackIndex, + SILValue packIndex) { + auto eltAddr = + init->getAddressForInPlacePackInitialization(SGF, loc, eltTy); + SGF.B.createPackElementSet(loc, eltAddr, packIndex, packAddr); + }); + + // The result plan just needs to finish the initialization when + // it's finished. + return ResultPlanPtr(new InPlaceInitializationResultPlan(init)); + } + + // Otherwise, make a tuple temporary and write the element addresses + // into the pack. + auto tupleTy = CanTupleType(TupleType::get( + {packTy->getElementType(componentIndex)}, SGF.getASTContext())); + auto tupleAddr = SGF.emitTemporaryAllocation(loc, + SILType::getPrimitiveObjectType(tupleTy)); + + SGF.emitDynamicPackLoop(loc, formalPackType, componentIndex, + /*limit*/ SILValue(), openedEnv, /*reverse*/ false, + [&](SILValue indexWithinComponent, + SILValue expansionPackIndex, + SILValue packIndex) { + auto eltAddr = SGF.B.createTuplePackElementAddr(loc, expansionPackIndex, + tupleAddr, eltTy); + SGF.B.createPackElementSet(loc, eltAddr, packIndex, packAddr); + }); + + // The result plan will write into `init` during finish(). + origType = origType.getPackExpansionPatternType(); + return ResultPlanPtr( + new PackTransformResultPlan(packAddr, formalPackType, + componentIndex, init, origType, + calleeTypeInfo.getOverrideRep())); +} + +ResultPlanPtr +ResultPlanBuilder::buildScalarIntoPack(SILValue packAddr, + CanPackType formalPackType, + unsigned componentIndex, + Initialization *init, + AbstractionPattern origType) { + assert(!origType.isPackExpansion()); + assert(init); + auto substType = formalPackType.getElementType(componentIndex); + assert(!isa(substType)); + + // Fake up an @out result. + auto loweredEltType = packAddr->getType().castTo() + ->getElementType(componentIndex); + SILResultInfo resultInfo(loweredEltType, ResultConvention::Indirect); + + // Use the normal scalar emission path. + auto plan = buildForScalar(init, origType, substType, resultInfo); + + // Immediately gather the indirect result. + SmallVector indirectResults; + plan->gatherIndirectResultAddrs(SGF, loc, indirectResults); + assert(indirectResults.size() == 1); + auto eltAddr = indirectResults.front(); + + // Write that into the pack. + auto packIndex = + SGF.B.createScalarPackIndex(loc, componentIndex, formalPackType); + SGF.B.createPackElementSet(loc, eltAddr, packIndex, packAddr); + + return plan; } ResultPlanPtr ResultPlanBuilder::buildForTuple(Initialization *init, @@ -991,7 +1300,9 @@ ResultPlanPtr ResultPlanBuilder::buildForTuple(Initialization *init, // If the tuple is address-only, we'll get much better code if we // emit into a single buffer. auto &substTL = SGF.getTypeLowering(substType); - if (substTL.isAddressOnly() && SGF.F.getConventions().useLoweredAddresses()) { + if (substTL.isAddressOnly() && + (substType.containsPackExpansionType() || + SGF.F.getConventions().useLoweredAddresses())) { // Create a temporary. auto temporary = SGF.emitTemporary(loc, substTL); diff --git a/lib/SILGen/ResultPlan.h b/lib/SILGen/ResultPlan.h index 308322f1fbe2c..d936e37a794c4 100644 --- a/lib/SILGen/ResultPlan.h +++ b/lib/SILGen/ResultPlan.h @@ -15,6 +15,7 @@ #include "Callee.h" #include "ExecutorBreadcrumb.h" +#include "Initialization.h" #include "ManagedValue.h" #include "swift/AST/Types.h" #include "swift/Basic/LLVM.h" @@ -38,7 +39,7 @@ class CalleeTypeInfo; /// An abstract class for working with results.of applies. class ResultPlan { public: - virtual RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType, + virtual RValue finish(SILGenFunction &SGF, SILLocation loc, ArrayRef &directResults, SILValue bridgedForeignError) = 0; virtual ~ResultPlan() = default; @@ -84,9 +85,26 @@ struct ResultPlanBuilder { ResultPlanPtr build(Initialization *emitInto, AbstractionPattern origType, CanType substType); + ResultPlanPtr buildForScalar(Initialization *emitInto, + AbstractionPattern origType, + CanType substType, + SILResultInfo result); ResultPlanPtr buildForTuple(Initialization *emitInto, AbstractionPattern origType, CanTupleType substType); + ResultPlanPtr buildForPackExpansion(MutableArrayRef inits, + ArrayRef origTypes, + CanTupleEltTypeArrayRef substTypes); + ResultPlanPtr buildPackExpansionIntoPack(SILValue packAddr, + CanPackType formalPackType, + unsigned componentIndex, + Initialization *init, + AbstractionPattern origType); + ResultPlanPtr buildScalarIntoPack(SILValue packAddr, + CanPackType formalPackType, + unsigned componentIndex, + Initialization *init, + AbstractionPattern origType); static ResultPlanPtr computeResultPlan(SILGenFunction &SGF, const CalleeTypeInfo &calleeTypeInfo, diff --git a/lib/SILGen/SILGenApply.cpp b/lib/SILGen/SILGenApply.cpp index 1d68e8dddf4a1..ce1b755c99a23 100644 --- a/lib/SILGen/SILGenApply.cpp +++ b/lib/SILGen/SILGenApply.cpp @@ -5038,7 +5038,7 @@ CallEmission::applySpecializedEmitter(SpecializedEmitter &specializedEmitter, // Then finish our value. if (resultPlan.has_value()) { return std::move(*resultPlan) - ->finish(SGF, loc, formalResultType, directResultsFinal, SILValue()); + ->finish(SGF, loc, directResultsFinal, SILValue()); } else { return RValue( SGF, *uncurriedLoc, formalResultType, directResultsFinal[0]); @@ -5245,7 +5245,6 @@ RValue SILGenFunction::emitApply( ApplyOptions options, SGFContext evalContext, Optional implicitActorHopTarget) { auto substFnType = calleeTypeInfo.substFnType; - auto substResultType = calleeTypeInfo.substResultType; // Create the result plan. SmallVector indirectResultAddrs; @@ -5497,8 +5496,8 @@ RValue SILGenFunction::emitApply( } auto directResultsArray = makeArrayRef(directResults); - RValue result = resultPlan->finish(*this, loc, substResultType, - directResultsArray, bridgedForeignError); + RValue result = resultPlan->finish(*this, loc, directResultsArray, + bridgedForeignError); assert(directResultsArray.empty() && "didn't claim all direct results"); return result; diff --git a/lib/SILGen/SILGenPack.cpp b/lib/SILGen/SILGenPack.cpp index 59e3c37032ae2..4c7318b98295d 100644 --- a/lib/SILGen/SILGenPack.cpp +++ b/lib/SILGen/SILGenPack.cpp @@ -447,6 +447,26 @@ void SILGenFunction::emitDynamicPackLoop(SILLocation loc, B.emitBlock(endBB); } +/// Given that we're within a dynamic pack loop with the same expansion +/// shape as a pack expansion component of the given formal pack type, +/// produce a pack index for the current component within the formal pack. +/// +/// Note that the *outer* pack index for the dynamic pack loop +/// isn't necessarily correct for the given pack, just the *expansion* +/// pack index. +static SILValue emitPackPackIndexForActiveExpansion(SILGenFunction &SGF, + SILLocation loc, + CanPackType formalPackType, + unsigned componentIndex) { + auto activeExpansion = SGF.getInnermostPackExpansion(); + auto packIndex = activeExpansion->ExpansionIndex; + if (formalPackType->getNumElements() != 1) { + packIndex = SGF.B.createPackPackIndex(loc, componentIndex, packIndex, + formalPackType); + } + return packIndex; +} + void InPlacePackExpansionInitialization:: performPackExpansionInitialization(SILGenFunction &SGF, SILLocation loc, @@ -466,17 +486,14 @@ void InPlacePackExpansionInitialization:: // The pack index from the active pack expansion is just into the // expansion component; wrap it as necessary to index into the larger // pack/tuple element list. - auto activeExpansion = SGF.getInnermostPackExpansion(); - auto packIndex = activeExpansion->ExpansionIndex; - if (FormalPackType->getNumElements() > 1) { - packIndex = SGF.B.createPackPackIndex(loc, ComponentIndex, packIndex, - FormalPackType); - } + auto packIndex = emitPackPackIndexForActiveExpansion(SGF, loc, + FormalPackType, + ComponentIndex); // Translate the pattern type into the environment of the innermost // pack expansion. auto loweredPatternTy = getLoweredExpansionType().getPatternType(); - if (auto env = activeExpansion->OpenedElementEnv) { + if (auto env = SGF.getInnermostPackExpansion()->OpenedElementEnv) { // This AST-level transformation is fine on lowered types because // we're just replacing pack archetypes with element archetypes. loweredPatternTy = @@ -506,6 +523,28 @@ void InPlacePackExpansionInitialization:: } } +bool InPlacePackExpansionInitialization:: + canPerformInPlacePackInitialization(GenericEnvironment *env, + SILType eltAddrTy) const { + auto loweredPatternTy = getLoweredExpansionType().getPatternType(); + if (env) { + loweredPatternTy = + env->mapContextualPackTypeIntoElementContext(loweredPatternTy); + } + + return loweredPatternTy == eltAddrTy.getASTType(); +} + +SILValue InPlacePackExpansionInitialization:: + getAddressForInPlacePackInitialization(SILGenFunction &SGF, + SILLocation loc, + SILType eltAddrTy) { + auto packIndex = emitPackPackIndexForActiveExpansion(SGF, loc, + FormalPackType, + ComponentIndex); + return getElementAddress(SGF, loc, packIndex, eltAddrTy); +} + void InPlacePackExpansionInitialization:: finishInitialization(SILGenFunction &SGF) { if (ExpansionCleanup.isValid()) diff --git a/test/SILGen/variadic-generic-results.swift b/test/SILGen/variadic-generic-results.swift index de9e5d485fd09..8e343def1a053 100644 --- a/test/SILGen/variadic-generic-results.swift +++ b/test/SILGen/variadic-generic-results.swift @@ -1,6 +1,8 @@ // RUN: %target-swift-emit-silgen -enable-experimental-feature VariadicGenerics %s | %FileCheck %s // REQUIRES: asserts +func sequence() {} + func copyOrThrow(value: T) throws -> T { return value } // CHECK-LABEL: @$s4main13copyIntoTupleyxxQp_txxQpRvzlF : $@convention(thin) (@pack_guaranteed Pack{repeat each T}) -> @pack_out Pack{repeat each T} { @@ -81,3 +83,132 @@ func copyOrThrowIntoTuple(_ args: repeat each T) throws -> (repeat each return (repeat try copyOrThrow(value: each args)) } +// CHECK-LABEL: @$s4main22callCopyAndDestructure1a1b1cySi_S2StF +// Set up the result pack. +// CHECK: [[RESULT_PACK:%.*]] = alloc_pack $Pack{Int, String, String} +// CHECK-NEXT: [[R0:%.*]] = alloc_stack $Int +// CHECK-NEXT: [[R0_IDX:%.*]] = scalar_pack_index 0 of $Pack{Int, String, String} +// CHECK-NEXT: pack_element_set [[R0]] : $*Int into [[R0_IDX]] of [[RESULT_PACK]] : $*Pack{Int, String, String} +// CHECK-NEXT: [[R1:%.*]] = alloc_stack $String +// CHECK-NEXT: [[R1_IDX:%.*]] = scalar_pack_index 1 of $Pack{Int, String, String} +// CHECK-NEXT: pack_element_set [[R1]] : $*String into [[R1_IDX]] of [[RESULT_PACK]] : $*Pack{Int, String, String} +// CHECK-NEXT: [[R2:%.*]] = alloc_stack $String +// CHECK-NEXT: [[R2_IDX:%.*]] = scalar_pack_index 2 of $Pack{Int, String, String} +// CHECK-NEXT: pack_element_set [[R2]] : $*String into [[R2_IDX]] of [[RESULT_PACK]] : $*Pack{Int, String, String} +// Set up the argument pack. +// CHECK: [[ARG_PACK:%.*]] = alloc_pack $Pack{Int, String, String} +// CHECK-NEXT: [[ARG0:%.*]] = alloc_stack $Int +// CHECK-NEXT: store %0 to [trivial] [[ARG0]] : $*Int +// CHECK-NEXT: [[ARG0_IDX:%.*]] = scalar_pack_index 0 of $Pack{Int, String, String} +// CHECK-NEXT: pack_element_set [[ARG0]] : $*Int into [[ARG0_IDX]] of [[ARG_PACK]] : $*Pack{Int, String, String} +// CHECK-NEXT: [[ARG1:%.*]] = alloc_stack $String +// CHECK-NEXT: [[COPY1:%.*]] = copy_value %1 : $String +// CHECK-NEXT: store [[COPY1]] to [init] [[ARG1]] : $*String +// CHECK-NEXT: [[ARG1_IDX:%.*]] = scalar_pack_index 1 of $Pack{Int, String, String} +// CHECK-NEXT: pack_element_set [[ARG1]] : $*String into [[ARG1_IDX]] of [[ARG_PACK]] : $*Pack{Int, String, String} +// CHECK-NEXT: [[ARG2:%.*]] = alloc_stack $String +// CHECK-NEXT: [[COPY2:%.*]] = copy_value %2 : $String +// CHECK-NEXT: store [[COPY2]] to [init] [[ARG2]] : $*String +// CHECK-NEXT: [[ARG2_IDX:%.*]] = scalar_pack_index 2 of $Pack{Int, String, String} +// CHECK-NEXT: pack_element_set [[ARG2]] : $*String into [[ARG2_IDX]] of [[ARG_PACK]] : $*Pack{Int, String, String} +// Perform the call. +// CHECK-NEXT: // function_ref +// CHECK-NEXT: [[FN:%.*]] = function_ref @$s4main13copyIntoTupleyxxQp_txxQpRvzlF +// CHECK-NEXT: apply [[FN]]([[RESULT_PACK]], [[ARG_PACK]]) +// Destroy the argument pack. +// CHECK-NEXT: destroy_addr [[ARG2]] : $*String +// CHECK-NEXT: dealloc_stack [[ARG2]] : $*String +// CHECK-NEXT: destroy_addr [[ARG1]] : $*String +// CHECK-NEXT: dealloc_stack [[ARG1]] : $*String +// CHECK-NEXT: dealloc_stack [[ARG0]] : $*Int +// CHECK-NEXT: dealloc_pack [[ARG_PACK]] : $*Pack{Int, String, String} +// Load from the pack and bind the locals. +// CHECK-NEXT: [[A:%.*]] = load [trivial] [[R0]] : $*Int +// CHECK-NEXT: debug_value [[A]] : $Int +// CHECK-NEXT: [[B:%.*]] = load [take] [[R1]] : $*String +// CHECK-NEXT: [[C:%.*]] = load [take] [[R2]] : $*String +// CHECK-NEXT: [[C_BORROW:%.*]] = begin_borrow [lexical] [[C]] : $String +// CHECK-NEXT: debug_value [[C_BORROW]] : $String +// CHECK-NEXT: destroy_value [[B]] : $String +// End of statement. +// CHECK-NEXT: dealloc_stack [[R2]] : $*String +// CHECK-NEXT: dealloc_stack [[R1]] : $*String +// CHECK-NEXT: dealloc_stack [[R0]] : $*Int +// CHECK-NEXT: dealloc_pack [[RESULT_PACK]] : $*Pack{Int, String, String} +// Sequence marker. +// CHECK-NEXT: // function_ref +// CHECK-NEXT: [[SEQUENCE_FN:%.*]] = function_ref @$s4main8sequenceyyF +// CHECK-NEXT: apply [[SEQUENCE_FN]]() +// Leave the function. +// CHECK-NEXT: end_borrow [[C_BORROW]] : $String +// CHECK-NEXT: destroy_value [[C]] : $String +// CHECK-NEXT: [[RET:%.*]] = tuple () +// CHECK-NEXT: return [[RET]] : $() +func callCopyAndDestructure(a: Int, b: String, c: String) { + let (a,_,c) = copyIntoTuple(a, b, c) + sequence() +} + +// CHECK-LABEL: @$s4main15callCopyAndBind4argsyxxQp_tRvzlF +// Set up the result pack to initialize the elements of the tuple +// we're going to bind the local variable to. +// CHECK: [[TUPLE:%.*]] = alloc_stack [lexical] $(repeat each T), let, name "result" +// CHECK-NEXT: [[RESULT_PACK:%.*]] = alloc_pack $Pack{repeat each T} +// CHECK-NEXT: [[ZERO:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Word, 1 +// CHECK-NEXT: [[LEN:%.*]] = pack_length $Pack{repeat each T} +// CHECK-NEXT: br bb1([[ZERO]] : $Builtin.Word) +// CHECK: bb1([[IDX:%.*]] : $Builtin.Word) +// CHECK-NEXT: [[IDX_EQ_LEN:%.*]] = builtin "cmp_eq_Word"([[IDX]] : $Builtin.Word, [[LEN]] : $Builtin.Word) : $Builtin.Int1 +// CHECK-NEXT: cond_br [[IDX_EQ_LEN]], bb3, bb2 +// CHECK: bb2: +// CHECK-NEXT: [[INDEX:%.*]] = dynamic_pack_index [[IDX]] of $Pack{repeat each T} +// CHECK-NEXT: open_pack_element [[INDEX]] of at , shape $T, uuid [[UUID:".*"]] +// CHECK-NEXT: [[ELT_ADDR:%.*]] = tuple_pack_element_addr [[INDEX]] of [[TUPLE]] : $*(repeat each T) as $*@pack_element([[UUID]]) T +// CHECK-NEXT: pack_element_set [[ELT_ADDR]] : $*@pack_element([[UUID]]) T into [[INDEX]] of [[RESULT_PACK]] : $*Pack{repeat each T} +// CHECK-NEXT: [[NEXT_IDX:%.*]] = builtin "add_Word"([[IDX]] : $Builtin.Word, [[ONE]] : $Builtin.Word) : $Builtin.Word +// CHECK-NEXT: br bb1([[NEXT_IDX]] : $Builtin.Word) +// CHECK: bb3: +// Set up the argument pack with copies of the parameter pack. +// CHECK-NEXT: [[ARG_PACK:%.*]] = alloc_pack $Pack{repeat each T} +// CHECK-NEXT: [[ARG_TUPLE:%.*]] = alloc_stack $(repeat each T) +// CHECK-NEXT: [[ZERO:%.*]] = integer_literal $Builtin.Word, 0 +// CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Word, 1 +// CHECK-NEXT: [[LEN:%.*]] = pack_length $Pack{repeat each T} +// CHECK-NEXT: br bb4([[ZERO]] : $Builtin.Word) +// CHECK: bb4([[IDX:%.*]] : $Builtin.Word) +// CHECK-NEXT: [[IDX_EQ_LEN:%.*]] = builtin "cmp_eq_Word"([[IDX]] : $Builtin.Word, [[LEN]] : $Builtin.Word) : $Builtin.Int1 +// CHECK-NEXT: cond_br [[IDX_EQ_LEN]], bb6, bb5 +// CHECK: bb5: +// CHECK-NEXT: [[INDEX:%.*]] = dynamic_pack_index [[IDX]] of $Pack{repeat each T} +// CHECK-NEXT: open_pack_element [[INDEX]] of at , shape $T, uuid [[UUID:".*"]] +// CHECK-NEXT: [[DEST_ADDR:%.*]] = tuple_pack_element_addr [[INDEX]] of [[ARG_TUPLE]] : $*(repeat each T) as $*@pack_element([[UUID]]) T +// CHECK-NEXT: [[SRC_ADDR:%.*]] = pack_element_get [[INDEX]] of %0 : $*Pack{repeat each T} as $*@pack_element([[UUID]]) T +// CHECK-NEXT: copy_addr [[SRC_ADDR]] to [init] [[DEST_ADDR]] : $*@pack_element([[UUID]]) T +// CHECK-NEXT: pack_element_set [[DEST_ADDR]] : $*@pack_element([[UUID]]) T into [[INDEX]] of [[ARG_PACK]] : $*Pack{repeat each T} +// CHECK-NEXT: [[NEXT_IDX:%.*]] = builtin "add_Word"([[IDX]] : $Builtin.Word, [[ONE]] : $Builtin.Word) : $Builtin.Word +// CHECK-NEXT: br bb4([[NEXT_IDX]] : $Builtin.Word) +// CHECK: bb6: +// Perform the call. +// CHECK-NEXT: // function_ref +// CHECK-NEXT: [[FN:%.*]] = function_ref @$s4main13copyIntoTupleyxxQp_txxQpRvzlF +// CHECK-NEXT: apply [[FN]]([[RESULT_PACK]], [[ARG_PACK]]) +// End of statement. +// CHECK-NEXT: destroy_addr [[ARG_TUPLE]] : $*(repeat each T) +// CHECK-NEXT: dealloc_stack [[ARG_TUPLE]] : $*(repeat each T) +// CHECK-NEXT: dealloc_pack [[ARG_PACK]] : $*Pack{repeat each T} +// CHECK-NEXT: dealloc_pack [[RESULT_PACK]] : $*Pack{repeat each T} +// Sequence point. +// CHECK-NEXT: // function_ref +// CHECK-NEXT: [[SEQUENCE_FN:%.*]] = function_ref @$s4main8sequenceyyF +// CHECK-NEXT: apply [[SEQUENCE_FN]]() +// Leave the function. +// CHECK-NEXT: destroy_addr [[TUPLE]] : $*(repeat each T) +// CHECK-NEXT: dealloc_stack [[TUPLE]] : $*(repeat each T) +// CHECK-NEXT: [[RET:%.*]] = tuple () +// CHECK-NEXT: return [[RET]] : $() +func callCopyAndBind(args: repeat each T) { + let result = copyIntoTuple(repeat each args) + sequence() +} +