Skip to content

Commit 332750d

Browse files
committed
[SILGen] Handle @out tuple expansion.
When branching to the exit block, flatten an @out tuple return value into its components, as is done for all other return values. In the exit block, when constructing the @out tuple to return, visit the tuple-type-tree of the return value to reconstruct the nested tuple structure: @out tuple returns are not flattened, unlike regular tuple returns.
1 parent 24c4c61 commit 332750d

File tree

4 files changed

+253
-4
lines changed

4 files changed

+253
-4
lines changed

include/swift/SIL/SILFunctionConventions.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@ class SILFunctionConventions {
256256
: funcTy->getNumResults();
257257
}
258258

259+
/// Like getNumDirectSILResults but @out tuples, which are not flattened in
260+
/// the return type, are recursively flattened in the count.
261+
template <bool _ = false>
262+
unsigned getNumExpandedDirectSILResults(TypeExpansionContext context) const;
263+
259264
struct DirectSILResultFilter {
260265
bool loweredAddresses;
261266
DirectSILResultFilter(bool loweredAddresses)
@@ -429,6 +434,34 @@ SILFunctionConventions::getDirectSILResultTypes(
429434
SILResultTypeFunc(*this, context));
430435
}
431436

437+
template <bool _>
438+
unsigned SILFunctionConventions::getNumExpandedDirectSILResults(
439+
TypeExpansionContext context) const {
440+
if (silConv.loweredAddresses)
441+
return funcTy->getNumDirectFormalResults();
442+
unsigned retval = 0;
443+
// Worklist of elements to flatten or count.
444+
SmallVector<SILType, 4> flattenedElements;
445+
// Seed the worklist with the direct results.
446+
for (auto result : getDirectSILResultTypes(context)) {
447+
flattenedElements.push_back(result);
448+
}
449+
// Pop elements from the worklist and either increment the count or, if the
450+
// type is a tuple, add its elements to the worklist.
451+
while (!flattenedElements.empty()) {
452+
auto ty = flattenedElements.pop_back_val();
453+
if (auto tupleType = ty.getASTType()->getAs<TupleType>()) {
454+
for (auto index :
455+
llvm::reverse(indices(tupleType->getElementTypes()))) {
456+
flattenedElements.push_back(ty.getTupleElementType(index));
457+
}
458+
} else {
459+
retval += 1;
460+
}
461+
}
462+
return retval;
463+
}
464+
432465
struct SILFunctionConventions::SILParameterTypeFunc {
433466
SILFunctionConventions silConv;
434467
TypeExpansionContext context;

lib/SILGen/SILGenEpilog.cpp

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "ASTVisitor.h"
1314
#include "SILGen.h"
1415
#include "SILGenFunction.h"
15-
#include "ASTVisitor.h"
16+
#include "swift/AST/Types.h"
1617
#include "swift/SIL/SILArgument.h"
18+
#include "llvm/ADT/STLExtras.h"
1719

1820
using namespace swift;
1921
using namespace Lowering;
@@ -33,7 +35,28 @@ void SILGenFunction::prepareEpilog(Optional<Type> directResultType,
3335
for (auto directResult : fnConv.getDirectSILResults()) {
3436
SILType resultType = F.getLoweredType(F.mapTypeIntoContext(
3537
fnConv.getSILType(directResult, getTypeExpansionContext())));
36-
epilogBB->createPhiArgument(resultType, OwnershipKind::Owned);
38+
// @out tuples do not get flattened in the function's return type, but
39+
// the epilog block expects (recursively) flattened arguments. Flatten
40+
// the type now.
41+
SmallVector<SILType, 4> worklist;
42+
worklist.push_back(resultType);
43+
while (!worklist.empty()) {
44+
auto ty = worklist.pop_back_val();
45+
if (auto tupleType = ty.getASTType()->getAs<TupleType>()) {
46+
assert(!fnConv.useLoweredAddresses() &&
47+
"expanding tuple in non-opaque-values exit block?!");
48+
// Push tuple elements in reverse order (resulting in later tuple
49+
// elements appearing earlier in worklist) so that as the worklist
50+
// is drained by popping the back, arguments are created for the
51+
// earlier types first.
52+
for (auto index :
53+
llvm::reverse(indices(tupleType->getElementTypes()))) {
54+
worklist.push_back(ty.getTupleElementType(index));
55+
}
56+
} else {
57+
epilogBB->createPhiArgument(ty, OwnershipKind::Owned);
58+
}
59+
}
3760
}
3861
}
3962
}
@@ -61,15 +84,84 @@ void SILGenFunction::prepareCoroutineUnwindEpilog(CleanupLocation cleanupLoc) {
6184
CoroutineUnwindDest = JumpDest(unwindBB, getCleanupsDepth(), cleanupLoc);
6285
}
6386

