Skip to content

Commit 8e48fd6

Browse files
Merge pull request #39930 from nate-chandler/lexical_lifetimes/mem2reg-end-single-block-lifetimes-with-valid-memory
[Mem2Reg] Handled unreachables for single-block allocations.
2 parents 4a56efe + 797eab1 commit 8e48fd6

File tree

2 files changed

+221
-6
lines changed

2 files changed

+221
-6
lines changed

lib/SILOptimizer/Transforms/SILMem2Reg.cpp

+40-6
Original file line numberDiff line numberDiff line change
@@ -1651,12 +1651,42 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *asi) {
16511651

16521652
if (runningVals && runningVals->isStorageValid &&
16531653
shouldAddLexicalLifetime(asi)) {
1654-
assert(isa<UnreachableInst>(parentBlock->getTerminator()) &&
1655-
"valid storage after reaching end of single-block allocation not "
1656-
"terminated by unreachable?!");
1657-
endLexicalLifetimeBeforeInst(
1658-
asi, /*beforeInstruction=*/parentBlock->getTerminator(), ctx,
1659-
runningVals->value);
1654+
// There is still valid storage after visiting all instructions in this
1655+
// block which are the only instructions involving this alloc_stack.
1656+
// This can only happen if all paths from this block end in unreachable.
1657+
//
1658+
// We need to end the lexical lifetime at the last possible location, either
1659+
// just before an unreachable instruction or just before a branch to a block
1660+
// that is not dominated by parentBlock.
1661+
1662+
// Walk forward from parentBlock until finding blocks which either
1663+
// (1) terminate in unreachable
1664+
// (2) have successors which are not dominated by parentBlock
1665+
GraphNodeWorklist<SILBasicBlock *, 2> worklist;
1666+
worklist.initialize(parentBlock);
1667+
while (auto *block = worklist.pop()) {
1668+
auto *terminator = block->getTerminator();
1669+
if (isa<UnreachableInst>(terminator)) {
1670+
endLexicalLifetimeBeforeInst(asi, /*beforeInstruction=*/terminator, ctx,
1671+
runningVals->value);
1672+
continue;
1673+
}
1674+
bool endedLifetime = false;
1675+
for (auto *successor : block->getSuccessorBlocks()) {
1676+
if (!domInfo->dominates(parentBlock, successor)) {
1677+
endLexicalLifetimeBeforeInst(asi, /*beforeInstruction=*/terminator,
1678+
ctx, runningVals->value);
1679+
endedLifetime = true;
1680+
break;
1681+
}
1682+
}
1683+
if (endedLifetime) {
1684+
continue;
1685+
}
1686+
for (auto *successor : block->getSuccessorBlocks()) {
1687+
worklist.insert(successor);
1688+
}
1689+
}
16601690
}
16611691
}
16621692

