diff --git a/lib/IRGen/LoadableByAddress.cpp b/lib/IRGen/LoadableByAddress.cpp index 349539a83e3..042d8e5b589 100644 --- a/lib/IRGen/LoadableByAddress.cpp +++ b/lib/IRGen/LoadableByAddress.cpp @@ -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 { friend SILVisitorBase; @@ -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); diff --git a/test/IRGen/big_types.sil b/test/IRGen/big_types.sil index b50b5d96582..109c7c02c2e 100644 --- a/test/IRGen/big_types.sil +++ b/test/IRGen/big_types.sil @@ -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) () -> () { diff --git a/test/IRGen/loadable_by_address_reg2mem.sil b/test/IRGen/loadable_by_address_reg2mem.sil index 271a208be94..a28b4ffc768 100644 --- a/test/IRGen/loadable_by_address_reg2mem.sil +++ b/test/IRGen/loadable_by_address_reg2mem.sil @@ -66,15 +66,12 @@ struct Small { // CHECK: bb0: // CHECK: %0 = alloc_stack $Optional // CHECK: %1 = alloc_stack $X -// CHECK: %2 = alloc_stack $X -// CHECK: %3 = alloc_stack $Optional -// CHECK: copy_addr [take] %2 to [init] %1 : $*X -// CHECK: %5 = init_enum_data_addr %0 : $*Optional, #Optional.some!enumelt -// CHECK: copy_addr [take] %1 to [init] %5 : $*X +// CHECK: %2 = alloc_stack $Optional +// CHECK: %3 = init_enum_data_addr %0 : $*Optional, #Optional.some!enumelt +// CHECK: copy_addr [take] %1 to [init] %3 : $*X // CHECK: inject_enum_addr %0 : $*Optional, #Optional.some!enumelt -// CHECK: copy_addr [take] %0 to [init] %3 : $*Optional -// CHECK: dealloc_stack %3 : $*Optional -// CHECK: dealloc_stack %2 : $*X +// CHECK: copy_addr [take] %0 to [init] %2 : $*Optional +// CHECK: dealloc_stack %2 : $*Optional // CHECK: dealloc_stack %1 : $*X // CHECK: dealloc_stack %0 : $*Optional // 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 -// CHECK: %1 = alloc_stack $Optional +// CHECK: %1 = alloc_stack $X // CHECK: %2 = alloc_stack $Optional -// CHECK: copy_addr [take] %2 to [init] %1 : $*Optional -// CHECK: copy_addr [take] %1 to [init] %0 : $*Optional -// CHECK: switch_enum_addr %0 : $*Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 +// CHECK: copy_addr [take] %2 to [init] %0 : $*Optional +// CHECK: switch_enum_addr %2 : $*Optional, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2 // CHECK: bb1: -// CHECK: %6 = unchecked_take_enum_data_addr %0 : $*Optional, #Optional.some!enumelt +// CHECK: %5 = unchecked_take_enum_data_addr %0 : $*Optional, #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 -// CHECK: dealloc_stack %1 : $*Optional +// CHECK: dealloc_stack %1 : $*X // CHECK: dealloc_stack %0 : $*Optional // CHECK: } // end sil function 'test2' @@ -121,6 +122,8 @@ bb0: switch_enum %2 : $Optional, 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 + +sil @test18: $@convention(thin) () -> () { +bb0: + %0 = global_addr @global : $*Optional + %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 : $() +}