Skip to content

Commit 797eab1

Browse files
committed
[Mem2Reg] Handled unreachables for single-block allocations.
Previously, it was asserted that any single-block allocation which had valid memory after all instructions in the block were visited terminated in unreachable. That assertion was false--while all paths through that block must end in unreachable, the block itself need not be terminated with an unreachable inst. Here, that is corrected by walking forward from the block until blocks terminated by unreachables or blocks not dominated by the block containing the alloc_stack are reached. In the first case, the lifetime is ended just before the unreachable inst. In the second, the lifetime is ended just before the branch to such a successor block (i.e. just before the branch to a block which is not dominated by the block containing the alloc_stack).
1 parent 0fc25cc commit 797eab1

File tree

2 files changed

+217
-6
lines changed

2 files changed

+217
-6
lines changed

lib/SILOptimizer/Transforms/SILMem2Reg.cpp

Lines changed: 36 additions & 6 deletions
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

test/SILOptimizer/mem2reg_lifetime.sil

Lines changed: 181 additions & 0 deletions
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)