Skip to content

Commit dcce7fc

Browse files
authored
AddressLowering: Handle coroutines (#58821)
1 parent 1a76678 commit dcce7fc

File tree

7 files changed

+203
-16
lines changed

7 files changed

+203
-16
lines changed

include/swift/AST/Types.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4460,6 +4460,32 @@ class SILFunctionType final
44604460
SILType getDirectFormalResultsType(SILModule &M,
44614461
TypeExpansionContext expansion);
44624462

4463+
unsigned getNumIndirectFormalYields() const {
4464+
return isCoroutine() ? NumAnyIndirectFormalResults : 0;
4465+
}
4466+
/// Does this function have any formally indirect yields?
4467+
bool hasIndirectFormalYields() const {
4468+
return getNumIndirectFormalYields() != 0;
4469+
}
4470+
unsigned getNumDirectFormalYields() const {
4471+
return isCoroutine() ? NumAnyResults - NumAnyIndirectFormalResults : 0;
4472+
}
4473+
4474+
struct IndirectFormalYieldFilter {
4475+
bool operator()(SILYieldInfo yield) const {
4476+
return !yield.isFormalIndirect();
4477+
}
4478+
};
4479+
4480+
using IndirectFormalYieldIter =
4481+
llvm::filter_iterator<const SILYieldInfo *, IndirectFormalYieldFilter>;
4482+
using IndirectFormalYieldRange = iterator_range<IndirectFormalYieldIter>;
4483+
4484+
/// A range of SILYieldInfo for all formally Indirect Yields.
4485+
IndirectFormalYieldRange getIndirectFormalYields() const {
4486+
return llvm::make_filter_range(getYields(), IndirectFormalYieldFilter());
4487+
}
4488+
44634489
/// Get a single non-address SILType for all SIL results regardless of whether
44644490
/// they are formally indirect. The actual SIL result type of an apply
44654491
/// instruction that calls this function depends on the current SIL stage and

include/swift/SIL/SILValue.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,9 @@ struct ValueOwnershipKind {
255255
explicit ValueOwnershipKind(unsigned newValue) : value(innerty(newValue)) {}
256256
ValueOwnershipKind(const SILFunction &f, SILType type,
257257
SILArgumentConvention convention);
258+
ValueOwnershipKind(const SILFunction &f, SILType type,
259+
SILArgumentConvention convention,
260+
SILModuleConventions moduleConventions);
258261

259262
/// Parse Value into a ValueOwnershipKind.
260263
///

lib/AST/ASTContext.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3949,7 +3949,10 @@ SILFunctionType::SILFunctionType(
39493949
} else {
39503950
assert(normalResults.empty());
39513951
NumAnyResults = yields.size();
3952-
NumAnyIndirectFormalResults = 0; // unused
3952+
NumAnyIndirectFormalResults = std::count_if(
3953+
yields.begin(), yields.end(), [](const SILYieldInfo &yieldInfo) {
3954+
return yieldInfo.isFormalIndirect();
3955+
});
39533956
memcpy(getMutableYields().data(), yields.data(),
39543957
yields.size() * sizeof(SILYieldInfo));
39553958
}

lib/SIL/IR/SILInstructions.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -536,10 +536,12 @@ BeginApplyInst::create(SILDebugLocation loc, SILValue callee,
536536

537537
for (auto &yield : substCalleeType->getYields()) {
538538
auto yieldType = conv.getSILType(yield, F.getTypeExpansionContext());
539-
auto convention = SILArgumentConvention(yield.getConvention());
539+
auto argConvention = SILArgumentConvention(yield.getConvention());
540540
resultTypes.push_back(yieldType);
541-
resultOwnerships.push_back(
542-
ValueOwnershipKind(F, yieldType, convention));
541+
resultOwnerships.push_back(ValueOwnershipKind(
542+
F, yieldType, argConvention,
543+
moduleConventions.hasValue() ? moduleConventions.getValue()
544+
: SILModuleConventions(F.getModule())));
543545
}
544546

545547
resultTypes.push_back(SILType::getSILTokenType(F.getASTContext()));

lib/SIL/IR/SILValue.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,13 @@ StringRef OwnershipKind::asString() const {
186186

187187
ValueOwnershipKind::ValueOwnershipKind(const SILFunction &F, SILType Type,
188188
SILArgumentConvention Convention)
189-
: value(OwnershipKind::Any) {
190-
auto &M = F.getModule();
189+
: ValueOwnershipKind(F, Type, Convention,
190+
SILModuleConventions(F.getModule())) {}
191191

192+
ValueOwnershipKind::ValueOwnershipKind(const SILFunction &F, SILType Type,
193+
SILArgumentConvention Convention,
194+
SILModuleConventions moduleConventions)
195+
: value(OwnershipKind::Any) {
192196
// Trivial types can be passed using a variety of conventions. They always
193197
// have trivial ownership.
194198
if (Type.isTrivial(F)) {
@@ -199,14 +203,12 @@ ValueOwnershipKind::ValueOwnershipKind(const SILFunction &F, SILType Type,
199203
switch (Convention) {
200204
case SILArgumentConvention::Indirect_In:
201205
case SILArgumentConvention::Indirect_In_Constant:
202-
value = SILModuleConventions(M).useLoweredAddresses()
203-
? OwnershipKind::None
204-
: OwnershipKind::Owned;
206+
value = moduleConventions.useLoweredAddresses() ? OwnershipKind::None
207+
: OwnershipKind::Owned;
205208
break;
206209
case SILArgumentConvention::Indirect_In_Guaranteed:
207-
value = SILModuleConventions(M).useLoweredAddresses()
208-
? OwnershipKind::None
209-
: OwnershipKind::Guaranteed;
210+
value = moduleConventions.useLoweredAddresses() ? OwnershipKind::None
211+
: OwnershipKind::Guaranteed;
210212
break;
211213
case SILArgumentConvention::Indirect_Inout:
212214
case SILArgumentConvention::Indirect_InoutAliasable:

lib/SILOptimizer/Mandatory/AddressLowering.cpp

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -645,8 +645,10 @@ void OpaqueValueVisitor::checkForIndirectApply(FullApplySite applySite) {
645645
}
646646
++calleeArgIdx;
647647
}
648-
if (applySite.getSubstCalleeType()->hasIndirectFormalResults())
648+
649+
if (applySite.getSubstCalleeType()->hasIndirectFormalResults()) {
649650
pass.indirectApplies.insert(applySite);
651+
}
650652
}
651653

652654
/// If `value` is address-only, add it to the `valueStorageMap`.
@@ -883,6 +885,16 @@ static SILValue getProjectedUseValue(Operand *operand) {
883885
return SILValue();
884886
}
885887

888+
static bool doesNotNeedStackAllocation(SILValue value) {
889+
auto *defInst = value->getDefiningInstruction();
890+
if (!defInst)
891+
return false;
892+
893+
if (isa<LoadBorrowInst>(defInst) || isa<BeginApplyInst>(defInst))
894+
return true;
895+
896+
return false;
897+
}
886898
//===----------------------------------------------------------------------===//
887899
// OpaqueStorageAllocation
888900
//
@@ -1042,11 +1054,15 @@ void OpaqueStorageAllocation::allocateValue(SILValue value) {
10421054
if (getReusedStorageOperand(value))
10431055
return;
10441056

1057+
if (doesNotNeedStackAllocation(value))
1058+
return;
1059+
10451060
// Check for values that inherently project storage from their operand.
10461061
if (auto *storageOper = getProjectedDefOperand(value)) {
10471062
pass.valueStorageMap.recordDefProjection(storageOper, value);
10481063
return;
10491064
}
1065+
10501066
if (value->getOwnershipKind() == OwnershipKind::Guaranteed) {
10511067
value->dump();
10521068
llvm::report_fatal_error("^^^ guaranteed values must reuse storage");
@@ -1183,7 +1199,6 @@ void OpaqueStorageAllocation::removeAllocation(SILValue value) {
11831199
AllocStackInst *OpaqueStorageAllocation::createStackAllocation(SILValue value) {
11841200
assert(value.getOwnershipKind() != OwnershipKind::Guaranteed &&
11851201
"creating storage for a guaranteed value implies a copy");
1186-
11871202
// Instructions that produce an opened type never reach here because they
11881203
// have guaranteed ownership--they project their storage. We reach this
11891204
// point after the opened value has been copied.
@@ -1822,10 +1837,11 @@ class ApplyRewriter {
18221837
loweredCalleeConv(getLoweredCallConv(oldCall)) {}
18231838

18241839
void convertApplyWithIndirectResults();
1840+
void convertBeginApplyWithOpaqueYield();
18251841

18261842
protected:
18271843
SILBasicBlock::iterator getCallResultInsertionPoint() {
1828-
if (isa<ApplyInst>(apply))
1844+
if (isa<ApplyInst>(apply) || isa<BeginApplyInst>(apply))
18291845
return std::next(SILBasicBlock::iterator(apply.getInstruction()));
18301846

18311847
auto *bb = cast<TryApplyInst>(apply)->getNormalBB();
@@ -2080,6 +2096,35 @@ void ApplyRewriter::rewriteApply(ArrayRef<SILValue> newCallArgs) {
20802096
// will be deleted with its destructure_tuple.
20812097
}
20822098

2099+
void ApplyRewriter::convertBeginApplyWithOpaqueYield() {
2100+
auto *origCall = cast<BeginApplyInst>(apply.getInstruction());
2101+
SmallVector<SILValue, 4> opValues;
2102+
2103+
for (auto &oper : origCall->getArgumentOperands()) {
2104+
opValues.push_back(oper.get());
2105+
}
2106+
2107+
// Recreate the begin_apply so that the instruction results have the right
2108+
// ownership kind as per the lowered addresses convention.
2109+
auto *newCall = argBuilder.createBeginApply(
2110+
callLoc, apply.getCallee(), apply.getSubstitutionMap(), opValues,
2111+
origCall->getApplyOptions(), origCall->getSpecializationInfo());
2112+
this->apply = FullApplySite(newCall);
2113+
2114+
// Replace uses of orig begin_apply with the new begin_apply
2115+
auto oldResults = origCall->getAllResultsBuffer();
2116+
auto newResults = newCall->getAllResultsBuffer();
2117+
assert(oldResults.size() == newResults.size());
2118+
for (auto i : indices(oldResults)) {
2119+
if (oldResults[i].getType().isAddressOnly(*pass.function)) {
2120+
pass.valueStorageMap.setStorageAddress(&oldResults[i], &newResults[i]);
2121+
pass.valueStorageMap.getStorage(&oldResults[i]).markRewritten();
2122+
} else {
2123+
oldResults[i].replaceAllUsesWith(&newResults[i]);
2124+
}
2125+
}
2126+
}
2127+
20832128
// Replace \p tryApply with a new try_apply using \p newCallArgs.
20842129
//
20852130
// If the old result was a single opaque value, then create and return a
@@ -2545,6 +2590,16 @@ class UseRewriter : SILInstructionVisitor<UseRewriter> {
25452590
CallArgRewriter(applyInst, pass).rewriteIndirectArgument(use);
25462591
}
25472592

2593+
void visitBeginApplyInst(BeginApplyInst *bai) {
2594+
CallArgRewriter(bai, pass).rewriteIndirectArgument(use);
2595+
}
2596+
2597+
void visitYieldInst(YieldInst *yield) {
2598+
SILValue addr =
2599+
pass.valueStorageMap.getStorage(yield->getOperand(0)).storageAddress;
2600+
yield->setOperand(0, addr);
2601+
}
2602+
25482603
void visitAssignInst(AssignInst *assignInst);
25492604

25502605
void visitBeginBorrowInst(BeginBorrowInst *borrow);
@@ -3039,6 +3094,11 @@ class DefRewriter : SILInstructionVisitor<DefRewriter> {
30393094
ApplyRewriter(applyInst, pass).convertApplyWithIndirectResults();
30403095
}
30413096

3097+
void visitBeginApplyInst(BeginApplyInst *bai) {
3098+
CallArgRewriter(bai, pass).rewriteArguments();
3099+
ApplyRewriter(bai, pass).convertBeginApplyWithOpaqueYield();
3100+
}
3101+
30423102
// Rewrite the apply for an indirect result.
30433103
void visitDestructureTupleInst(DestructureTupleInst *destructure) {
30443104
SILValue srcVal = destructure->getOperand();
@@ -3107,6 +3167,10 @@ class DefRewriter : SILInstructionVisitor<DefRewriter> {
31073167
}
31083168
}
31093169

3170+
void visitLoadBorrowInst(LoadBorrowInst *lbi) {
3171+
pass.valueStorageMap.setStorageAddress(lbi, lbi->getOperand());
3172+
}
3173+
31103174
// Define an opaque struct.
31113175
void visitStructInst(StructInst *structInst) {
31123176
// For each element, initialize the operand's memory. Some struct elements
@@ -3159,8 +3223,9 @@ static void rewriteIndirectApply(FullApplySite apply,
31593223
// If all indirect args were loadable, then they still need to be rewritten.
31603224
CallArgRewriter(apply, pass).rewriteArguments();
31613225

3162-
if (!apply.getSubstCalleeType()->hasIndirectFormalResults())
3226+
if (!apply.getSubstCalleeType()->hasIndirectFormalResults()) {
31633227
return;
3228+
}
31643229

31653230
// If the call has indirect results and wasn't already rewritten, rewrite it
31663231
// now. This handles try_apply, which is not rewritten when DefRewriter visits

test/SILOptimizer/address_lowering.sil

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ enum Mixed<T> {
4848
case o(AnyObject)
4949
};
5050

51+
class TestGeneric<T> {
52+
init()
53+
@_borrowed @_hasStorage var borrowedGeneric: T { get set }
54+
}
55+
5156
precedencegroup ComparisonPrecedence {
5257
assignment: true
5358
associativity: right
@@ -1171,6 +1176,87 @@ bb2(%9 : $Error):
11711176
throw %9 : $Error
11721177
}
11731178

1179+
sil hidden [ossa] @testBeginApplyDeadYield : $@convention(thin) <T> (@guaranteed TestGeneric<T>) -> () {
1180+
bb0(%0 : @guaranteed $TestGeneric<T>):
1181+
%2 = class_method %0 : $TestGeneric<T>, #TestGeneric.borrowedGeneric!read : <T> (TestGeneric<T>) -> () -> (), $@yield_once @convention(method) <τ_0_0> (@guaranteed TestGeneric<τ_0_0>) -> @yields @in_guaranteed τ_0_0
1182+
(%3, %4) = begin_apply %2<T>(%0) : $@yield_once @convention(method) <τ_0_0> (@guaranteed TestGeneric<τ_0_0>) -> @yields @in_guaranteed τ_0_0
1183+
end_apply %4
1184+
%10 = tuple ()
1185+
return %10 : $()
1186+
}
1187+
1188+
class Klass {}
1189+
1190+
// CHECK-LABEL: sil hidden [ossa] @testBeginApplyLoadableYieldWithIndirectConv :
1191+
// CHECK: bb0(%0 : @guaranteed $TestGeneric<Klass>):
1192+
// CHECK: [[STK:%.*]] = alloc_stack $Klass
1193+
// CHECK: [[METH:%.*]] = class_method %0 : $TestGeneric<Klass>, #TestGeneric.borrowedGeneric!read : <T> (TestGeneric<T>) -> () -> (), $@yield_once @convention(method) <τ_0_0> (@guaranteed TestGeneric<τ_0_0>) -> @yields @in_guaranteed τ_0_0
1194+
// CHECK: ([[Y:%.*]], [[TOK:%.*]]) = begin_apply [[METH]]<Klass>(%0) : $@yield_once @convention(method) <τ_0_0> (@guaranteed TestGeneric<τ_0_0>) -> @yields @in_guaranteed τ_0_0
1195+
// CHECK: copy_addr [[Y]] to [initialization] [[STK]] : $*Klass
1196+
// CHECK: end_apply [[TOK]]
1197+
// CHECK: destroy_addr [[STK]] : $*Klass
1198+
// CHECK-LABEL: }
1199+
sil hidden [ossa] @testBeginApplyLoadableYieldWithIndirectConv : $@convention(thin) <Klass> (@guaranteed TestGeneric<Klass>) -> () {
1200+
bb0(%0 : @guaranteed $TestGeneric<Klass>):
1201+
%2 = class_method %0 : $TestGeneric<Klass>, #TestGeneric.borrowedGeneric!read : <Klass> (TestGeneric<Klass>) -> () -> (), $@yield_once @convention(method) <τ_0_0> (@guaranteed TestGeneric<τ_0_0>) -> @yields @in_guaranteed τ_0_0
1202+
(%3, %4) = begin_apply %2<Klass>(%0) : $@yield_once @convention(method) <τ_0_0> (@guaranteed TestGeneric<τ_0_0>) -> @yields @in_guaranteed τ_0_0
1203+
%5 = copy_value %3 : $Klass
1204+
end_apply %4
1205+
destroy_value %5 : $Klass
1206+
%10 = tuple ()
1207+
return %10 : $()
1208+
}
1209+
1210+
// CHECK-LABEL: sil hidden [ossa] @testBeginApplyOpaqueYield :
1211+
// CHECK: bb0(%0 : @guaranteed $TestGeneric<T>):
1212+
// CHECK: [[STK:%.*]] = alloc_stack $T
1213+
// CHECK: [[M:%.*]] = class_method %0 : $TestGeneric<T>, #TestGeneric.borrowedGeneric!read : <T> (TestGeneric<T>) -> () -> (), $@yield_once @convention(method) <τ_0_0> (@guaranteed TestGeneric<τ_0_0>) -> @yields @in_guaranteed τ_0_0
1214+
// CHECK: ([[Y:%.*]], [[TOK:%.*]]) = begin_apply %2<T>(%0) : $@yield_once @convention(method) <τ_0_0> (@guaranteed TestGeneric<τ_0_0>) -> @yields @in_guaranteed τ_0_0
1215+
// CHECK: copy_addr [[Y]] to [initialization] [[STK]] : $*T
1216+
// CHECK: end_apply [[TOK]]
1217+
// CHECK: destroy_addr [[STK]] : $*T
1218+
// CHECK-LABEL: } // end sil function 'testBeginApplyOpaqueYield'
1219+
sil hidden [ossa] @testBeginApplyOpaqueYield : $@convention(thin) <T> (@guaranteed TestGeneric<T>) -> () {
1220+
bb0(%0 : @guaranteed $TestGeneric<T>):
1221+
%2 = class_method %0 : $TestGeneric<T>, #TestGeneric.borrowedGeneric!read : <T> (TestGeneric<T>) -> () -> (), $@yield_once @convention(method) <τ_0_0> (@guaranteed TestGeneric<τ_0_0>) -> @yields @in_guaranteed τ_0_0
1222+
(%3, %4) = begin_apply %2<T>(%0) : $@yield_once @convention(method) <τ_0_0> (@guaranteed TestGeneric<τ_0_0>) -> @yields @in_guaranteed τ_0_0
1223+
%5 = copy_value %3 : $T
1224+
end_apply %4
1225+
destroy_value %5 : $T
1226+
%10 = tuple ()
1227+
return %10 : $()
1228+
}
1229+
1230+
// CHECK-LABEL: sil hidden [ossa] @testOpaqueYield :
1231+
// CHECK: bb0(%0 : @guaranteed $TestGeneric<T>):
1232+
// CHECK: [[REF:%.*]] = ref_element_addr %0 : $TestGeneric<T>, #TestGeneric.borrowedGeneric
1233+
// CHECK: [[ACCESS:%.*]] = begin_access [read] [dynamic] [[REF]] : $*T
1234+
// CHECK: yield [[ACCESS]] : $*T, resume bb2, unwind bb1
1235+
// CHECK: bb1:
1236+
// CHECK: end_access [[ACCESS]] : $*T
1237+
// CHECK: unwind
1238+
// CHECK: bb2:
1239+
// CHECK: end_access [[ACCESS]] : $*T
1240+
// CHECK-LABEL: } // end sil function 'testOpaqueYield'
1241+
sil hidden [ossa] @testOpaqueYield : $@yield_once @convention(method) <T> (@guaranteed TestGeneric<T>) -> @yields @in_guaranteed T {
1242+
bb0(%0 : @guaranteed $TestGeneric<T>):
1243+
%2 = ref_element_addr %0 : $TestGeneric<T>, #TestGeneric.borrowedGeneric
1244+
%3 = begin_access [read] [dynamic] %2 : $*T
1245+
%4 = load_borrow %3 : $*T
1246+
yield %4 : $T, resume bb1, unwind bb2
1247+
1248+
bb1:
1249+
end_borrow %4 : $T
1250+
end_access %3 : $*T
1251+
%8 = tuple ()
1252+
return %8 : $()
1253+
1254+
bb2:
1255+
end_borrow %4 : $T
1256+
end_access %3 : $*T
1257+
unwind
1258+
}
1259+
11741260
sil [ossa] @use_T : $@convention(thin) <T> (@in T) -> ()
11751261

11761262
// CHECK-LABEL: sil [ossa] @test_checked_cast_br1 : $@convention(method) <T> (@in Any) -> () {

0 commit comments

Comments
 (0)