87+
/// View a given SILType as a type-tree under the operation of tupling and visit
88+
/// its nodes (tuple elements) in post-order.
89+
///
90+
/// For convenience, the index of the type in the flattened tuple is passed to
91+
/// the visitor.
92+
template <typename Visit>
93+
void visitTupleTypeTreeInPostOrder(SILType root, Visit visit) {
94+
struct Node {
95+
SILType ty;
96+
unsigned index;
97+
};
98+
SmallVector<std::pair<Node, unsigned>, 32> stack;
99+
auto tupleElementCount = [](SILType ty) -> unsigned {
100+
if (auto tupleType = ty.getASTType()->getAs<TupleType>())
101+
return tupleType->getNumElements();
102+
return 0;
103+
};
104+
auto tupleElement = [](SILType ty, unsigned index) -> SILType {
105+
return ty.getTupleElementType(index);
106+
};
107+
unsigned flattenedIndex = 0;
108+
stack.push_back({{root, flattenedIndex}, 0});
109+
while (!stack.empty()) {
110+
while (stack.back().second != tupleElementCount(stack.back().first.ty)) {
111+
auto index = stack.back().second;
112+
stack.back().second++;
113+
stack.push_back(
114+
{{tupleElement(stack.back().first.ty, index), flattenedIndex}, 0});
115+
}
116+
auto node = stack.pop_back_val().first;
117+
visit(node.ty, node.index);
118+
if (!node.ty.getASTType()->template is<TupleType>())
119+
flattenedIndex += 1;
120+
}
121+
}
122+
64123
/// Given a list of direct results, form the direct result value.
65124
///
66125
/// Note that this intentionally loses any tuple sub-structure of the
67-
/// formal result type.
126+
/// formal result type, except in the case of @out tuples where it must be
127+
/// preserved.
68128
static SILValue buildReturnValue(SILGenFunction &SGF, SILLocation loc,
69129
ArrayRef<SILValue> directResults) {
70130
if (directResults.size() == 1)
71131
return directResults[0];
72132

133+
auto fnConv = SGF.F.getConventions();
134+
if (!fnConv.useLoweredAddresses()) {
135+
// In opaque-values code, nested @out enums are not flattened. Reconstruct
136+
// nested tuples.
137+
auto resultType = SGF.F.getLoweredType(SGF.F.mapTypeIntoContext(
138+
fnConv.getSILResultType(SGF.getTypeExpansionContext())));
139+
SmallVector<Optional<SILValue>, 4> mutableDirectResult;
140+
for (auto result : directResults) {
141+
mutableDirectResult.push_back({result});
142+
}
143+
visitTupleTypeTreeInPostOrder(resultType, [&](SILType ty, unsigned index) {
144+
if (auto tupleTy = ty.getASTType()->getAs<TupleType>()) {
145+
SmallVector<SILValue, 4> elements;
146+
unsigned offset = 0;
147+
auto elementCount = tupleTy->getNumElements();
148+
while (elements.size() < elementCount) {
149+
if (mutableDirectResult[index + offset].hasValue()) {
150+
auto val = mutableDirectResult[index + offset].getValue();
151+
elements.push_back(val);
152+
mutableDirectResult[index + offset].reset();
153+
}
154+
++offset;
155+
}
156+
assert(!mutableDirectResult[index].hasValue());
157+
auto tuple = SGF.B.createTuple(loc, ty, elements);
158+
mutableDirectResult[index] = tuple;
159+
}
160+
});
161+
assert(mutableDirectResult[0].hasValue());
162+
return mutableDirectResult[0].getValue();
163+
}
164+
73165
SmallVector<TupleTypeElt, 4> eltTypes;
74166
for (auto elt : directResults)
75167
eltTypes.push_back(elt->getType().getASTType());
@@ -195,7 +287,9 @@ SILGenFunction::emitEpilogBB(SILLocation topLevel) {
195287
// block.
196288
SILValue returnValue;
197289
if (!directResults.empty()) {
198-
assert(directResults.size() == F.getConventions().getNumDirectSILResults());
290+
assert(directResults.size() ==
291+
F.getConventions().getNumExpandedDirectSILResults(
292+
getTypeExpansionContext()));
199293
returnValue = buildReturnValue(*this, topLevel, directResults);
200294
}
201295

test/SILGen/opaque_values_silgen.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,3 +478,24 @@ enum TestEnum<T> {
478478
}
479479
}
480480

481+
// Verify exit block arguments are ordered correctly.
482+
//
483+
// CHECK-LABEL: sil private [ossa] @$s20opaque_values_silgen19duplicate_with_int49condition5valueSi_S2ix_xttSb_xtlFSi_S2ix_xttyXEfU_ : {{.*}} {
484+
// CHECK: br [[EPILOG:bb[0-9]+]]({{%[^,]+}} : $Int, {{%[^,]+}} : $Int, {{%[^,]+}} : $Int, {{%[^,]+}} : $Value, {{%[^,]+}} : $Value)
485+
// CHECK: br [[EPILOG]]({{%[^,]+}} : $Int, {{%[^,]+}} : $Int, {{%[^,]+}} : $Int, {{%[^,]+}} : $Value, {{%[^,]+}} : $Value)
486+
// CHECK: [[EPILOG]]({{%[^,]+}} : $Int, {{%[^,]+}} : $Int, {{%[^,]+}} : $Int, {{%[^,]+}} : @owned $Value, {{%[^,]+}} : @owned $Value):
487+
// CHECK-LABEL: } // end sil function '$s20opaque_values_silgen19duplicate_with_int49condition5valueSi_S2ix_xttSb_xtlFSi_S2ix_xttyXEfU_'
488+
func doit<T>(_ f: () -> T) -> T {
489+
f()
490+
}
491+
@_silgen_name("duplicate_with_int4")
492+
func duplicate_with_int4<Value>(condition: Bool, value: Value) -> (Int, Int, Int, (Value, Value)) {
493+
doit {
494+
if condition {
495+
return (Int(), Int(), Int(), (value, value))
496+
} else {
497+
return (Int(), Int(), Int(), (value, value))
498+
}
499+
}
500+
}
501+

test/SILOptimizer/opaque_values_Onone.swift

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,104 @@ enum Maybe2<T : Equatable> {
6262
}
6363
}
6464
}
65+
66+
func doit<T>(_ f: () -> T) -> T {
67+
f()
68+
}
69+
// CHECK-LABEL: sil hidden @duplicate1 : {{.*}} {
70+
// CHECK: {{bb[0-9]+}}({{%[^,]+}} : $*Value, {{%[^,]+}} : $*Value, [[INSTANCE_ADDR_IN:%[^,]+]] : $*Value):
71+
// CHECK: [[INSTANCE_ADDR:%[^,]+]] = alloc_stack $Value
72+
// CHECK: [[OUTPUT_TUPLE_ADDR:%[^,]+]] = alloc_stack $(Value, Value)
73+
// CHECK: [[DUPLICATE_CLOSURE:%[^,]+]] = function_ref @$s19opaque_values_Onone10duplicate15valuex_xtx_tlFx_xtyXEfU_
74+
// CHECK: copy_addr [[INSTANCE_ADDR_IN]] to [init] [[INSTANCE_ADDR]]
75+
// CHECK: [[DUPLICATE_INSTANCE_CLOSURE:%[^,]+]] = partial_apply [callee_guaranteed] [on_stack] [[DUPLICATE_CLOSURE]]<Value>([[INSTANCE_ADDR]])
76+
// CHECK: [[DEPENDENDENCY:%[^,]+]] = mark_dependence [[DUPLICATE_INSTANCE_CLOSURE]] : $@noescape @callee_guaranteed () -> @out (Value, Value) on [[INSTANCE_ADDR]] : $*Value
77+
// CHECK: [[CONVERTED:%[^,]+]] = convert_function [[DEPENDENDENCY]]
78+
// CHECK: apply {{%[^,]+}}<(Value, Value)>([[OUTPUT_TUPLE_ADDR]], [[CONVERTED]])
79+
// CHECK-LABEL: } // end sil function 'duplicate1'
80+
// CHECK-LABEL: sil private @$s19opaque_values_Onone10duplicate15valuex_xtx_tlFx_xtyXEfU_ : {{.*}} {
81+
// CHECK: {{bb[0-9]+}}([[TUPLE_ADDR:%[^,]+]] : $*(Value, Value), [[VALUE_ADDR:%[^,]+]] :
82+
// CHECK: [[ELT_1_ADDR:%[^,]+]] = tuple_element_addr [[TUPLE_ADDR]]{{.*}}, 0
83+
// CHECK: copy_addr [[VALUE_ADDR]] to [init] [[ELT_1_ADDR]]
84+
// CHECK: [[ELT_2_ADDR:%[^,]+]] = tuple_element_addr [[TUPLE_ADDR]]{{.*}}, 1
85+
// CHECK: copy_addr [[VALUE_ADDR]] to [init] [[ELT_2_ADDR]] : $*Value
86+
// CHECK-LABEL: } // end sil function '$s19opaque_values_Onone10duplicate15valuex_xtx_tlFx_xtyXEfU_'
87+
@_silgen_name("duplicate1")
88+
func duplicate1<Value>(value: Value) -> (Value, Value) {
89+
doit {
90+
(value, value)
91+
}
92+
}
93+
// CHECK-LABEL: sil hidden @duplicate2 : {{.*}} {
94+
// CHECK: {{bb[0-9]+}}({{%[^,]+}} : $*Value, {{%[^,]+}} : $*Value, [[INSTANCE_ADDR_IN:%[^,]+]] : $*Value):
95+
// CHECK: [[INSTANCE_ADDR:%[^,]+]] = alloc_stack $Value
96+
// CHECK: [[OUTPUT_TUPLE_ADDR:%[^,]+]] = alloc_stack $(one: Value, two: Value)
97+
// CHECK: [[DUPLICATE_CLOSURE:%[^,]+]] = function_ref @$s19opaque_values_Onone10duplicate25valuex3one_x3twotx_tlFxAD_xAEtyXEfU_
98+
// CHECK: copy_addr [[INSTANCE_ADDR_IN]] to [init] [[INSTANCE_ADDR]]
99+
// CHECK: [[DUPLICATE_INSTANCE_CLOSURE:%[^,]+]] = partial_apply [callee_guaranteed] [on_stack] [[DUPLICATE_CLOSURE]]<Value>([[INSTANCE_ADDR]])
100+
// CHECK: [[DEPENDENDENCY:%[^,]+]] = mark_dependence [[DUPLICATE_INSTANCE_CLOSURE]] : $@noescape @callee_guaranteed () -> @out (one: Value, two: Value) on [[INSTANCE_ADDR]] : $*Value
101+
// CHECK: [[CONVERTED:%[^,]+]] = convert_function [[DEPENDENDENCY]]
102+
// CHECK: apply {{%[^,]+}}<(one: Value, two: Value)>([[OUTPUT_TUPLE_ADDR]], [[CONVERTED]])
103+
// CHECK-LABEL: } // end sil function 'duplicate2'
104+
// CHECK-LABEL: sil private @$s19opaque_values_Onone10duplicate25valuex3one_x3twotx_tlFxAD_xAEtyXEfU_ : {{.*}} {
105+
// CHECK: {{bb[0-9]+}}([[TUPLE_ADDR:%[^,]+]] : $*(one: Value, two: Value), [[VALUE_ADDR:%[^,]+]] :
106+
// CHECK: [[ELT_1_ADDR:%[^,]+]] = tuple_element_addr [[TUPLE_ADDR]]{{.*}}, 0
107+
// CHECK: copy_addr [[VALUE_ADDR]] to [init] [[ELT_1_ADDR]]
108+
// CHECK: [[ELT_2_ADDR:%[^,]+]] = tuple_element_addr [[TUPLE_ADDR]]{{.*}}, 1
109+
// CHECK: copy_addr [[VALUE_ADDR]] to [init] [[ELT_2_ADDR]] : $*Value
110+
// CHECK-LABEL: } // end sil function '$s19opaque_values_Onone10duplicate25valuex3one_x3twotx_tlFxAD_xAEtyXEfU_'
111+
@_silgen_name("duplicate2")
112+
func duplicate2<Value>(value: Value) -> (one: Value, two: Value) {
113+
doit {
114+
(one: value, two: value)
115+
}
116+
}
117+
@_silgen_name("duplicate_with_int1")
118+
func duplicate_with_int1<Value>(value: Value) -> (Value, Value, Int) {
119+
doit {
120+
(value, value, 42)
121+
}
122+
}
123+
@_silgen_name("duplicate_with_int2")
124+
func duplicate_with_int2<Value>(value: Value) -> ((Value, Value), Int) {
125+
doit {
126+
((value, value), 42)
127+
}
128+
}
129+
130+
// CHECK-LABEL: sil hidden @duplicate_with_int3 : {{.*}} {
131+
// CHECK: {{bb[0-9]+}}({{%[^,]+}} : $*Value, {{%[^,]+}} : $*Value, {{%[^,]+}} : $*Value, {{%[^,]+}} : $*Value, [[INSTANCE_ADDR_IN:%[^,]+]] : $*Value):
132+
// CHECK: [[INSTANCE_ADDR:%[^,]+]] = alloc_stack $Value
133+
// CHECK: [[CLOSURE:%[^,]+]] = function_ref @$s19opaque_values_Onone19duplicate_with_int35valueSi_x_x_x_SitxttSitx_tlFSi_x_x_x_SitxttSityXEfU_
134+
// CHECK: copy_addr [[INSTANCE_ADDR_IN]] to [init] [[INSTANCE_ADDR]]
135+
// CHECK: [[INSTANCE_CLOSURE:%[^,]+]] = partial_apply [callee_guaranteed] [on_stack] [[CLOSURE]]<Value>([[INSTANCE_ADDR]])
136+
// CHECK: [[DEPENDENCY:%[^,]+]] = mark_dependence [[INSTANCE_CLOSURE]]
137+
// CHECK: [[CONVERTED:%[^,]+]] = convert_function [[DEPENDENCY]]
138+
// CHECK: apply {{%[^,]+}}<(Int, (Value, (Value, (Value, Int), Value)), Int)>({{%[^,]+}}, [[CONVERTED]])
139+
// CHECK-LABEL: } // end sil function 'duplicate_with_int3'
140+
// CHECK-LABEL: sil private @$s19opaque_values_Onone19duplicate_with_int35valueSi_x_x_x_SitxttSitx_tlFSi_x_x_x_SitxttSityXEfU_ {{.*}} {
141+
// CHECK: {{bb[0-9]+}}([[OUT_ADDR:%[^,]+]] : $*(Int, (Value, (Value, (Value, Int), Value)), Int), [[IN_ADDR:%[^,]+]] : $*Value):
142+
// CHECK: [[OUT_1_ADDR:%[^,]+]] = tuple_element_addr [[OUT_ADDR]] : $*(Int, (Value, (Value, (Value, Int), Value)), Int), 1
143+
// CHECK: [[OUT_1_0_ADDR:%[^,]+]] = tuple_element_addr [[OUT_1_ADDR]] : $*(Value, (Value, (Value, Int), Value)), 0
144+
// CHECK: copy_addr [[IN_ADDR]] to [init] [[OUT_1_0_ADDR]]
145+
// CHECK: [[OUT_1_1_ADDR:%[^,]+]] = tuple_element_addr [[OUT_1_ADDR]] : $*(Value, (Value, (Value, Int), Value)), 1
146+
// CHECK: [[OUT_1_1_0_ADDR:%[^,]+]] = tuple_element_addr [[OUT_1_1_ADDR]] : $*(Value, (Value, Int), Value), 0
147+
// CHECK: copy_addr [[IN_ADDR]] to [init] [[OUT_1_1_0_ADDR]]
148+
// CHECK: [[OUT_1_1_1_ADDR:%[^,]+]] = tuple_element_addr [[OUT_1_1_ADDR]] : $*(Value, (Value, Int), Value), 1
149+
// CHECK: [[OUT_1_1_1_2_ADDR:%[^,]+]] = tuple_element_addr [[OUT_1_1_1_ADDR]] : $*(Value, Int), 0
150+
// CHECK: copy_addr [[IN_ADDR]] to [init] [[OUT_1_1_1_2_ADDR]]
151+
// CHECK: [[OUT_1_1_2_ADDR:%[^,]+]] = tuple_element_addr [[OUT_1_1_ADDR]] : $*(Value, (Value, Int), Value), 2
152+
// CHECK: copy_addr [[IN_ADDR]] to [init] [[OUT_1_1_2_ADDR]]
153+
// CHECK: [[OUT_1_1_1_1_ADDR:%[^,]+]] = tuple_element_addr [[OUT_1_1_1_ADDR]] : $*(Value, Int), 1
154+
// CHECK: store {{%[^,]+}} to [[OUT_1_1_1_1_ADDR]]
155+
// CHECK: [[OUT_2_ADDR:%[^,]+]] = tuple_element_addr [[OUT_ADDR]] : $*(Int, (Value, (Value, (Value, Int), Value)), Int), 0
156+
// CHECK: store {{%[^,]+}} to [[OUT_2_ADDR]]
157+
// CHECK: [[OUT_2_ADDR:%[^,]+]] = tuple_element_addr [[OUT_ADDR]] : $*(Int, (Value, (Value, (Value, Int), Value)), Int), 2
158+
// CHECK: store {{%[^,]+}} to [[OUT_2_ADDR]]
159+
// CHECK-LABEL: } // end sil function '$s19opaque_values_Onone19duplicate_with_int35valueSi_x_x_x_SitxttSitx_tlFSi_x_x_x_SitxttSityXEfU_'
160+
@_silgen_name("duplicate_with_int3")
161+
func duplicate_with_int3<Value>(value: Value) -> (Int, (Value, (Value, (Value, Int), Value)), Int) {
162+
doit {
163+
(42, (value, (value, (value, 43), value)), 44)
164+
}
165+
}

0 commit comments

Comments
 (0)