Skip to content

Commit b64510d

Browse files
committed
[AddressLowering] Rewrite indirect loadable yields
While visiting the function, record not only applies which have indirect formal _results_ but also applies which have indirect formal _yields_. When rewriting indirect applies, check the apply site kind and rewrite according to it. Taught ApplyRewriter to handle rewriting indirect loadable yields.
1 parent 5749e05 commit b64510d

File tree

2 files changed

+259
-12
lines changed

2 files changed

+259
-12
lines changed

lib/SILOptimizer/Mandatory/AddressLowering.cpp

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ static SILFunctionConventions getLoweredCallConv(ApplySite call) {
195195
/// If \p pseudoResult represents multiple results and at least one result is
196196
/// used, then return the destructure.
197197
static DestructureTupleInst *getCallDestructure(FullApplySite apply) {
198+
if (apply.getKind() == FullApplySiteKind::BeginApplyInst)
199+
return nullptr;
198200
if (apply.getSubstCalleeConv().getNumDirectSILResults() == 1)
199201
return nullptr;
200202

@@ -640,7 +642,8 @@ void OpaqueValueVisitor::checkForIndirectApply(FullApplySite applySite) {
640642
++calleeArgIdx;
641643
}
642644

643-
if (applySite.getSubstCalleeType()->hasIndirectFormalResults()) {
645+
if (applySite.getSubstCalleeType()->hasIndirectFormalResults() ||
646+
applySite.getSubstCalleeType()->hasIndirectFormalYields()) {
644647
pass.indirectApplies.insert(applySite);
645648
}
646649
}
@@ -2125,6 +2128,11 @@ void ApplyRewriter::rewriteApply(ArrayRef<SILValue> newCallArgs) {
21252128
static void emitEndBorrows(SILValue value, AddressLoweringState &pass);
21262129

21272130
void ApplyRewriter::convertBeginApplyWithOpaqueYield() {
2131+
// Avoid revisiting this apply.
2132+
bool erased = pass.indirectApplies.erase(apply);
2133+
assert(erased && "all yields should be rewritten at the same time");
2134+
(void)erased;
2135+
21282136
auto *origCall = cast<BeginApplyInst>(apply.getInstruction());
21292137
SmallVector<SILValue, 4> opValues;
21302138

@@ -2144,11 +2152,33 @@ void ApplyRewriter::convertBeginApplyWithOpaqueYield() {
21442152
auto newResults = newCall->getAllResultsBuffer();
21452153
assert(oldResults.size() == newResults.size());
21462154
for (auto i : indices(oldResults)) {
2147-
if (oldResults[i].getType().isAddressOnly(*pass.function)) {
2148-
pass.valueStorageMap.setStorageAddress(&oldResults[i], &newResults[i]);
2149-
pass.valueStorageMap.getStorage(&oldResults[i]).markRewritten();
2155+
auto &oldResult = oldResults[i];
2156+
auto &newResult = newResults[i];
2157+
if (oldResult.getType().isAddressOnly(*pass.function)) {
2158+
if (!oldResult.getType().isAddress()) {
2159+
pass.valueStorageMap.setStorageAddress(&oldResult, &newResult);
2160+
pass.valueStorageMap.getStorage(&oldResult).markRewritten();
2161+
} else {
2162+
oldResult.replaceAllUsesWith(&newResult);
2163+
}
21502164
} else {
2151-
oldResults[i].replaceAllUsesWith(&newResults[i]);
2165+
if (oldResult.getType().isObject() && newResult.getType().isAddress()) {
2166+
if (oldResult.getOwnershipKind() == OwnershipKind::Guaranteed) {
2167+
SILValue load =
2168+
resultBuilder.emitLoadBorrowOperation(callLoc, &newResult);
2169+
oldResult.replaceAllUsesWith(load);
2170+
emitEndBorrows(load, pass);
2171+
} else {
2172+
auto *load = resultBuilder.createLoad(
2173+
callLoc, &newResult,
2174+
newResult.getType().isTrivial(*pass.function)
2175+
? LoadOwnershipQualifier::Trivial
2176+
: LoadOwnershipQualifier::Take);
2177+
oldResult.replaceAllUsesWith(load);
2178+
}
2179+
} else {
2180+
oldResult.replaceAllUsesWith(&newResult);
2181+
}
21522182
}
21532183
}
21542184
}
@@ -3369,14 +3399,27 @@ static void rewriteIndirectApply(FullApplySite apply,
33693399
// If all indirect args were loadable, then they still need to be rewritten.
33703400
CallArgRewriter(apply, pass).rewriteArguments();
33713401

3372-
if (!apply.getSubstCalleeType()->hasIndirectFormalResults()) {
3373-
return;
3402+
switch (apply.getKind()) {
3403+
case FullApplySiteKind::ApplyInst:
3404+
case FullApplySiteKind::TryApplyInst: {
3405+
if (!apply.getSubstCalleeType()->hasIndirectFormalResults()) {
3406+
return;
3407+
}
3408+
// If the call has indirect results and wasn't already rewritten, rewrite it
3409+
// now. This handles try_apply, which is not rewritten when DefRewriter
3410+
// visits block arguments. It also handles apply with loadable indirect
3411+
// results.
3412+
ApplyRewriter(apply, pass).convertApplyWithIndirectResults();
3413+
break;
3414+
}
3415+
case FullApplySiteKind::BeginApplyInst: {
3416+
if (!apply.getSubstCalleeType()->hasIndirectFormalYields()) {
3417+
return;
3418+
}
3419+
ApplyRewriter(apply, pass).convertBeginApplyWithOpaqueYield();
3420+
break;
3421+
}
33743422
}
3375-
3376-
// If the call has indirect results and wasn't already rewritten, rewrite it
3377-
// now. This handles try_apply, which is not rewritten when DefRewriter visits
3378-
// block arguments. It also handles apply with loadable indirect results.
3379-
ApplyRewriter(apply, pass).convertApplyWithIndirectResults();
33803423

33813424
if (!apply.getInstruction()->isDeleted()) {
33823425
assert(!getCallDestructure(apply)

test/SILOptimizer/address_lowering.sil

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ struct I {}
2929

3030
class Klass {}
3131

32+
struct LoadableTrivialExtractable {
33+
var i: I
34+
}
35+
36+
struct LoadableNontrivial {
37+
var x: Klass
38+
}
39+
3240
struct SI<T> {
3341
var element: T
3442
var index: I
@@ -1468,6 +1476,202 @@ bb0(%0 : @guaranteed $TestGeneric<T>):
14681476
return %10 : $()
14691477
}
14701478

1479+
// CHECK-LABEL: sil [ossa] @testBeginApply4YieldLoadableTrivial : {{.*}} {
1480+
// CHECK: ([[ADDR:%[^,]+]], {{%[^,]+}}) = begin_apply
1481+
// CHECK: [[INSTANCE:%[^,]+]] = load [trivial] [[ADDR]]
1482+
// CHECK: struct_extract [[INSTANCE]]
1483+
// CHECK-LABEL: } // end sil function 'testBeginApply4YieldLoadableTrivial'
1484+
sil [ossa] @testBeginApply4YieldLoadableTrivial : $@convention(thin) () -> I {
1485+
entry:
1486+
(%instance, %token) = begin_apply undef<LoadableTrivialExtractable>() : $@yield_once @convention(thin) <U> () -> @yields @in_guaranteed U
1487+
end_apply %token
1488+
%extract = struct_extract %instance : $LoadableTrivialExtractable, #LoadableTrivialExtractable.i
1489+
return %extract : $I
1490+
}
1491+
1492+
// CHECK-LABEL: sil [ossa] @testBeginApply5Yield2LoadableTrivial : {{.*}} {
1493+
// CHECK: ([[ADDR_1:%[^,]+]], [[ADDR_2:%[^,]+]], {{%[^,]+}}) = begin_apply
1494+
// CHECK: [[INSTANCE_1:%[^,]+]] = load [trivial] [[ADDR_1]]
1495+
// CHECK: [[INSTANCE_2:%[^,]+]] = load [trivial] [[ADDR_2]]
1496+
// CHECK: struct_extract [[INSTANCE_1]]
1497+
// CHECK: struct_extract [[INSTANCE_2]]
1498+
// CHECK-LABEL: } // end sil function 'testBeginApply5Yield2LoadableTrivial'
1499+
sil [ossa] @testBeginApply5Yield2LoadableTrivial : $@convention(thin) () -> (I, I) {
1500+
entry:
1501+
(%instance_1, %instance_2, %token) = begin_apply undef<LoadableTrivialExtractable>() : $@yield_once @convention(thin) <U> () -> (@yields @in_guaranteed U, @yields @in_guaranteed U)
1502+
end_apply %token
1503+
%i_1 = struct_extract %instance_1 : $LoadableTrivialExtractable, #LoadableTrivialExtractable.i
1504+
%i_2 = struct_extract %instance_2 : $LoadableTrivialExtractable, #LoadableTrivialExtractable.i
1505+
%tuple = tuple (%i_1 : $I, %i_2 : $I)
1506+
return %tuple : $(I, I)
1507+
}
1508+
1509+
// CHECK-LABEL: sil [ossa] @testBeginApply6Yield1LoadableTrivial1OpaqueGuaranteed : {{.*}} {
1510+
// CHECK: {{bb[0-9]+}}([[TRIV_OUT:%[^,]+]] : $*I, [[OPAQUE_OUT:%[^,]+]] : $*T):
1511+
// CHECK: ([[ADDR_1:%[^,]+]], [[ADDR_2:%[^,]+]], {{%[^,]+}}) = begin_apply
1512+
// CHECK: [[INSTANCE_1:%[^,]+]] = load [trivial] [[ADDR_1]]
1513+
// CHECK: copy_addr [[ADDR_2]] to [init] [[OPAQUE_OUT]]
1514+
// CHECK: [[EXTRACT:%[^,]+]] = struct_extract [[INSTANCE_1]]
1515+
// CHECK: store [[EXTRACT]] to [trivial] [[TRIV_OUT]]
1516+
// CHECK-LABEL: } // end sil function 'testBeginApply6Yield1LoadableTrivial1OpaqueGuaranteed'
1517+
sil [ossa] @testBeginApply6Yield1LoadableTrivial1OpaqueGuaranteed : $@convention(thin) <T> () -> (@out I, @out T) {
1518+
entry:
1519+
(%instance_1, %instance_2, %token) = begin_apply undef<LoadableTrivialExtractable, T>() : $@yield_once @convention(thin) <U, T> () -> (@yields @in_guaranteed U, @yields @in_guaranteed T)
1520+
%copy = copy_value %instance_2 : $T
1521+
end_apply %token
1522+
%i = struct_extract %instance_1 : $LoadableTrivialExtractable, #LoadableTrivialExtractable.i
1523+
%tuple = tuple (%i : $I, %copy : $T)
1524+
return %tuple : $(I, T)
1525+
}
1526+
1527+
// CHECK-LABEL: sil [ossa] @testBeginApply7YieldLoadableNontrivialGuaranteed : {{.*}} {
1528+
// CHECK: ([[ADDR:%[^,]+]], {{%[^,]+}}) = begin_apply
1529+
// CHECK: [[BORROW:%[^,]+]] = load_borrow [[ADDR]]
1530+
// CHECK: end_borrow [[BORROW]]
1531+
// CHECK-LABEL: } // end sil function 'testBeginApply7YieldLoadableNontrivialGuaranteed'
1532+
sil [ossa] @testBeginApply7YieldLoadableNontrivialGuaranteed : $@convention(thin) () -> @owned Klass {
1533+
entry:
1534+
(%instance, %token) = begin_apply undef<LoadableNontrivial>() : $@yield_once @convention(thin) <U> () -> @yields @in_guaranteed U
1535+
%extract = struct_extract %instance : $LoadableNontrivial, #LoadableNontrivial.x
1536+
%retval = copy_value %extract : $Klass
1537+
end_apply %token
1538+
return %retval : $Klass
1539+
}
1540+
1541+
// CHECK-LABEL: sil [ossa] @testBeginApply8Yield2LoadableNontrivialGuaranteed : {{.*}} {
1542+
// CHECK: ([[ADDR_1:%[^,]+]], [[ADDR_2:%[^,]+]], {{%[^,]+}}) = begin_apply
1543+
// CHECK: [[INSTANCE_1:%[^,]+]] = load_borrow [[ADDR_1]]
1544+
// CHECK: [[INSTANCE_2:%[^,]+]] = load_borrow [[ADDR_2]]
1545+
// CHECK: struct_extract [[INSTANCE_1]]
1546+
// CHECK: struct_extract [[INSTANCE_2]]
1547+
// CHECK: end_borrow [[INSTANCE_1]]
1548+
// CHECK: end_borrow [[INSTANCE_2]]
1549+
// CHECK-LABEL: } // end sil function 'testBeginApply8Yield2LoadableNontrivialGuaranteed'
1550+
sil [ossa] @testBeginApply8Yield2LoadableNontrivialGuaranteed : $@convention(thin) () -> @owned (Klass, Klass) {
1551+
entry:
1552+
(%instance_1, %instance_2, %token) = begin_apply undef<LoadableNontrivial>() : $@yield_once @convention(thin) <U> () -> (@yields @in_guaranteed U, @yields @in_guaranteed U)
1553+
%extract_1 = struct_extract %instance_1 : $LoadableNontrivial, #LoadableNontrivial.x
1554+
%extract_2 = struct_extract %instance_2 : $LoadableNontrivial, #LoadableNontrivial.x
1555+
%copy_1 = copy_value %extract_1 : $Klass
1556+
%copy_2 = copy_value %extract_2 : $Klass
1557+
end_apply %token
1558+
%retval = tuple (%copy_1 : $Klass, %copy_2 : $Klass)
1559+
return %retval : $(Klass, Klass)
1560+
}
1561+
1562+
// CHECK-LABEL: sil [ossa] @testBeginApply9Yield1LoadableNontrivialGuaranteed1OpaqueGuaranteed : {{.*}} {
1563+
// CHECK: {{bb[0-9]+}}({{%[^,]+}} : $*Klass, [[OPAQUE_OUT:%[^,]+]] : $*T):
1564+
// CHECK: ([[ADDR_1:%[^,]+]], [[ADDR_2:%[^,]+]], {{%[^,]+}}) = begin_apply
1565+
// CHECK: [[BORROW_1:%[^,]+]] = load_borrow [[ADDR_1]] : $*LoadableNontrivial
1566+
// CHECK: struct_extract [[BORROW_1]] : $LoadableNontrivial, #LoadableNontrivial.x
1567+
// CHECK: end_borrow [[BORROW_1]] : $LoadableNontrivial
1568+
// CHECK: copy_addr [[ADDR_2]] to [init] [[OPAQUE_OUT]] : $*T
1569+
// CHECK-LABEL: } // end sil function 'testBeginApply9Yield1LoadableNontrivialGuaranteed1OpaqueGuaranteed'
1570+
sil [ossa] @testBeginApply9Yield1LoadableNontrivialGuaranteed1OpaqueGuaranteed : $@convention(thin) <T> () -> (@out Klass, @out T) {
1571+
entry:
1572+
(%instance_1, %instance_2, %token) = begin_apply undef<LoadableNontrivial, T>() : $@yield_once @convention(thin) <U, T> () -> (@yields @in_guaranteed U, @yields @in_guaranteed T)
1573+
%extract_1 = struct_extract %instance_1 : $LoadableNontrivial, #LoadableNontrivial.x
1574+
%copy_1 = copy_value %extract_1 : $Klass
1575+
%copy_2 = copy_value %instance_2 : $T
1576+
end_apply %token
1577+
%retval = tuple (%copy_1 : $Klass, %copy_2 : $T)
1578+
return %retval : $(Klass, T)
1579+
}
1580+
1581+
// CHECK-LABEL: sil [ossa] @testBeginApplyAYieldLoadableNontrivialOwned : {{.*}} {
1582+
// CHECK: ([[ADDR:%[^,]+]], {{%[^,]+}}) = begin_apply
1583+
// CHECK: [[INSTANCE:%[^,]+]] = load [take] [[ADDR]] : $*LoadableNontrivial
1584+
// CHECK: [[BORROW:%[^,]+]] = begin_borrow [[INSTANCE]] : $LoadableNontrivial
1585+
// CHECK: struct_extract [[BORROW]] : $LoadableNontrivial, #LoadableNontrivial.x
1586+
// CHECK: end_borrow [[BORROW]] : $LoadableNontrivial
1587+
// CHECK-LABEL: } // end sil function 'testBeginApplyAYieldLoadableNontrivialOwned'
1588+
sil [ossa] @testBeginApplyAYieldLoadableNontrivialOwned : $@convention(thin) () -> @owned Klass {
1589+
entry:
1590+
(%instance, %token) = begin_apply undef<LoadableNontrivial>() : $@yield_once @convention(thin) <U> () -> @yields @in U
1591+
end_apply %token
1592+
%borrow = begin_borrow %instance : $LoadableNontrivial
1593+
%extract = struct_extract %borrow : $LoadableNontrivial, #LoadableNontrivial.x
1594+
%retval = copy_value %extract : $Klass
1595+
end_borrow %borrow : $LoadableNontrivial
1596+
destroy_value %instance : $LoadableNontrivial
1597+
return %retval : $Klass
1598+
}
1599+
1600+
// CHECK-LABEL: sil [ossa] @testBeginApplyBYield2LoadableNontrivialOwned : {{.*}} {
1601+
// CHECK: ([[ADDR_1:%[^,]+]], [[ADDR_2:%[^,]+]], {{%[^,]+}}) = begin_apply
1602+
// CHECK: [[INSTANCE_1:%[^,]+]] = load [take] [[ADDR_1]]
1603+
// CHECK: [[INSTANCE_2:%[^,]+]] = load [take] [[ADDR_2]]
1604+
// CHECK: begin_borrow [[INSTANCE_1]]
1605+
// CHECK: begin_borrow [[INSTANCE_2]]
1606+
// CHECK-LABEL: } // end sil function 'testBeginApplyBYield2LoadableNontrivialOwned'
1607+
sil [ossa] @testBeginApplyBYield2LoadableNontrivialOwned : $@convention(thin) () -> @owned (Klass, Klass) {
1608+
entry:
1609+
(%instance_1, %instance_2, %token) = begin_apply undef<LoadableNontrivial>() : $@yield_once @convention(thin) <U> () -> (@yields @in U, @yields @in U)
1610+
end_apply %token
1611+
%borrow_1 = begin_borrow %instance_1 : $LoadableNontrivial
1612+
%extract_1 = struct_extract %borrow_1 : $LoadableNontrivial, #LoadableNontrivial.x
1613+
%copy_1 = copy_value %extract_1 : $Klass
1614+
end_borrow %borrow_1 : $LoadableNontrivial
1615+
destroy_value %instance_1 : $LoadableNontrivial
1616+
%borrow_2 = begin_borrow %instance_2 : $LoadableNontrivial
1617+
%extract_2 = struct_extract %borrow_2 : $LoadableNontrivial, #LoadableNontrivial.x
1618+
%copy_2 = copy_value %extract_2 : $Klass
1619+
end_borrow %borrow_2 : $LoadableNontrivial
1620+
destroy_value %instance_2 : $LoadableNontrivial
1621+
%retval = tuple (%copy_1 : $Klass, %copy_2 : $Klass)
1622+
return %retval : $(Klass, Klass)
1623+
}
1624+
1625+
// CHECK-LABEL: sil [ossa] @testBeginApplyCYield1LoadableNontrivialOwned1OpaqueOwned : $@convention(thin) <T> () -> (@out Klass, @out T) {
1626+
// CHECK: {{bb[0-9]+}}({{%[^,]+}} : $*Klass, [[OPAQUE_OUT:%[^,]+]] : $*T):
1627+
// CHECK: ([[ADDR_1:%[^,]+]], [[ADDR_2:%[^,]+]], {{%[^,]+}}) = begin_apply
1628+
// CHECK: [[INSTANCE_1:%[^,]+]] = load [take] [[ADDR_1]]
1629+
// CHECK: begin_borrow [[INSTANCE_1]]
1630+
// CHECK: copy_addr [take] [[ADDR_2]] to [init] [[OPAQUE_OUT]]
1631+
// CHECK-LABEL: } // end sil function 'testBeginApplyCYield1LoadableNontrivialOwned1OpaqueOwned'
1632+
sil [ossa] @testBeginApplyCYield1LoadableNontrivialOwned1OpaqueOwned : $@convention(thin) <T> () -> (@out Klass, @out T) {
1633+
entry:
1634+
(%instance_1, %instance_2, %token) = begin_apply undef<LoadableNontrivial, T>() : $@yield_once @convention(thin) <U, T> () -> (@yields @in U, @yields @in T)
1635+
end_apply %token
1636+
%borrow_1 = begin_borrow %instance_1 : $LoadableNontrivial
1637+
%extract_1 = struct_extract %borrow_1 : $LoadableNontrivial, #LoadableNontrivial.x
1638+
%copy_1 = copy_value %extract_1 : $Klass
1639+
end_borrow %borrow_1 : $LoadableNontrivial
1640+
destroy_value %instance_1 : $LoadableNontrivial
1641+
%retval = tuple (%copy_1 : $Klass, %instance_2 : $T)
1642+
return %retval : $(Klass, T)
1643+
}
1644+
1645+
// CHECK-LABEL: sil [ossa] @testBeginApplyDYieldOpaqueInout : {{.*}} {
1646+
// CHECK: begin_apply undef<T>() : $@yield_once @convention(method) <τ_0_0> () -> @yields @inout τ_0_0
1647+
// CHECK-LABEL: } // end sil function 'testBeginApplyDYieldOpaqueInout'
1648+
sil [ossa] @testBeginApplyDYieldOpaqueInout : $@yield_once @convention(thin) <T> () -> () {
1649+
entry:
1650+
(%addr, %token) = begin_apply undef<T>() : $@yield_once @convention(method) <U> () -> @yields @inout U
1651+
end_apply %token
1652+
%8 = tuple ()
1653+
return %8 : $()
1654+
}
1655+
1656+
// CHECK-LABEL: sil [ossa] @testBeginApplyEYieldOpaqueInoutAndYield : {{.*}}{
1657+
// CHECK: ([[REGISTER_0:%[^,]+]], {{%[^,]+}}) = begin_apply
1658+
// CHECK: yield [[REGISTER_0]] : $*T
1659+
// CHECK-LABEL: } // end sil function 'testBeginApplyEYieldOpaqueInoutAndYield'
1660+
sil [ossa] @testBeginApplyEYieldOpaqueInoutAndYield : $@yield_once @convention(thin) <T> () -> @yields @inout T {
1661+
entry:
1662+
(%addr, %token) = begin_apply undef<T>() : $@yield_once @convention(method) <U> () -> @yields @inout U
1663+
yield %addr : $*T, resume bb1, unwind bb2
1664+
1665+
bb1:
1666+
end_apply %token
1667+
%8 = tuple ()
1668+
return %8 : $()
1669+
1670+
bb2:
1671+
abort_apply %token
1672+
unwind
1673+
}
1674+
14711675
// CHECK-LABEL: sil hidden [ossa] @testOpaqueYield :
14721676
// CHECK: bb0(%0 : @guaranteed $TestGeneric<T>):
14731677
// CHECK: [[REF:%.*]] = ref_element_addr %0 : $TestGeneric<T>, #TestGeneric.borrowedGeneric

0 commit comments

Comments
 (0)