Skip to content

Commit 28220ff

Browse files
committed
SILCombine: make the alloc_stack-enum optimization a bit more tolerant.
When eliminating an enum from an alloc_stack, accept "dead" inject_enum_addr instructions, which inject different enum cases.
1 parent 44c8052 commit 28220ff

File tree

3 files changed

+98
-9
lines changed

3 files changed

+98
-9
lines changed

lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,10 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) {
453453
return false;
454454

455455
EnumElementDecl *element = nullptr;
456+
unsigned numInits =0;
457+
unsigned numTakes = 0;
458+
SILBasicBlock *initBlock = nullptr;
459+
SILBasicBlock *takeBlock = nullptr;
456460
SILType payloadType;
457461

458462
// First step: check if the stack location is only used to hold one specific
@@ -463,6 +467,8 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) {
463467
case SILInstructionKind::DebugValueAddrInst:
464468
case SILInstructionKind::DestroyAddrInst:
465469
case SILInstructionKind::DeallocStackInst:
470+
case SILInstructionKind::InjectEnumAddrInst:
471+
// We'll check init_enum_addr below.
466472
break;
467473
case SILInstructionKind::InitEnumDataAddrInst: {
468474
auto *ieda = cast<InitEnumDataAddrInst>(user);
@@ -472,20 +478,17 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) {
472478
element = el;
473479
assert(!payloadType || payloadType == ieda->getType());
474480
payloadType = ieda->getType();
475-
break;
476-
}
477-
case SILInstructionKind::InjectEnumAddrInst: {
478-
auto *el = cast<InjectEnumAddrInst>(user)->getElement();
479-
if (element && el != element)
480-
return false;
481-
element = el;
481+
numInits++;
482+
initBlock = user->getParent();
482483
break;
483484
}
484485
case SILInstructionKind::UncheckedTakeEnumDataAddrInst: {
485486
auto *el = cast<UncheckedTakeEnumDataAddrInst>(user)->getElement();
486487
if (element && el != element)
487488
return false;
488489
element = el;
490+
numTakes++;
491+
takeBlock = user->getParent();
489492
break;
490493
}
491494
default:
@@ -495,6 +498,24 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) {
495498
if (!element || !payloadType)
496499
return false;
497500

501+
// If the enum has a single init-take pair in a single block, we know that
502+
// the enum cannot contain any valid payload outside that init-take pair.
503+
//
504+
// This also means that we can ignore any inject_enum_addr of another enum
505+
// case, because this can only inject a case without a payload.
506+
bool singleInitTakePair =
507+
(numInits == 1 && numTakes == 1 && initBlock == takeBlock);
508+
if (!singleInitTakePair) {
509+
// No single init-take pair: We cannot ignore inject_enum_addrs with a
510+
// mismatching case.
511+
for (auto *use : AS->getUses()) {
512+
if (auto *inject = dyn_cast<InjectEnumAddrInst>(use->getUser())) {
513+
if (inject->getElement() != element)
514+
return false;
515+
}
516+
}
517+
}
518+
498519
// Second step: replace the enum alloc_stack with a payload alloc_stack.
499520
auto *newAlloc = Builder.createAllocStack(
500521
AS->getLoc(), payloadType, AS->getVarInfo(), AS->hasDynamicLifetime());
@@ -508,6 +529,22 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) {
508529
eraseInstFromFunction(*user);
509530
break;
510531
case SILInstructionKind::DestroyAddrInst:
532+
if (singleInitTakePair) {
533+
// It's not possible that the enum has a payload at the destroy_addr,
534+
// because it must have already been taken by the take of the
535+
// single init-take pair.
536+
// We _have_ to remove the destroy_addr, because we also remove all
537+
// inject_enum_addrs which might inject a payload-less case before
538+
// the destroy_addr.
539+
eraseInstFromFunction(*user);
540+
} else {
541+
// The enum payload can still be valid at the destroy_addr, so we have
542+
// to keep the destroy_addr. Just replace the enum with the payload
543+
// (and because it's not a singleInitTakePair, we can be sure that the
544+
// enum cannot have any other case than the payload case).
545+
use->set(newAlloc);
546+
}
547+
break;
511548
case SILInstructionKind::DeallocStackInst:
512549
use->set(newAlloc);
513550
break;

test/SILOptimizer/cast_optimizer_conditional_conformance.sil

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,6 @@ bb6:
9898
// CHECK: [[IEDA:%.*]] = init_enum_data_addr [[OPT]] : $*Optional<HasFoo>, #Optional.some!enumelt
9999
// CHECK: checked_cast_addr_br take_always S1<Int8> in [[VAL]] : $*S1<Int8> to HasFoo in [[IEDA]] : $*HasFoo, bb1, bb2
100100
// bbs...
101-
// CHECK: switch_enum_addr [[OPT]] : $*Optional<HasFoo>, case #Optional.some!enumelt: bb4, case #Optional.none!enumelt: bb5
102-
// bbs...
103101
// CHECK: [[UNWRAP:%.*]] = unchecked_take_enum_data_addr [[OPT]] : $*Optional<HasFoo>, #Optional.some!enumelt
104102
// CHECK: copy_addr [take] [[UNWRAP]] to [initialization] [[EXIS]] : $*HasFoo
105103
// CHECK: [[OPEN:%.*]] = open_existential_addr immutable_access [[EXIS]] : $*HasFoo to $*@opened("4E16CBC0-FD9F-11E8-A311-D0817AD9F6DD") HasFoo

test/SILOptimizer/sil_combine_enums.sil

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ enum MP {
504504
}
505505

506506
sil @take_s : $@convention(thin) (@in S) -> ()
507+
sil @take_t : $@convention(thin) (@in T) -> ()
507508
sil @use_mp : $@convention(thin) (@in_guaranteed MP) -> ()
508509

509510
// CHECK-LABEL: sil @expand_alloc_stack_of_enum1
@@ -568,6 +569,37 @@ bb3:
568569
return %11 : $()
569570
}
570571

