@@ -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 ;
0 commit comments