@@ -1733,6 +1763,10 @@ bool MemoryToRegisters::run() {
17331763
f.verifyCriticalEdges();
17341764

17351765
for (auto &block : f) {
1766+
// Don't waste time optimizing unreachable blocks.
1767+
if (!domInfo->isReachableFromEntry(&block)) {
1768+
continue;
1769+
}
17361770
for (SILInstruction *inst : deleter.updatingReverseRange(&block)) {
17371771
auto *asi = dyn_cast<AllocStackInst>(inst);
17381772
if (!asi)

test/SILOptimizer/mem2reg_lifetime.sil

+181
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,8 @@ enum E {
523523
case one(C)
524524
}
525525

526+
sil [ossa] @getC : $@convention(thin) () -> @owned C
527+
526528
sil [ossa] @callee_guaranteed : $@convention(thin) (@guaranteed C) -> ()
527529
sil [ossa] @callee_owned : $@convention(thin) (@owned C) -> ()
528530
sil [ossa] @callee_error_owned : $@convention(thin) (@owned C) -> @error Error
@@ -1545,6 +1547,185 @@ entry(%instance : @owned $C):
15451547
unreachable
15461548
}
15471549

1550+
1551+
// CHECK-LABEL: sil [ossa] @unreachable_5 : $@convention(thin) (@owned C) -> () {
1552+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $C):
1553+
// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [lexical] [[INSTANCE]]
1554+
// CHECK: [[LIFETIME_OWNED:%[^,]+]] = copy_value [[LIFETIME]]
1555+
// CHECK: br [[EXIT:bb[0-9]+]]
1556+
// CHECK: [[EXIT]]:
1557+
// CHECK: end_borrow [[LIFETIME]]
1558+
// CHECK: destroy_value [[INSTANCE]]
1559+
// CHECK: unreachable
1560+
// CHECK-LABEL: } // end sil function 'unreachable_5'
1561+
sil [ossa] @unreachable_5 : $@convention(thin) (@owned C) -> () {
1562+
entry(%instance_1 : @owned $C):
1563+
%addr = alloc_stack [lexical] $C
1564+
store %instance_1 to [init] %addr :$*C
1565+
br exit
1566+
exit:
1567+
unreachable
1568+
}
1569+
1570+
// CHECK-LABEL: sil [ossa] @unreachable_6 : $@convention(thin) (@owned C) -> () {
1571+
// CHECK: alloc_stack [lexical]
1572+
// CHECK-LABEL: } // end sil function 'unreachable_6'
1573+
sil [ossa] @unreachable_6 : $@convention(thin) (@owned C) -> () {
1574+
entry(%instance_1 : @owned $C):
1575+
br exit
1576+
unr:
1577+
%addr = alloc_stack [lexical] $C
1578+
store %instance_1 to [init] %addr :$*C
1579+
br die
1580+
die:
1581+
unreachable
1582+
exit:
1583+
%res = tuple ()
1584+
return %res : $()
1585+
}
1586+
1587+
1588+
// CHECK-LABEL: sil [ossa] @unreachable_7 : $@convention(thin) (@owned C) -> () {
1589+
// CHECK: alloc_stack [lexical]
1590+
// CHECK-LABEL: } // end sil function 'unreachable_7'
1591+
sil [ossa] @unreachable_7 : $@convention(thin) (@owned C) -> () {
1592+
entry(%instance_1 : @owned $C):
1593+
cond_br undef, exit, die_later
1594+
unr:
1595+
%addr = alloc_stack [lexical] $C
1596+
store %instance_1 to [init] %addr :$*C
1597+
br die_later_2
1598+
die_later_2:
1599+
br die
1600+
die_later:
1601+
br die
1602+
die:
1603+
unreachable
1604+
exit:
1605+
%res = tuple ()
1606+
return %res : $()
1607+
}
1608+
1609+
1610+
// CHECK-LABEL: sil [ossa] @unreachable_8 : $@convention(thin) (@owned C) -> () {
1611+
// CHECK: alloc_stack [lexical]
1612+
// CHECK-LABEL: } // end sil function 'unreachable_8'
1613+
sil [ossa] @unreachable_8 : $@convention(thin) (@owned C) -> () {
1614+
entry(%instance_1 : @owned $C):
1615+
cond_br undef, exit, die_later
1616+
unr:
1617+
%addr = alloc_stack [lexical] $C
1618+
store %instance_1 to [init] %addr : $*C
1619+
cond_br undef, die_later_2_a, die_later_2_b
1620+
die_later_2_a:
1621+
br die_later_2
1622+
die_later_2_b:
1623+
br die_later_2
1624+
die_later_2:
1625+
br die
1626+
die_later:
1627+
br die
1628+
die:
1629+
unreachable
1630+
exit:
1631+
%res = tuple ()
1632+
return %res : $()
1633+
}
1634+
1635+
1636+
// CHECK-LABEL: sil [ossa] @unreachable_loop_1 : $@convention(thin) () -> () {
1637+
// CHECK: {{bb[0-9]+}}:
1638+
// CHECK: [[GET_C:%[^,]+]] = function_ref @getC : $@convention(thin) () -> @owned C
1639+
// CHECK: [[INSTANCE:%[^,]+]] = apply [[GET_C]]() : $@convention(thin) () -> @owned C
1640+
// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [lexical] [[INSTANCE]]
1641+
// CHECK: [[LIFETIME_OWNED:%[^,]+]] = copy_value [[LIFETIME]]
1642+
// CHECK: br [[LOOP_HEADER:bb[0-9]+]]
1643+
// CHECK: [[LOOP_HEADER]]:
1644+
// CHECK: br [[LOOP_BODY:bb[0-9]+]]
1645+
// CHECK: [[LOOP_BODY]]:
1646+
// CHECK: br [[LOOP_EXIT:bb[0-9]+]]
1647+
// CHECK: [[LOOP_EXIT]]:
1648+
// CHECK: cond_br undef, [[LOOP_BACKEDGE:bb[0-9]+]], [[DIE:bb[0-9]+]]
1649+
// CHECK: [[LOOP_BACKEDGE]]:
1650+
// CHECK: br [[LOOP_HEADER]]
1651+
// CHECK: [[DIE]]:
1652+
// CHECK: end_borrow [[LIFETIME]]
1653+
// CHECK: destroy_value [[INSTANCE]]
1654+
// CHECK: unreachable
1655+
// CHECK-LABEL: } // end sil function 'unreachable_loop_1'
1656+
sil [ossa] @unreachable_loop_1 : $@convention(thin) () -> () {
1657+
entry:
1658+
%delegate_var = alloc_stack [lexical] $C
1659+
%getC = function_ref @getC : $@convention(thin) () -> @owned C
1660+
%delegate = apply %getC() : $@convention(thin) () -> @owned C
1661+
store %delegate to [init] %delegate_var : $*C
1662+
br loop_header
1663+
loop_header:
1664+
br loop_body
1665+
loop_body:
1666+
br loop_exit
1667+
loop_exit:
1668+
cond_br undef, loop_backedge, die
1669+
loop_backedge:
1670+
br loop_header
1671+
die:
1672+
unreachable
1673+
}
1674+
1675+
1676+
// CHECK-LABEL: sil [ossa] @unreachable_loop_2 : $@convention(thin) () -> () {
1677+
// CHECK: alloc_stack [lexical]
1678+
// CHECK-LABEL: } // end sil function 'unreachable_loop_2'
1679+
sil [ossa] @unreachable_loop_2 : $@convention(thin) () -> () {
1680+
entry:
1681+
br loop_header
1682+
unr:
1683+
%delegate_var = alloc_stack [lexical] $C
1684+
%getC = function_ref @getC : $@convention(thin) () -> @owned C
1685+
%delegate = apply %getC() : $@convention(thin) () -> @owned C
1686+
store %delegate to [init] %delegate_var : $*C
1687+
br loop_header
1688+
loop_header:
1689+
br loop_body
1690+
loop_body:
1691+
br loop_exit
1692+
loop_exit:
1693+
cond_br undef, loop_backedge, die
1694+
loop_backedge:
1695+
br loop_header
1696+
die:
1697+
unreachable
1698+
}
1699+
1700+
1701+
// CHECK-LABEL: sil [ossa] @unreachable_loop_3 : $@convention(thin) () -> () {
1702+
// CHECK: alloc_stack [lexical]
1703+
// CHECK-LABEL: } // end sil function 'unreachable_loop_3'
1704+
sil [ossa] @unreachable_loop_3 : $@convention(thin) () -> () {
1705+
entry:
1706+
br die_entry
1707+
die_entry:
1708+
br die
1709+
unr:
1710+
%delegate_var = alloc_stack [lexical] $C
1711+
%getC = function_ref @getC : $@convention(thin) () -> @owned C
1712+
%delegate = apply %getC() : $@convention(thin) () -> @owned C
1713+
store %delegate to [init] %delegate_var : $*C
1714+
br loop_header
1715+
loop_header:
1716+
br loop_body
1717+
loop_body:
1718+
br loop_exit
1719+
loop_exit:
1720+
cond_br undef, loop_backedge, die_loop
1721+
loop_backedge:
1722+
br loop_header
1723+
die_loop:
1724+
br die
1725+
die:
1726+
unreachable
1727+
}
1728+
15481729
// }} unreachable_N
15491730

15501731
// diamond_N {{ the usages of the alloc_stack occur over a diamond of blocks

0 commit comments

Comments
 (0)