mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
LargeTypesReg2Mem: Peephole load followed by single user to reuse the load's address
For example the pattern:
```
%e = load %1 : SomeEnum
switch_enum %e
```
The switch_enum_addr should reuse the load's address operand instead of
creating a new location for the SSA value of %e.
While we are there also don't create a new stack location for extracting payload
in a switch_enum_addr case target basic block if that extracted payload is not
used. That is if the basic block arguments in all switch target blocks are
unused in the switch_enum version.
```
switch_enum %enum, case #Optional.some!enumelt: bb1,
case #Optional.none!enumelt: bb2
bb2(%payload : $Payload):
// unused %payload value
```
rdar://156602951
This commit is contained in:
@@ -3927,6 +3927,13 @@ void AddressAssignment::finish(DominanceInfo *dominance,
|
||||
StackNesting::fixNesting(&currFn);
|
||||
}
|
||||
|
||||
static bool isSoleUserOf(LoadInst *load, SILInstruction *next) {
|
||||
if (!load->hasOneUse())
|
||||
return false;
|
||||
if(load->getSingleUse()->getUser() != next)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
namespace {
|
||||
class AssignAddressToDef : SILInstructionVisitor<AssignAddressToDef> {
|
||||
friend SILVisitorBase<AssignAddressToDef>;
|
||||
@@ -4036,6 +4043,14 @@ protected:
|
||||
}
|
||||
|
||||
void visitLoadInst(LoadInst *load) {
|
||||
// Forward the address of the load if its sole user immediately follows the
|
||||
// load instructions.
|
||||
if (isSoleUserOf(load, &*++load->getIterator())) {
|
||||
assignment.markForDeletion(load);
|
||||
assignment.mapValueToAddress(origValue, load->getOperand());
|
||||
return;
|
||||
}
|
||||
|
||||
auto builder = assignment.getBuilder(load->getIterator());
|
||||
auto addr = assignment.createAllocStack(load->getType());
|
||||
|
||||
@@ -4464,22 +4479,35 @@ protected:
|
||||
|
||||
void visitSwitchEnumInst(SwitchEnumInst *sw) {
|
||||
auto opdAddr = assignment.getAddressForValue(sw->getOperand());
|
||||
{
|
||||
// UncheckedTakeEnumDataAddr is destructive. If we have a used switch target
|
||||
// block argument we need to provide for a destructible location for the
|
||||
// UncheckedTakeEnumDataAddr.
|
||||
SILValue destructibleAddress;
|
||||
auto initDestructibleAddress = [&] () -> void{
|
||||
if (destructibleAddress)
|
||||
return;
|
||||
auto addr = assignment.createAllocStack(sw->getOperand()->getType());
|
||||
// UncheckedTakeEnumDataAddr is destructive.
|
||||
// So we need to copy to keep the original address location valid.
|
||||
auto builder = assignment.getBuilder(sw->getIterator());
|
||||
builder.createCopyAddr(sw->getLoc(), opdAddr, addr, IsTake,
|
||||
IsInitialization);
|
||||
opdAddr = addr;
|
||||
}
|
||||
destructibleAddress = addr;
|
||||
};
|
||||
|
||||
auto loc = sw->getLoc();
|
||||
|
||||
auto rewriteCase = [&](EnumElementDecl *caseDecl, SILBasicBlock *caseBB) {
|
||||
// Nothing to do for unused case payloads.
|
||||
if (caseBB->getArguments().size() == 0)
|
||||
if (caseBB->getArguments().size() == 0 ||
|
||||
caseBB->getArguments()[0]->use_empty()) {
|
||||
if (caseBB->getArguments().size()) {
|
||||
assignment.markBlockArgumentForDeletion(caseBB);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
initDestructibleAddress();
|
||||
|
||||
assert(caseBB->getArguments().size() == 1);
|
||||
SILArgument *caseArg = caseBB->getArgument(0);
|
||||
@@ -4488,8 +4516,10 @@ protected:
|
||||
|
||||
SILBuilder caseBuilder = assignment.getBuilder(caseBB->begin());
|
||||
auto *caseAddr =
|
||||
caseBuilder.createUncheckedTakeEnumDataAddr(loc, opdAddr, caseDecl,
|
||||
caseArg->getType().getAddressType());
|
||||
caseBuilder.createUncheckedTakeEnumDataAddr(loc, destructibleAddress,
|
||||
caseDecl,
|
||||
caseArg->getType().getAddressType());
|
||||
|
||||
if (assignment.isLargeLoadableType(caseArg->getType())) {
|
||||
assignment.mapValueToAddress(caseArg, caseAddr);
|
||||
assignment.markBlockArgumentForDeletion(caseBB);
|
||||
|
||||
@@ -80,17 +80,14 @@ unwind:
|
||||
// CHECK-LABEL: sil @use_yield_big : $@convention(thin) () -> () {
|
||||
// CHECK: bb0:
|
||||
// CHECK-NEXT: [[TEMP:%.*]] = alloc_stack $BigStruct
|
||||
// CHECK-NEXT: [[TEMP2:%.*]] = alloc_stack $BigStruct
|
||||
// CHECK-NEXT: // function_ref
|
||||
// CHECK-NEXT: [[CORO:%.*]] = function_ref @test_yield_big : $@yield_once @convention(thin) () -> @yields @in_guaranteed BigStruct
|
||||
// CHECK-NEXT: ([[ADDR:%.*]], [[TOKEN:%.*]]) = begin_apply [[CORO]]()
|
||||
// CHECK-NEXT: copy_addr [take] [[ADDR]] to [init] [[TEMP]] : $*BigStruct
|
||||
// CHECK-NEXT: copy_addr [take] [[TEMP]] to [init] [[TEMP2]] : $*BigStruct
|
||||
// CHECK-NEXT: // function_ref
|
||||
// CHECK-NEXT: [[USE:%.*]] = function_ref @use_big_struct : $@convention(thin) (@in_guaranteed BigStruct) -> ()
|
||||
// CHECK-NEXT: apply [[USE]]([[TEMP2]])
|
||||
// CHECK-NEXT: apply [[USE]]([[TEMP]])
|
||||
// CHECK-NEXT: [[RET:%.*]] = end_apply [[TOKEN]] as $()
|
||||
// CHECK-NEXT: dealloc_stack [[TEMP2]] : $*BigStruct
|
||||
// CHECK-NEXT: dealloc_stack [[TEMP]] : $*BigStruct
|
||||
// CHECK-NEXT: return [[RET]] : $()
|
||||
sil @use_yield_big : $@convention(thin) () -> () {
|
||||
|
||||
@@ -66,15 +66,12 @@ struct Small {
|
||||
// CHECK: bb0:
|
||||
// CHECK: %0 = alloc_stack $Optional<X>
|
||||
// CHECK: %1 = alloc_stack $X
|
||||
// CHECK: %2 = alloc_stack $X
|
||||
// CHECK: %3 = alloc_stack $Optional<X>
|
||||
// CHECK: copy_addr [take] %2 to [init] %1 : $*X
|
||||
// CHECK: %5 = init_enum_data_addr %0 : $*Optional<X>, #Optional.some!enumelt
|
||||
// CHECK: copy_addr [take] %1 to [init] %5 : $*X
|
||||
// CHECK: %2 = alloc_stack $Optional<X>
|
||||
// CHECK: %3 = init_enum_data_addr %0 : $*Optional<X>, #Optional.some!enumelt
|
||||
// CHECK: copy_addr [take] %1 to [init] %3 : $*X
|
||||
// CHECK: inject_enum_addr %0 : $*Optional<X>, #Optional.some!enumelt
|
||||
// CHECK: copy_addr [take] %0 to [init] %3 : $*Optional<X>
|
||||
// CHECK: dealloc_stack %3 : $*Optional<X>
|
||||
// CHECK: dealloc_stack %2 : $*X
|
||||
// CHECK: copy_addr [take] %0 to [init] %2 : $*Optional<X>
|
||||
// CHECK: dealloc_stack %2 : $*Optional<X>
|
||||
// CHECK: dealloc_stack %1 : $*X
|
||||
// CHECK: dealloc_stack %0 : $*Optional<X>
|
||||
// CHECK: } // end sil function 'test1'
|
||||
@@ -92,17 +89,21 @@ bb0:
|
||||
return %t : $()
|
||||
}
|
||||
|
||||
sil @useX : $@convention(thin) (X) -> ()
|
||||
|
||||
// CHECK: sil @test2 : $@convention(thin) () -> () {
|
||||
// CHECK: bb0:
|
||||
// CHECK: %0 = alloc_stack $Optional<X>
|
||||
// CHECK: %1 = alloc_stack $Optional<X>
|
||||
// CHECK: %1 = alloc_stack $X
|
||||
// CHECK: %2 = alloc_stack $Optional<X>
|
||||
// CHECK: copy_addr [take] %2 to [init] %1 : $*Optional<X>
|
||||
// CHECK: copy_addr [take] %1 to [init] %0 : $*Optional<X>
|
||||
// CHECK: switch_enum_addr %0 : $*Optional<X>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
|
||||
// CHECK: copy_addr [take] %2 to [init] %0 : $*Optional<X>
|
||||
// CHECK: switch_enum_addr %2 : $*Optional<X>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
|
||||
|
||||
// CHECK: bb1:
|
||||
// CHECK: %6 = unchecked_take_enum_data_addr %0 : $*Optional<X>, #Optional.some!enumelt
|
||||
// CHECK: %5 = unchecked_take_enum_data_addr %0 : $*Optional<X>, #Optional.some!enumelt
|
||||
// CHECK: copy_addr [take] %5 to [init] %1 : $*X
|
||||
// CHECK: %7 = function_ref @useX : $@convention(thin) (@in_guaranteed X) -> ()
|
||||
// CHECK: %8 = apply %7(%1) : $@convention(thin) (@in_guaranteed X) -> ()
|
||||
// CHECK: br bb3
|
||||
|
||||
// CHECK: bb2:
|
||||
@@ -110,7 +111,7 @@ bb0:
|
||||
|
||||
// CHECK: bb3:
|
||||
// CHECK: dealloc_stack %2 : $*Optional<X>
|
||||
// CHECK: dealloc_stack %1 : $*Optional<X>
|
||||
// CHECK: dealloc_stack %1 : $*X
|
||||
// CHECK: dealloc_stack %0 : $*Optional<X>
|
||||
// CHECK: } // end sil function 'test2'
|
||||
|
||||
@@ -121,6 +122,8 @@ bb0:
|
||||
switch_enum %2 : $Optional<X>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
|
||||
|
||||
bb1(%3: $X):
|
||||
%f = function_ref @useX : $@convention(thin) (X) -> ()
|
||||
apply %f(%3) : $@convention(thin) (X) -> ()
|
||||
br bb3
|
||||
|
||||
bb2:
|
||||
@@ -184,11 +187,9 @@ bb0:
|
||||
|
||||
// CHECK: sil @test5 : $@convention(thin) (@in (X, X)) -> () {
|
||||
// CHECK: bb0(%0 : $*(X, X)):
|
||||
// CHECK: %1 = alloc_stack $(X, X)
|
||||
// CHECK: %2 = alloc_stack $X
|
||||
// CHECK: copy_addr [take] %0 to [init] %1 : $*(X, X)
|
||||
// CHECK: %4 = tuple_element_addr %1 : $*(X, X), 1
|
||||
// CHECK: copy_addr [take] %4 to [init] %2 : $*X
|
||||
// CHECK: %1 = alloc_stack $X
|
||||
// CHECK: %2 = tuple_element_addr %0 : $*(X, X), 1
|
||||
// CHECK: copy_addr [take] %2 to [init] %1 : $*X
|
||||
// CHECK: } // end sil function 'test5'
|
||||
|
||||
sil @test5 : $@convention(thin) (@in (X, X)) -> () {
|
||||
@@ -231,11 +232,9 @@ bb0:
|
||||
|
||||
// CHECK: sil @test7 : $@convention(thin) (@in Y) -> () {
|
||||
// CHECK: bb0(%0 : $*Y):
|
||||
// CHECK: %1 = alloc_stack $Y
|
||||
// CHECK: %2 = alloc_stack $X
|
||||
// CHECK: copy_addr [take] %0 to [init] %1 : $*Y
|
||||
// CHECK: %4 = struct_element_addr %1 : $*Y, #Y.y1
|
||||
// CHECK: copy_addr [take] %4 to [init] %2 : $*X
|
||||
// CHECK: %1 = alloc_stack $X
|
||||
// CHECK: %2 = struct_element_addr %0 : $*Y, #Y.y1
|
||||
// CHECK: copy_addr [take] %2 to [init] %1 : $*X
|
||||
// CHECK: } // end sil function 'test7'
|
||||
|
||||
sil @test7 : $@convention(thin) (@in Y) -> () {
|
||||
@@ -339,8 +338,8 @@ bb0:
|
||||
}
|
||||
|
||||
// CHECK: sil @test13
|
||||
// CHECK: [[ADDR:%.*]] = unchecked_addr_cast %1 : $*X to $*Y
|
||||
// CHECK: copy_addr [take] [[ADDR]] to [init] %2 : $*Y
|
||||
// CHECK: [[ADDR:%.*]] = unchecked_addr_cast %2 : $*X to $*Y
|
||||
// CHECK: copy_addr [take] [[ADDR]] to [init] %1 : $*Y
|
||||
// CHECK: } // end sil function 'test13'
|
||||
sil @test13 : $@convention(thin) (@in X) -> () {
|
||||
bb0(%0 : $*X):
|
||||
@@ -443,3 +442,24 @@ bb0(%0 : $String):
|
||||
%13 = tuple ()
|
||||
return %13 : $()
|
||||
}
|
||||
|
||||
sil_global private @global : $Optional<X>
|
||||
|
||||
sil @test18: $@convention(thin) () -> () {
|
||||
bb0:
|
||||
%0 = global_addr @global : $*Optional<X>
|
||||
%1 = begin_access [modify] [static] [no_nested_conflict] %0
|
||||
%2 = load %1
|
||||
switch_enum %2, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb1
|
||||
|
||||
bb1:
|
||||
%4 = integer_literal $Builtin.Int1, -1
|
||||
cond_fail %4, "Unexpectedly found nil while unwrapping an Optional value"
|
||||
unreachable
|
||||
|
||||
bb2(%7 : $X):
|
||||
%8 = unchecked_take_enum_data_addr %1, #Optional.some!enumelt
|
||||
end_access %1
|
||||
%13 = tuple ()
|
||||
return %13 : $()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user