572+
// CHECK-LABEL: sil @expand_alloc_stack_of_enum_multiple_cases
573+
// CHECK: [[A:%[0-9]+]] = alloc_stack $T
574+
// CHECK: bb1:
575+
// CHECK-NEXT: store %0 to [[A]]
576+
// CHECK: apply {{%[0-9]+}}([[A]])
577+
// CHECK: bb2:
578+
// CHECK-NEXT: br bb3
579+
// CHECK: bb3:
580+
// CHECK: } // end sil function 'expand_alloc_stack_of_enum_multiple_cases'
581+
sil @expand_alloc_stack_of_enum_multiple_cases : $@convention(method) (T) -> () {
582+
bb0(%0 : $T):
583+
%1 = alloc_stack $X
584+
cond_br undef, bb1, bb2
585+
bb1:
586+
%2 = init_enum_data_addr %1 : $*X, #X.some!enumelt
587+
store %0 to %2 : $*T
588+
inject_enum_addr %1 : $*X, #X.some!enumelt
589+
%7 = unchecked_take_enum_data_addr %1 : $*X, #X.some!enumelt
590+
%8 = function_ref @take_t : $@convention(thin) (@in T) -> ()
591+
%9 = apply %8(%7) : $@convention(thin) (@in T) -> ()
592+
br bb3
593+
bb2:
594+
inject_enum_addr %1 : $*X, #X.none!enumelt
595+
destroy_addr %1 : $*X
596+
br bb3
597+
bb3:
598+
dealloc_stack %1 : $*X
599+
%11 = tuple ()
600+
return %11 : $()
601+
}
602+
571603
// CHECK-LABEL: sil @dont_expand_alloc_stack_of_enum_multiple_cases
572604
// CHECK: alloc_stack $MP
573605
// CHECK: } // end sil function 'dont_expand_alloc_stack_of_enum_multiple_cases'
@@ -592,6 +624,28 @@ bb3:
592624
return %11 : $()
593625
}
594626

627+
// CHECK-LABEL: sil @dont_expand_alloc_stack_of_enum_multiple_cases2
628+
// CHECK: alloc_stack $X
629+
// CHECK: } // end sil function 'dont_expand_alloc_stack_of_enum_multiple_cases2'
630+
sil @dont_expand_alloc_stack_of_enum_multiple_cases2 : $@convention(method) (T) -> () {
631+
bb0(%0 : $T):
632+
%1 = alloc_stack $X
633+
cond_br undef, bb1, bb2
634+
bb1:
635+
%2 = init_enum_data_addr %1 : $*X, #X.some!enumelt
636+
store %0 to %2 : $*T
637+
inject_enum_addr %1 : $*X, #X.some!enumelt
638+
br bb3
639+
bb2:
640+
inject_enum_addr %1 : $*X, #X.none!enumelt
641+
br bb3
642+
bb3:
643+
destroy_addr %1 : $*X
644+
dealloc_stack %1 : $*X
645+
%11 = tuple ()
646+
return %11 : $()
647+
}
648+
595649
// CHECK-LABEL: sil @dont_expand_alloc_stack_of_enum_unknown_use
596650
// CHECK: alloc_stack $MP
597651
// CHECK: } // end sil function 'dont_expand_alloc_stack_of_enum_unknown_use'

0 commit comments

Comments
 (0)