Skip to content

Commit 8862b1b

Browse files
authored
Merge pull request #64272 from hborla/emit-materialize-pack-expr
[SILGen] Emit `MaterializePackExpr`s.
2 parents ce6dec5 + 3cca788 commit 8862b1b

File tree

8 files changed

+161
-20
lines changed

8 files changed

+161
-20
lines changed

lib/SILGen/SILGenApply.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3793,8 +3793,6 @@ class ArgEmitter {
37933793
auto expansionExpr =
37943794
cast<PackExpansionExpr>(expr->getSemanticsProvidingExpr());
37953795

3796-
SGF.prepareToEmitPackExpansionExpr(expansionExpr);
3797-
37983796
// TODO: try to borrow the existing elements if we can do that within
37993797
// the limitations of the SIL representation.
38003798

lib/SILGen/SILGenExpr.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,8 +1534,6 @@ RValueEmitter::visitPackExpansionExpr(PackExpansionExpr *E,
15341534
assert(init && init->canPerformPackExpansionInitialization() &&
15351535
"cannot emit a PackExpansionExpr without an appropriate context");
15361536

1537-
SGF.prepareToEmitPackExpansionExpr(E);
1538-
15391537
auto type = E->getType()->getCanonicalType();
15401538
assert(isa<PackExpansionType>(type));
15411539
auto formalPackType = CanPackType::get(SGF.getASTContext(), {type});
@@ -1570,7 +1568,8 @@ RValueEmitter::visitPackElementExpr(PackElementExpr *E, SGFContext C) {
15701568

15711569
RValue
15721570
RValueEmitter::visitMaterializePackExpr(MaterializePackExpr *E, SGFContext C) {
1573-
llvm_unreachable("not implemented for MaterializePackExpr");
1571+
// Always emitted through `visitPackElementExpr`.
1572+
llvm_unreachable("materialized pack outside of PackElementExpr");
15741573
}
15751574

15761575
RValue RValueEmitter::visitArchetypeToSuperExpr(ArchetypeToSuperExpr *E,
@@ -6202,8 +6201,6 @@ RValue SILGenFunction::emitPlusZeroRValue(Expr *E) {
62026201

62036202
static void emitIgnoredPackExpansion(SILGenFunction &SGF,
62046203
PackExpansionExpr *E) {
6205-
SGF.prepareToEmitPackExpansionExpr(E);
6206-
62076204
auto expansionType =
62086205
cast<PackExpansionType>(E->getType()->getCanonicalType());
62096206
auto formalPackType = CanPackType::get(SGF.getASTContext(), expansionType);

lib/SILGen/SILGenFunction.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -560,8 +560,17 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
560560
SILValue ExpectedExecutor;
561561

562562
struct ActivePackExpansion {
563-
SILValue ExpansionIndex;
564563
GenericEnvironment *OpenedElementEnv;
564+
SILValue ExpansionIndex;
565+
566+
/// Mapping from temporary pack expressions to their values. These
567+
/// are evaluated once, with their elements projected in a dynamic
568+
/// pack loop.
569+
llvm::SmallDenseMap<MaterializePackExpr *, SILValue>
570+
MaterializedPacks;
571+
572+
ActivePackExpansion(GenericEnvironment *OpenedElementEnv)
573+
: OpenedElementEnv(OpenedElementEnv) {}
565574
};
566575

567576
/// The innermost active pack expansion.
@@ -1819,7 +1828,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
18191828
/// places that will need to support any sort of future feature
18201829
/// where e.g. certain `each` operands need to be evaluated once
18211830
/// for the entire expansion.
1822-
void prepareToEmitPackExpansionExpr(PackExpansionExpr *E) {}
1831+
void prepareToEmitPackExpansionExpr(PackExpansionExpr *E);
18231832

18241833
//
18251834
// Helpers for emitting ApplyExpr chains.

lib/SILGen/SILGenLValue.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3342,6 +3342,24 @@ LValue SILGenLValue::visitPackElementExpr(PackElementExpr *e,
33423342
}
33433343
}
33443344

3345+
if (auto packExpr = dyn_cast<MaterializePackExpr>(refExpr)) {
3346+
auto *activeExpansion = SGF.getInnermostPackExpansion();
3347+
assert(activeExpansion->MaterializedPacks.count(packExpr) &&
3348+
"didn't materialize pack before dynamic pack loop emission");
3349+
3350+
auto elementTy =
3351+
SGF.getLoweredType(substFormalType).getAddressType();
3352+
auto tupleAddr = activeExpansion->MaterializedPacks.find(packExpr);
3353+
auto packIndex = activeExpansion->ExpansionIndex;
3354+
auto elementAddr =
3355+
SGF.B.createTuplePackElementAddr(e, packIndex, tupleAddr->second, elementTy);
3356+
return LValue::forAddress(accessKind,
3357+
ManagedValue::forLValue(elementAddr),
3358+
/*access enforcement*/ None,
3359+
AbstractionPattern(substFormalType),
3360+
substFormalType);
3361+
}
3362+
33453363
SGF.SGM.diagnose(refExpr, diag::not_implemented,
33463364
"emission of 'each' for this kind of expression");
33473365
auto loweredTy = SGF.getLoweredType(substFormalType).getAddressType();

lib/SILGen/SILGenPack.cpp

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "Initialization.h"
1414
#include "Scope.h"
1515
#include "SILGenFunction.h"
16+
#include "swift/AST/ASTWalker.h"
1617
#include "swift/AST/GenericEnvironment.h"
1718

1819
using namespace swift;
@@ -165,8 +166,52 @@ class PartialDestroyRemainingTupleCleanup : public Cleanup {
165166
#endif
166167
}
167168
};
169+
170+
/// An ASTWalker to emit tuple values in `MaterializePackExpr` nodes.
171+
///
172+
/// Materialized packs are emitted inside a pack expansion context before
173+
/// entering the dynamic pack loop so that the values are only evaluated
174+
/// once, rather than at each pack element iteration.
175+
struct MaterializePackEmitter : public ASTWalker {
176+
SILGenFunction &SGF;
177+
178+
MaterializePackEmitter(SILGenFunction &SGF) : SGF(SGF) {}
179+
180+
ASTWalker::PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
181+
using Action = ASTWalker::Action;
182+
183+
// Don't walk into nested pack expansions.
184+
if (isa<PackExpansionExpr>(expr))
185+
return Action::SkipChildren(expr);
186+
187+
if (auto *packExpr = dyn_cast<MaterializePackExpr>(expr)) {
188+
auto *fromExpr = packExpr->getFromExpr();
189+
assert(fromExpr->getType()->is<TupleType>());
190+
191+
auto &lowering = SGF.getTypeLowering(fromExpr->getType());
192+
auto loweredTy = lowering.getLoweredType();
193+
auto tupleAddr = SGF.emitTemporaryAllocation(fromExpr, loweredTy);
194+
auto init = SGF.useBufferAsTemporary(tupleAddr, lowering);
195+
SGF.emitExprInto(fromExpr, init.get());
196+
197+
// Write the tuple value to a side table in the active pack expansion
198+
// to be projected later within the dynamic pack loop.
199+
auto *activeExpansion = SGF.getInnermostPackExpansion();
200+
activeExpansion->MaterializedPacks[packExpr] = tupleAddr;
201+
}
202+
203+
return Action::Continue(expr);
204+
}
205+
};
206+
168207
} // end anonymous namespace
169208

209+
void
210+
SILGenFunction::prepareToEmitPackExpansionExpr(PackExpansionExpr *E) {
211+
MaterializePackEmitter tempPackEmission(*this);
212+
E->getPatternExpr()->walk(tempPackEmission);
213+
}
214+
170215
CleanupHandle SILGenFunction::enterDeallocPackCleanup(SILValue temp) {
171216
assert(temp->getType().isAddress() && "dealloc must have an address type");
172217
assert(temp->getType().is<SILPackType>());
@@ -390,6 +435,17 @@ void SILGenFunction::emitDynamicPackLoop(SILLocation loc,
390435
"cannot reverse with a starting index");
391436
ASTContext &ctx = SGM.getASTContext();
392437

438+
// Save and restore the innermost pack expansion.
439+
ActivePackExpansion activeExpansionRecord = {
440+
openedElementEnv
441+
};
442+
443+
llvm::SaveAndRestore<ActivePackExpansion*>
444+
packExpansionScope(InnermostPackExpansion, &activeExpansionRecord);
445+
446+
if (auto *expansion = loc.getAsASTNode<PackExpansionExpr>())
447+
prepareToEmitPackExpansionExpr(expansion);
448+
393449
auto wordTy = SILType::getBuiltinWordType(ctx);
394450
auto boolTy = SILType::getBuiltinIntegerType(1, ctx);
395451

@@ -461,6 +517,7 @@ void SILGenFunction::emitDynamicPackLoop(SILLocation loc,
461517
// Construct the dynamic pack index into the component.
462518
SILValue packExpansionIndex =
463519
B.createDynamicPackIndex(loc, curIndex, formalDynamicPackType);
520+
getInnermostPackExpansion()->ExpansionIndex = packExpansionIndex;
464521

465522
// If there's an opened element environment, open it here.
466523
if (openedElementEnv) {
@@ -478,14 +535,6 @@ void SILGenFunction::emitDynamicPackLoop(SILLocation loc,
478535
// Emit the loop body in a scope as a convenience, since it's necessary
479536
// to avoid dominance problems anyway.
480537
{
481-
// Save and restore the innermost pack expansion.
482-
ActivePackExpansion activeExpansionRecord = {
483-
packExpansionIndex,
484-
openedElementEnv
485-
};
486-
llvm::SaveAndRestore<ActivePackExpansion*>
487-
packExpansionScope(InnermostPackExpansion, &activeExpansionRecord);
488-
489538
FullExpr scope(Cleanups, CleanupLocation(loc));
490539
emitBody(curIndex, packExpansionIndex, packIndex);
491540
}

lib/SILOptimizer/Mandatory/PMOMemoryUseCollector.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,8 @@ bool ElementUseCollector::collectUses(SILValue Pointer) {
289289
if (auto *si = dyn_cast<StoreInst>(User)) {
290290
if (UI->getOperandNumber() == StoreInst::Dest) {
291291
if (auto tupleType = PointeeType.getAs<TupleType>()) {
292-
if (!tupleType->isEqual(Module.getASTContext().TheEmptyTupleType)) {
292+
if (!tupleType->isEqual(Module.getASTContext().TheEmptyTupleType) &&
293+
!tupleType->containsPackExpansionType()) {
293294
UsesToScalarize.push_back(User);
294295
continue;
295296
}
@@ -325,7 +326,8 @@ bool ElementUseCollector::collectUses(SILValue Pointer) {
325326
// If this is a copy of a tuple, we should scalarize it so that we don't
326327
// have an access that crosses elements.
327328
if (auto tupleType = PointeeType.getAs<TupleType>()) {
328-
if (!tupleType->isEqual(Module.getASTContext().TheEmptyTupleType)) {
329+
if (!tupleType->isEqual(Module.getASTContext().TheEmptyTupleType) &&
330+
!tupleType->containsPackExpansionType()) {
329331
UsesToScalarize.push_back(CAI);
330332
continue;
331333
}

test/Interpreter/variadic_generic_tuples.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,19 @@ tuples.test("makeTuple3") {
5757
expectEqual("(Swift.Int, Swift.Float)", _typeName(makeTuple3(t: Int.self, u: Float.self)))
5858
}
5959

60-
runAllTests()
60+
func makeTuple<each Element>(
61+
_ element: repeat each Element
62+
) -> (repeat each Element) {
63+
return (repeat each element)
64+
}
65+
66+
func expandTupleElements<each T: Equatable>(_ value: repeat each T) {
67+
let values = makeTuple(repeat each value)
68+
_ = (repeat expectEqual(each value, each values.element))
69+
}
70+
71+
tuples.test("expandTuple") {
72+
expandTupleElements(1, "hello", true)
73+
}
74+
75+
runAllTests()

test/SILGen/variadic-generic-tuples.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,56 @@ public struct Container<each T> {
112112
// CHECK-NEXT: return [[RET]] : $()
113113

114114
// CHECK-LABEL: sil {{.*}}@$s4main9ContainerV7storageAA6StoredVyxGxQp_tvM
115+
116+
struct Wrapper<Value> {
117+
let value: Value
118+
}
119+
120+
// CHECK-LABEL: @$s4main17wrapTupleElementsyAA7WrapperVyxGxQp_txxQpRvzlF : $@convention(thin) <each T> (@pack_guaranteed Pack{repeat each T}) -> @pack_out Pack{repeat Wrapper<each T>}
121+
func wrapTupleElements<each T>(_ value: repeat each T) -> (repeat Wrapper<each T>) {
122+
// CHECK: [[RETURN_VAL:%.*]] : $*Pack{repeat Wrapper<each T>}
123+
124+
// CHECK: [[VAR:%.*]] = alloc_stack [lexical] $(repeat each T)
125+
let values = (repeat each value)
126+
127+
// Create a temporary for the 'values' in 'each values.element'
128+
// CHECK: bb3:
129+
// CHECK-NEXT: [[TEMP:%.*]] = alloc_stack $(repeat each T)
130+
// CHECK-NEXT: copy_addr [[VAR]] to [init] [[TEMP]] : $*(repeat each T)
131+
132+
// Integer values for dynamic pack loop
133+
// CHECK-NEXT: [[ZERO:%.*]] = integer_literal $Builtin.Word, 0
134+
// CHECK-NEXT: [[ONE:%.*]] = integer_literal $Builtin.Word, 1
135+
// CHECK-NEXT: [[PACK_LEN:%.*]] = pack_length $Pack{repeat each T}
136+
// CHECK-NEXT: br bb4([[ZERO]] : $Builtin.Word)
137+
138+
// Loop condition
139+
// CHECK: bb4([[INDEX:%.*]] : $Builtin.Word)
140+
// CHECK-NEXT: [[INDEX_EQ_LEN:%.*]] = builtin "cmp_eq_Word"([[INDEX]] : $Builtin.Word, [[PACK_LEN]] : $Builtin.Word) : $Builtin.Int1
141+
// CHECK-NEXT: cond_br [[INDEX_EQ_LEN]], bb6, bb5
142+
143+
// Loop body
144+
// CHECK: bb5:
145+
// CHECK-NEXT: [[CUR_INDEX:%.*]] = dynamic_pack_index [[INDEX]] of $Pack{repeat Wrapper<each T>}
146+
// CHECK-NEXT: open_pack_element [[CUR_INDEX]] of <each T> at <Pack{repeat each T}>, shape $T, uuid [[UUID:".*"]]
147+
// CHECK-NEXT: [[RETURN_VAL_ELT_ADDR:%.*]] = pack_element_get [[CUR_INDEX]] of [[RETURN_VAL]] : $*Pack{repeat Wrapper<each T>} as $*Wrapper<@pack_element([[UUID]]) T>
148+
// CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin Wrapper<@pack_element([[UUID]]) T>.Type
149+
// CHECK-NEXT: [[TUPLE_ELT_ADDR:%.*]] = tuple_pack_element_addr [[CUR_INDEX]] of [[TEMP]] : $*(repeat each T) as $*@pack_element([[UUID]]) T
150+
// CHECK-NEXT: [[INIT_ARG:%.*]] = alloc_stack $@pack_element([[UUID]]) T
151+
// CHECK-NEXT: copy_addr [[TUPLE_ELT_ADDR]] to [init] [[INIT_ARG]] : $*@pack_element([[UUID]]) T
152+
// function_ref Wrapper.init(value:)
153+
// CHECK: [[INIT:%.*]] = function_ref @$s4main7WrapperV5valueACyxGx_tcfC : $@convention(method) <τ_0_0> (@in τ_0_0, @thin Wrapper<τ_0_0>.Type) -> @out Wrapper<τ_0_0>
154+
// CHECK-NEXT: apply [[INIT]]<@pack_element([[UUID]]) T>([[RETURN_VAL_ELT_ADDR]], [[INIT_ARG]], [[METATYPE]]) : $@convention(method) <τ_0_0> (@in τ_0_0, @thin Wrapper<τ_0_0>.Type) -> @out Wrapper<τ_0_0>
155+
// CHECK-NEXT: dealloc_stack [[INIT_ARG]] : $*@pack_element([[UUID]]) T
156+
// CHECK-NEXT: [[NEXT_INDEX:%.*]] = builtin "add_Word"([[INDEX]] : $Builtin.Word, [[ONE]] : $Builtin.Word) : $Builtin.Word
157+
// CHECK-NEXT: br bb4([[NEXT_INDEX]] : $Builtin.Word)
158+
159+
return (repeat Wrapper(value: each values.element))
160+
161+
// CHECK: destroy_addr [[TEMP]] : $*(repeat each T)
162+
// CHECK: dealloc_stack [[TEMP]] : $*(repeat each T)
163+
// CHECK: destroy_addr [[VAR]] : $*(repeat each T)
164+
// CHECK: dealloc_stack [[VAR]] : $*(repeat each T)
165+
// CHECK-NEXT: [[RET:%.*]] = tuple ()
166+
// CHECK-NEXT: return [[RET]] : $()
167+
}

0 commit comments

Comments
 (0)