Skip to content

Commit b187ba0

Browse files
committed
Add support for indirect tuple-type results.
This could happen as a result of specialization or concrete address-only values. For now, it's just tested by SIL unit tests.
1 parent b90007a commit b187ba0

File tree

5 files changed

+104
-38
lines changed

5 files changed

+104
-38
lines changed

include/swift/SIL/ApplySite.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -581,9 +581,9 @@ class FullApplySite : public ApplySite {
581581
}
582582

583583
/// Get the SIL value that represents all of the given call's results. For a
584-
/// single direct result, returns the result. For multiple results, returns a
585-
/// fake tuple value. The tuple has no storage of its own. The real results
586-
/// must be extracted from it.
584+
/// single direct result, returns the actual result. For multiple results,
585+
/// returns a pseudo-result tuple. The tuple has no storage of its own. The
586+
/// real results must be extracted from it.
587587
///
588588
/// For ApplyInst, returns the single-value instruction itself.
589589
///
@@ -592,7 +592,7 @@ class FullApplySite : public ApplySite {
592592
/// For BeginApplyInst, returns an invalid value. For coroutines, there is no
593593
/// single value representing all results. Yielded values are generally
594594
/// handled differently since they have the convention of incoming arguments.
595-
SILValue getPseudoResult() const {
595+
SILValue getResult() const {
596596
switch (getKind()) {
597597
case FullApplySiteKind::ApplyInst:
598598
return SILValue(cast<ApplyInst>(getInstruction()));

lib/SILOptimizer/Mandatory/AddressLowering.cpp

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -174,14 +174,19 @@ cleanupAfterCall(FullApplySite apply,
174174
// that lowers values to storage.
175175
//===----------------------------------------------------------------------===//
176176

177-
/// If \p pseudoResult has multiple results, return the destructure.
178-
static DestructureTupleInst *getCallMultiResult(SILValue pseudoResult) {
179-
if (pseudoResult->getType().is<TupleType>()) {
180-
if (auto *use = pseudoResult->getSingleUse())
181-
return cast<DestructureTupleInst>(use->getUser());
177+
/// If \p pseudoResult represents multiple results and at least one result is
178+
/// used, then return the destructure.
179+
static DestructureTupleInst *getCallDestructure(FullApplySite apply) {
180+
if (apply.getSubstCalleeConv().getNumDirectSILResults() == 1)
181+
return nullptr;
182182

183-
assert(pseudoResult->use_empty() && "pseudo result can't be used");
184-
}
183+
SILValue pseudoResult = apply.getResult();
184+
assert(pseudoResult->getType().is<TupleType>());
185+
if (auto *use = pseudoResult->getSingleUse())
186+
return cast<DestructureTupleInst>(use->getUser());
187+
188+
assert(pseudoResult->use_empty()
189+
&& "pseudo result can only be used by a single destructure_tuple");
185190
return nullptr;
186191
}
187192

@@ -205,19 +210,18 @@ static bool
205210
visitCallResults(FullApplySite apply,
206211
llvm::function_ref<bool(SILValue, SILResultInfo)> visitor) {
207212
auto fnConv = apply.getSubstCalleeConv();
208-
SILValue pseudoResult = apply.getPseudoResult();
209-
if (auto *destructure = getCallMultiResult(pseudoResult)) {
213+
if (auto *destructure = getCallDestructure(apply)) {
210214
return visitCallMultiResults(destructure, fnConv, visitor);
211215
}
212-
return visitor(pseudoResult, *fnConv.getDirectSILResults().begin());
216+
return visitor(apply.getResult(), *fnConv.getDirectSILResults().begin());
213217
}
214218

215219
/// Return true if the given value is either a "fake" tuple that represents all
216220
/// of a call's results or an empty tuple of no results. This may return true
217221
/// for either tuple_inst or a block argument.
218222
static bool isPseudoCallResult(SILValue value) {
219-
if (isa<ApplyInst>(value))
220-
return value->getType().is<TupleType>();
223+
if (auto *apply = dyn_cast<ApplyInst>(value))
224+
return ApplySite(apply).getSubstCalleeConv().getNumDirectSILResults() > 1;
221225

222226
auto *bbArg = dyn_cast<SILPhiArgument>(value);
223227
if (!bbArg)
@@ -227,11 +231,18 @@ static bool isPseudoCallResult(SILValue value) {
227231
if (!term)
228232
return false;
229233

230-
return isa<TryApplyInst>(term) && bbArg->getType().is<TupleType>();
234+
auto *tryApply = dyn_cast<TryApplyInst>(term);
235+
if (!tryApply)
236+
return false;
237+
238+
return ApplySite(tryApply).getSubstCalleeConv().getNumDirectSILResults() > 1;
231239
}
232240

233241
/// Return true if this is a pseudo-return value.
234242
static bool isPseudoReturnValue(SILValue value) {
243+
if (value->getFunction()->getConventions().getNumDirectSILResults() < 2)
244+
return false;
245+
235246
if (auto *tuple = dyn_cast<TupleInst>(value)) {
236247
Operand *singleUse = tuple->getSingleUse();
237248
return singleUse && isa<ReturnInst>(singleUse->getUser());
@@ -261,9 +272,12 @@ static SILValue getTupleStorageValue(Operand *operand) {
261272
if (!singleUse || !isa<ReturnInst>(singleUse->getUser()))
262273
return tuple;
263274

275+
SILFunction *function = tuple->getFunction();
276+
if (function->getConventions().getNumDirectSILResults() < 2)
277+
return tuple;
278+
264279
unsigned resultIdx = tuple->getElementIndex(operand);
265280

266-
SILFunction *function = tuple->getFunction();
267281
auto loweredFnConv = getLoweredFnConv(function);
268282
assert(loweredFnConv.getResults().size() == tuple->getElements().size());
269283

@@ -279,11 +293,11 @@ static SILValue getTupleStorageValue(Operand *operand) {
279293

280294
/// Return the value representing storage for a single return value.
281295
///
282-
/// bb0(%loweredIndirectResult : $*T, ...)
296+
/// bb0(%loweredIndirectResult : $*T, ...) // function entry
283297
/// return %oper
284298
///
285299
/// For %oper, return %loweredIndirectResult
286-
static SILValue getSingleReturnValue(Operand *operand) {
300+
static SILValue getSingleReturnAddress(Operand *operand) {
287301
assert(!isPseudoReturnValue(operand->get()));
288302

289303
auto *function = operand->getParentFunction();
@@ -612,7 +626,7 @@ void OpaqueValueVisitor::visitValue(SILValue value) {
612626

613627
// Canonicalize returned values.
614628
//
615-
// Given:
629+
// Given $() -> @out (T, T):
616630
// %t = def : $(T, T)
617631
// use %t : $(T, T)
618632
// return %t : $(T, T)
@@ -807,7 +821,7 @@ static SILValue getProjectedUseValue(Operand *operand) {
807821

808822
// Return instructions can project into the return value.
809823
case SILInstructionKind::ReturnInst:
810-
return getSingleReturnValue(operand);
824+
return getSingleReturnAddress(operand);
811825
}
812826
return SILValue();
813827
}
@@ -1420,9 +1434,7 @@ AddressMaterialization::materializeProjectionIntoUse(Operand *operand,
14201434
}
14211435
case SILInstructionKind::TupleInst: {
14221436
auto *tupleInst = cast<TupleInst>(user);
1423-
// Function return values.
1424-
if (tupleInst->hasOneUse()
1425-
&& isa<ReturnInst>(tupleInst->use_begin()->getUser())) {
1437+
if (isPseudoReturnValue(tupleInst)) {
14261438
unsigned resultIdx = tupleInst->getElementIndex(operand);
14271439
assert(resultIdx < pass.loweredFnConv.getNumIndirectSILResults());
14281440
// Cannot call getIndirectSILResults here because that API uses the
@@ -1830,9 +1842,8 @@ void ApplyRewriter::convertApplyWithIndirectResults() {
18301842
// Populate newCallArgs.
18311843
makeIndirectArgs(newCallArgs);
18321844

1833-
// Record the original results before potentially removing the apply
1834-
// (try_apply is removed during rewriting).
1835-
auto *destructure = getCallMultiResult(apply.getPseudoResult());
1845+
// Record the original result destructure before deleting a try_apply.
1846+
auto *destructure = getCallDestructure(apply);
18361847

18371848
switch (apply.getKind()) {
18381849
case FullApplySiteKind::ApplyInst: {
@@ -2071,7 +2082,7 @@ void ApplyRewriter::rewriteTryApply(ArrayRef<SILValue> newCallArgs) {
20712082
tryApply->getNormalBB(), tryApply->getErrorBB(),
20722083
tryApply->getApplyOptions(), tryApply->getSpecializationInfo());
20732084

2074-
auto *resultArg = cast<SILArgument>(apply.getPseudoResult());
2085+
auto *resultArg = cast<SILArgument>(apply.getResult());
20752086

20762087
auto replaceTermResult = [&](SILValue newResultVal) {
20772088
SILType resultTy = loweredCalleeConv.getSILResultType(typeCtx);
@@ -2091,8 +2102,6 @@ void ApplyRewriter::rewriteTryApply(ArrayRef<SILValue> newCallArgs) {
20912102

20922103
// Handle a single opaque result value.
20932104
if (pass.valueStorageMap.contains(resultArg)) {
2094-
assert(!resultArg->getType().is<TupleType>());
2095-
20962105
// Storage was materialized by materializeIndirectResultAddress.
20972106
auto &origStorage = pass.valueStorageMap.getStorage(resultArg);
20982107
assert(origStorage.isRewritten);
@@ -2142,7 +2151,7 @@ void ApplyRewriter::rewriteTryApply(ArrayRef<SILValue> newCallArgs) {
21422151
// // no uses of %d1, %d2
21432152
//
21442153
void ApplyRewriter::replaceDirectResults(DestructureTupleInst *oldDestructure) {
2145-
SILValue newPseudoResult = apply.getPseudoResult();
2154+
SILValue newPseudoResult = apply.getResult();
21462155

21472156
DestructureTupleInst *newDestructure = nullptr;
21482157
if (loweredCalleeConv.getNumDirectSILResults() > 1) {
@@ -2950,7 +2959,7 @@ static void rewriteIndirectApply(FullApplySite apply,
29502959
ApplyRewriter(apply, pass).convertApplyWithIndirectResults();
29512960

29522961
if (!apply.getInstruction()->isDeleted()) {
2953-
assert(!getCallMultiResult(apply.getPseudoResult())
2962+
assert(!getCallDestructure(apply)
29542963
&& "replaceDirectResults deletes the destructure");
29552964
pass.deleter.forceDelete(apply.getInstruction());
29562965
}

lib/SILOptimizer/Mandatory/AddressLowering.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ namespace swift {
3838
/// After allocation, before materialization or rewriting, we may have:
3939
///
4040
/// %result_addr = alloc_stack // storage for %result
41-
/// %result = apply () -> @out T
41+
/// %result = apply : $() -> @out T
4242
/// %extract = struct_extact %result // def-projection of %result
4343
///
4444
/// Or, a projection may project into a composing use (use-projection):
4545
///
46-
/// %struct_addr = alloc_stack // storage for %struct
47-
/// %result = apply () -> @out T // use-projection of %struct at operand #0
46+
/// %struct_addr = alloc_stack // storage for %struct
47+
/// %result = apply : $() -> @out T // use-projection of %struct at operand #0
4848
/// %struct = struct %result
4949
///
5050
/// A phi-projection is a use projection that projects its entire value

lib/SILOptimizer/Utils/Generics.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,7 +2242,7 @@ SILFunction *ReabstractionThunkGenerator::createThunk() {
22422242
Arguments.push_back(NewArg);
22432243
}
22442244
FullApplySite ApplySite = createReabstractionThunkApply(Builder);
2245-
SILValue ReturnValue = ApplySite.getPseudoResult();
2245+
SILValue ReturnValue = ApplySite.getResult();
22462246
assert(ReturnValue && "getPseudoResult out of sync with ApplySite?!");
22472247
Builder.createReturn(Loc, ReturnValue);
22482248

@@ -2255,7 +2255,7 @@ SILFunction *ReabstractionThunkGenerator::createThunk() {
22552255

22562256
FullApplySite ApplySite = createReabstractionThunkApply(Builder);
22572257

2258-
SILValue ReturnValue = ApplySite.getPseudoResult();
2258+
SILValue ReturnValue = ApplySite.getResult();
22592259
assert(ReturnValue && "getPseudoResult out of sync with ApplySite?!");
22602260

22612261
if (ReturnValueAddr) {

test/SILOptimizer/address_lowering.sil

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,63 @@ bb0(%0 : @owned $T):
7979
return %0 : $T
8080
}
8181

82+
// This could happen as a result of either partial specialization from
83+
// a single type parameter into a generic tuple, or specialization
84+
// from a single type parameter into a tuple of concrete address-only
85+
// types.
86+
//
87+
// CHECK-LABEL: sil [ossa] @f011_identity_tuple : $@convention(thin) <T> (@in (T, T)) -> @out (T, T) {
88+
// CHECK: bb0(%0 : $*(T, T), %1 : $*(T, T)):
89+
// CHECK: copy_addr [take] %1 to [initialization] %0 : $*(T, T)
90+
// CHECK-LABEL: } // end sil function 'f011_identity_tuple'
91+
sil [ossa] @f011_identity_tuple : $@convention(thin) <T> (@in (T, T)) -> @out (T, T) {
92+
bb0(%0 : @owned $(T, T)):
93+
return %0 : $(T, T)
94+
}
95+
96+
// CHECK-LABEL: sil [ossa] @f012_decompose_tuple_arg : $@convention(thin) <T> (@in (T, T)) -> @out (T, T) {
97+
// CHECK: bb0(%0 : $*(T, T), %1 : $*(T, T)):
98+
// CHECK: [[ARG0:%.*]] = tuple_element_addr %1 : $*(T, T), 0
99+
// CHECK: [[ARG1:%.*]] = tuple_element_addr %1 : $*(T, T), 1
100+
// CHECK: [[RET0:%.*]] = tuple_element_addr %0 : $*(T, T), 0
101+
// CHECK: apply %{{.*}}<T>([[RET0]], [[ARG0]]) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0
102+
// CHECK: [[RET1:%.*]] = tuple_element_addr %0 : $*(T, T), 1
103+
// CHECK: copy_addr [take] [[ARG1]] to [initialization] [[RET1]] : $*T
104+
// CHECK-LABEL: } // end sil function 'f012_decompose_tuple_arg'
105+
sil [ossa] @f012_decompose_tuple_arg : $@convention(thin) <T> (@in (T, T)) -> @out (T, T) {
106+
bb0(%0 : @owned $(T, T)):
107+
(%arg0, %arg1) = destructure_tuple %0 : $(T, T)
108+
%f = function_ref @f010_addrlower_identity : $@convention(thin) <T> (@in T) -> @out T
109+
%call = apply %f<T>(%arg0) : $@convention(thin) <τ_0_0> (@in τ_0_0) -> @out τ_0_0
110+
%result = tuple (%call : $T, %arg1 : $T)
111+
return %result : $(T, T)
112+
}
113+
114+
// CHECK-LABEL: sil [ossa] @f013_pass_tuple_arg : $@convention(thin) <T> (@in T) -> @out T {
115+
// CHECK: bb0(%0 : $*T, %1 : $*T):
116+
// CHECK: [[IN:%.*]] = alloc_stack $(T, T)
117+
// CHECK: [[OUT:%.*]] = alloc_stack $(T, T)
118+
// CHECK: [[IN1:%.*]] = tuple_element_addr [[IN]] : $*(T, T), 1
119+
// CHECK: copy_addr %1 to [initialization] [[IN1]] : $*T
120+
// CHECK: [[IN0:%.*]] = tuple_element_addr %2 : $*(T, T), 0
121+
// CHECK: copy_addr [take] %1 to [initialization] [[IN0]] : $*T
122+
// CHECK: apply %{{.*}}<T>([[OUT]], [[IN]]) : $@convention(thin) <τ_0_0> (@in (τ_0_0, τ_0_0)) -> @out (τ_0_0, τ_0_0)
123+
// CHECK: [[RET:%.*]] = tuple_element_addr [[OUT]] : $*(T, T), 0
124+
// CHECK: [[DEAD:%.*]] = tuple_element_addr [[OUT]] : $*(T, T), 1
125+
// CHECK: destroy_addr [[DEAD]] : $*T
126+
// CHECK: copy_addr [take] [[RET]] to [initialization] %0 : $*T
127+
// CHECK-LABEL: } // end sil function 'f013_pass_tuple_arg'
128+
sil [ossa] @f013_pass_tuple_arg : $@convention(thin) <T> (@in T) -> @out T {
129+
bb0(%0 : @owned $T):
130+
%copy0 = copy_value %0 : $T
131+
%arg = tuple (%0 : $T, %copy0 : $T)
132+
%f = function_ref @f011_identity_tuple : $@convention(thin) <T> (@in (T, T)) -> @out (T, T)
133+
%call = apply %f<T>(%arg) : $@convention(thin) <T> (@in (T, T)) -> @out (T, T)
134+
(%call0, %call1) = destructure_tuple %call : $(T, T)
135+
destroy_value %call1 : $T
136+
return %call0 : $T
137+
}
138+
82139
// CHECK-LABEL: sil [ossa] @f020_multiResult : $@convention(thin) <T> (@in T) -> (@out T, @out T, @out T) {
83140
// CHECK: %0 "$return_value"
84141
// CHECK: %1 "$return_value"

0 commit comments

Comments
 (0)