Skip to content

Commit 7ed618f

Browse files
committed
[TempRValueOpts] Opt some lexical alloc_stacks.
Previously, whenever an alloc_stack [lexical] was seen, optimization bailed conservatively. In the fullness of time, we should optimize an alloc_stack [lexical] so long as: (1) the root of the source of the store/copy_addr is lexical (2) the range in which the alloc_stack [lexical] contains the value store'd/copy_addr'd into it (i.e. the range ending at destroy_addr/etc) is contained within the live range of that lexical root Here, an incremental step in that direction is taken: if the base of the accessed storage of the source of a copy_addr is a guaranteed function argument, then we know (1) the base is lexical--guaranteed function arguments are always lexical (2) the range in which the alloc_stack contains the value copy_addr'd in is trivially contained within the range of that lexical root--the live range of a guaranteed function argument is the whole function Added TODOs for the full optimization.
1 parent 904266c commit 7ed618f

File tree

2 files changed

+72
-8
lines changed

2 files changed

+72
-8
lines changed

lib/SILOptimizer/Transforms/TempRValueElimination.cpp

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -501,11 +501,29 @@ void TempRValueOptPass::tryOptimizeCopyIntoTemp(CopyAddrInst *copyInst) {
501501
if (!tempObj)
502502
return;
503503

504-
// If the storage corresponds to a source-level var, do not optimize here.
505-
// Mem2Reg will transform the lexical alloc_stack into a lexical begin_borrow
506-
// which will ensure that the value's lifetime isn't observably shortened.
507-
if (tempObj->isLexical())
508-
return;
504+
// If the temporary storage is lexical, it either came from a source-level var
505+
// or was marked lexical because it was passed to a function that has been
506+
// inlined.
507+
// TODO: [begin_borrow_addr] Once we can mark addresses as being borrowed, we
508+
// won't need to mark alloc_stacks lexical during inlining. At that
509+
// point, the above comment should change, but the implementation
510+
// remains the same.
511+
//
512+
// In either case, we can eliminate the temporary if the source of the copy is
513+
// lexical and it is live for longer than the temporary.
514+
if (tempObj->isLexical()) {
515+
// TODO: Determine whether the base of the copy_addr's source is lexical and
516+
// its live range contains the range in which the alloc_stack
517+
// contains the value copied into it via the copy_addr.
518+
//
519+
// For now, only look for guaranteed arguments.
520+
auto storage = AccessStorageWithBase::compute(copyInst->getSrc());
521+
if (!storage.base)
522+
return;
523+
if (auto *arg = dyn_cast<SILFunctionArgument>(storage.base))
524+
if (arg->getOwnershipKind() != OwnershipKind::Guaranteed)
525+
return;
526+
}
509527

510528
bool isOSSA = copyInst->getFunction()->hasOwnership();
511529

@@ -645,10 +663,20 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
645663
return std::next(si->getIterator());
646664
}
647665

648-
// If the storage corresponds to a source-level var, do not optimize here.
649-
// Mem2Reg will transform the lexical alloc_stack into a lexical begin_borrow
650-
// which will ensure that the value's lifetime isn't observably shortened.
666+
// If the temporary storage is lexical, it either came from a source-level var
667+
// or was marked lexical because it was passed to a function that has been
668+
// inlined.
669+
// TODO: [begin_borrow_addr] Once we can mark addresses as being borrowed, we
670+
// won't need to mark alloc_stacks lexical during inlining. At that
671+
// point, the above comment should change, but the implementation
672+
// remains the same.
673+
//
674+
// In either case, we can eliminate the temporary if the source of the store
675+
// is lexical and it is live for longer than the temporary.
651676
if (tempObj->isLexical()) {
677+
// TODO: Find the lexical root of the source, if any, and allow optimization
678+
// if its live range contains the range in which the alloc_stack
679+
// contains the value stored into it.
652680
return std::next(si->getIterator());
653681
}
654682

test/SILOptimizer/temp_rvalue_opt_ossa.sil

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ class Klass {
1919
init()
2020
}
2121

22+
class OtherClass {
23+
var klass: Klass
24+
}
25+
2226
struct Two {
2327
var a: Klass
2428
var b: Klass
@@ -1544,3 +1548,35 @@ bb1(%6 : @guaranteed $Klass):
15441548
return %res : $()
15451549
}
15461550

1551+
// Lexical alloc_stacks can still be eliminated if their sources are guaranteed
1552+
// function arguments (because those are lexical and have a live range that
1553+
// contains the live range of the value stored into the alloc stack).
1554+
// CHECK-LABEL: sil [ossa] @copy_addr__lexical_alloc_stack_temporary__guaranteed_function_arg : {{.*}} {
1555+
// CHECK-NOT: alloc_stack
1556+
// CHECK-LABEL: } // end sil function 'copy_addr__lexical_alloc_stack_temporary__guaranteed_function_arg'
1557+
sil [ossa] @copy_addr__lexical_alloc_stack_temporary__guaranteed_function_arg : $@convention(thin) (@guaranteed OtherClass) -> () {
1558+
entry(%instance : @guaranteed $OtherClass):
1559+
%field_ptr = ref_element_addr %instance : $OtherClass, #OtherClass.klass
1560+
%addr = alloc_stack [lexical] $Klass
1561+
copy_addr %field_ptr to [initialization] %addr : $*Klass
1562+
destroy_addr %addr : $*Klass
1563+
dealloc_stack %addr : $*Klass
1564+
%retval = tuple ()
1565+
return %retval : $()
1566+
}
1567+
1568+
// Lexical alloc_stacks can't be eliminated even if their source is a function
1569+
// argument if that function argument is inout.
1570+
// CHECK-LABEL: sil [ossa] @copy_addr__lexical_alloc_stack_temporary__inout_function_arg : {{.*}} {
1571+
// CHECK: alloc_stack [lexical]
1572+
// CHECK-LABEL: } // end sil function 'copy_addr__lexical_alloc_stack_temporary__inout_function_arg'
1573+
sil [ossa] @copy_addr__lexical_alloc_stack_temporary__inout_function_arg : $@convention(thin) (@inout NonTrivialStruct) -> () {
1574+
entry(%instance : $*NonTrivialStruct):
1575+
%field_ptr = struct_element_addr %instance : $*NonTrivialStruct, #NonTrivialStruct.val
1576+
%addr = alloc_stack [lexical] $Klass
1577+
copy_addr %field_ptr to [initialization] %addr : $*Klass
1578+
destroy_addr %addr : $*Klass
1579+
dealloc_stack %addr : $*Klass
1580+
%retval = tuple ()
1581+
return %retval : $()
1582+
}

0 commit comments

Comments
 (0)