@@ -453,6 +453,10 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) {
453
453
return false ;
454
454
455
455
EnumElementDecl *element = nullptr ;
456
+ unsigned numInits =0 ;
457
+ unsigned numTakes = 0 ;
458
+ SILBasicBlock *initBlock = nullptr ;
459
+ SILBasicBlock *takeBlock = nullptr ;
456
460
SILType payloadType;
457
461
458
462
// First step: check if the stack location is only used to hold one specific
@@ -463,6 +467,8 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) {
463
467
case SILInstructionKind::DebugValueAddrInst:
464
468
case SILInstructionKind::DestroyAddrInst:
465
469
case SILInstructionKind::DeallocStackInst:
470
+ case SILInstructionKind::InjectEnumAddrInst:
471
+ // We'll check init_enum_addr below.
466
472
break ;
467
473
case SILInstructionKind::InitEnumDataAddrInst: {
468
474
auto *ieda = cast<InitEnumDataAddrInst>(user);
@@ -472,20 +478,17 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) {
472
478
element = el;
473
479
assert (!payloadType || payloadType == ieda->getType ());
474
480
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 ();
482
483
break ;
483
484
}
484
485
case SILInstructionKind::UncheckedTakeEnumDataAddrInst: {
485
486
auto *el = cast<UncheckedTakeEnumDataAddrInst>(user)->getElement ();
486
487
if (element && el != element)
487
488
return false ;
488
489
element = el;
490
+ numTakes++;
491
+ takeBlock = user->getParent ();
489
492
break ;
490
493
}
491
494
default :
@@ -495,6 +498,24 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) {
495
498
if (!element || !payloadType)
496
499
return false ;
497
500
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
+
498
519
// Second step: replace the enum alloc_stack with a payload alloc_stack.
499
520
auto *newAlloc = Builder.createAllocStack (
500
521
AS->getLoc (), payloadType, AS->getVarInfo (), AS->hasDynamicLifetime ());
@@ -508,6 +529,22 @@ bool SILCombiner::optimizeStackAllocatedEnum(AllocStackInst *AS) {
508
529
eraseInstFromFunction (*user);
509
530
break ;
510
531
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 ;
511
548
case SILInstructionKind::DeallocStackInst:
512
549
use->set (newAlloc);
513
550
break ;
0 commit comments