Skip to content

Commit f931727

Browse files
Merge pull request #61846 from nate-chandler/opaque-values/1/20221027
[OpaqueValues] Handle PartialApplyInsts and @out tuples.
2 parents bf3a30e + b0bcc75 commit f931727

File tree

7 files changed

+288
-34
lines changed

7 files changed

+288
-34
lines changed

include/swift/SIL/ApplySite.h

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,29 @@ class ApplySite {
474474
llvm_unreachable("covered switch");
475475
}
476476

477+
/// If this is a terminator apply site, then pass a builder to insert at the
478+
/// first instruction of each successor to \p func. Otherwise, pass a builder
479+
/// to insert at std::next(Inst).
480+
///
481+
/// The intention is that this abstraction will enable the compiler writer to
482+
/// ignore whether or not an apply site is a terminator when inserting
483+
/// instructions after an apply site. This results in eliminating unnecessary
484+
/// if-else code otherwise required to handle such situations.
485+
///
486+
/// NOTE: We pass std::next() for begin_apply. If one wishes to insert code
487+
/// /after/ the end_apply/abort_apply, please use instead
488+
/// insertAfterFullEvaluation.
489+
void insertAfterInvocation(function_ref<void(SILBuilder &)> func) const;
490+
491+
/// Pass a builder with insertion points that are guaranteed to be immediately
492+
/// after this full apply site has completely finished executing.
493+
///
494+
/// This is just like insertAfterInvocation except that if the full apply site
495+
/// is a begin_apply, we pass the insertion points after the end_apply,
496+
/// abort_apply rather than an insertion point right after the
497+
/// begin_apply. For such functionality, please invoke insertAfterInvocation.
498+
void insertAfterFullEvaluation(function_ref<void(SILBuilder &)> func) const;
499+
477500
/// Return whether the given apply is of a formally-throwing function
478501
/// which is statically known not to throw.
479502
bool isNonThrowing() const {
@@ -660,29 +683,6 @@ class FullApplySite : public ApplySite {
660683
llvm_unreachable("Covered switch isn't covered?!");
661684
}
662685

663-
/// If this is a terminator apply site, then pass a builder to insert at the
664-
/// first instruction of each successor to \p func. Otherwise, pass a builder
665-
/// to insert at std::next(Inst).
666-
///
667-
/// The intention is that this abstraction will enable the compiler writer to
668-
/// ignore whether or not an apply site is a terminator when inserting
669-
/// instructions after an apply site. This results in eliminating unnecessary
670-
/// if-else code otherwise required to handle such situations.
671-
///
672-
/// NOTE: We pass std::next() for begin_apply. If one wishes to insert code
673-
/// /after/ the end_apply/abort_apply, please use instead
674-
/// insertAfterFullEvaluation.
675-
void insertAfterInvocation(function_ref<void(SILBuilder &)> func) const;
676-
677-
/// Pass a builder with insertion points that are guaranteed to be immediately
678-
/// after this full apply site has completely finished executing.
679-
///
680-
/// This is just like insertAfterInvocation except that if the full apply site
681-
/// is a begin_apply, we pass the insertion points after the end_apply,
682-
/// abort_apply rather than an insertion point right after the
683-
/// begin_apply. For such functionality, please invoke insertAfterInvocation.
684-
void insertAfterFullEvaluation(function_ref<void(SILBuilder &)> func) const;
685-
686686
/// Returns true if \p op is an operand that passes an indirect
687687
/// result argument to the apply site.
688688
bool isIndirectResultOperand(const Operand &op) const {

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/SIL/IR/ApplySite.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,18 @@
1616

1717
using namespace swift;
1818

19-
void FullApplySite::insertAfterInvocation(function_ref<void(SILBuilder &)> func) const {
19+
void ApplySite::insertAfterInvocation(function_ref<void(SILBuilder &)> func) const {
2020
SILBuilderWithScope::insertAfter(getInstruction(), func);
2121
}
2222

23-
void FullApplySite::insertAfterFullEvaluation(
23+
void ApplySite::insertAfterFullEvaluation(
2424
function_ref<void(SILBuilder &)> func) const {
2525
switch (getKind()) {
26-
case FullApplySiteKind::ApplyInst:
27-
case FullApplySiteKind::TryApplyInst:
26+
case ApplySiteKind::ApplyInst:
27+
case ApplySiteKind::TryApplyInst:
28+
case ApplySiteKind::PartialApplyInst:
2829
return insertAfterInvocation(func);
29-
case FullApplySiteKind::BeginApplyInst:
30+
case ApplySiteKind::BeginApplyInst:
3031
SmallVector<EndApplyInst *, 2> endApplies;
3132
SmallVector<AbortApplyInst *, 2> abortApplies;
3233
auto *bai = cast<BeginApplyInst>(getInstruction());

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

lib/SILOptimizer/Mandatory/AddressLowering.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1714,13 +1714,13 @@ namespace {
17141714
/// object arguments with address-type arguments.
17151715
class CallArgRewriter {
17161716
AddressLoweringState &pass;
1717-
FullApplySite apply;
1717+
ApplySite apply;
17181718
SILLocation callLoc;
17191719
SILBuilder argBuilder;
17201720
AddressMaterialization addrMat;
17211721

17221722
public:
1723-
CallArgRewriter(FullApplySite apply, AddressLoweringState &pass)
1723+
CallArgRewriter(ApplySite apply, AddressLoweringState &pass)
17241724
: pass(pass), apply(apply), callLoc(apply.getLoc()),
17251725
argBuilder(pass.getBuilder(apply.getInstruction()->getIterator())),
17261726
addrMat(pass, argBuilder) {}
@@ -2599,6 +2599,10 @@ class UseRewriter : SILInstructionVisitor<UseRewriter> {
25992599
CallArgRewriter(applyInst, pass).rewriteIndirectArgument(use);
26002600
}
26012601

2602+
void visitPartialApplyInst(PartialApplyInst *pai) {
2603+
CallArgRewriter(pai, pass).rewriteIndirectArgument(use);
2604+
}
2605+
26022606
void visitBeginApplyInst(BeginApplyInst *bai) {
26032607
CallArgRewriter(bai, pass).rewriteIndirectArgument(use);
26042608
}

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+

0 commit comments

Comments
 (0)