@@ -183,23 +183,43 @@ class MemoryToRegisters {
183
183
// / Returns true if \p I is an address of a LoadInst, skipping struct and
184
184
// / tuple address projections. Sets \p singleBlock to null if the load (or
185
185
// / it's address is not in \p singleBlock.
186
- static bool isAddressForLoad (SILInstruction *I, SILBasicBlock *&singleBlock) {
187
-
188
- if (isa<LoadInst>(I))
186
+ // / This function looks for these patterns:
187
+ // / 1. (load %ASI)
188
+ // / 2. (load (struct_element_addr/tuple_element_addr/unchecked_addr_cast %ASI))
189
+ static bool isAddressForLoad (SILInstruction *I, SILBasicBlock *&singleBlock,
190
+ bool &hasGuaranteedOwnership) {
191
+
192
+ if (isa<LoadInst>(I)) {
193
+ // SILMem2Reg is disabled when we find:
194
+ // (load [take] (struct_element_addr/tuple_element_addr %ASI))
195
+ // struct_element_addr and tuple_element_addr are lowered into
196
+ // struct_extract and tuple_extract and these SIL instructions have a
197
+ // guaranteed ownership. For replacing load's users, we need an owned value.
198
+ // We will need a new copy and destroy of the running val placed after the
199
+ // last use. This is not implemented currently.
200
+ if (hasGuaranteedOwnership && cast<LoadInst>(I)->getOwnershipQualifier () ==
201
+ LoadOwnershipQualifier::Take) {
202
+ return false ;
203
+ }
189
204
return true ;
205
+ }
190
206
191
207
if (!isa<UncheckedAddrCastInst>(I) && !isa<StructElementAddrInst>(I) &&
192
208
!isa<TupleElementAddrInst>(I))
193
209
return false ;
194
-
210
+
211
+ if (isa<StructElementAddrInst>(I) || isa<TupleElementAddrInst>(I)) {
212
+ hasGuaranteedOwnership = true ;
213
+ }
214
+
195
215
// Recursively search for other (non-)loads in the instruction's uses.
196
216
for (auto UI : cast<SingleValueInstruction>(I)->getUses ()) {
197
217
SILInstruction *II = UI->getUser ();
198
218
if (II->getParent () != singleBlock)
199
219
singleBlock = nullptr ;
200
-
201
- if (!isAddressForLoad (II, singleBlock))
202
- return false ;
220
+
221
+ if (!isAddressForLoad (II, singleBlock, hasGuaranteedOwnership ))
222
+ return false ;
203
223
}
204
224
return true ;
205
225
}
@@ -233,7 +253,8 @@ static bool isCaptured(AllocStackInst *ASI, bool &inSingleBlock) {
233
253
singleBlock = nullptr ;
234
254
235
255
// Loads are okay.
236
- if (isAddressForLoad (II, singleBlock))
256
+ bool hasGuaranteedOwnership = false ;
257
+ if (isAddressForLoad (II, singleBlock, hasGuaranteedOwnership))
237
258
continue ;
238
259
239
260
// We can store into an AllocStack (but not the pointer).
@@ -348,21 +369,54 @@ static void collectLoads(SILInstruction *I, SmallVectorImpl<LoadInst *> &Loads)
348
369
static void replaceLoad (LoadInst *LI, SILValue val, AllocStackInst *ASI) {
349
370
ProjectionPath projections (val->getType ());
350
371
SILValue op = LI->getOperand ();
372
+ SILBuilderWithScope builder (LI);
373
+
351
374
while (op != ASI) {
352
375
assert (isa<UncheckedAddrCastInst>(op) || isa<StructElementAddrInst>(op) ||
353
376
isa<TupleElementAddrInst>(op));
354
377
auto *Inst = cast<SingleValueInstruction>(op);
355
378
projections.push_back (Projection (Inst));
356
379
op = Inst->getOperand (0 );
357
380
}
358
- SILBuilder builder (LI);
381
+
382
+ SmallVector<SILValue, 4 > borrowedVals;
359
383
for (auto iter = projections.rbegin (); iter != projections.rend (); ++iter) {
360
384
const Projection &projection = *iter;
385
+ assert (projection.getKind () == ProjectionKind::BitwiseCast ||
386
+ projection.getKind () == ProjectionKind::Struct ||
387
+ projection.getKind () == ProjectionKind::Tuple);
388
+
389
+ // struct_extract and tuple_extract expect guaranteed operand ownership
390
+ // non-trivial RunningVal is owned. Insert borrow operation to convert
391
+ if (projection.getKind () == ProjectionKind::Struct ||
392
+ projection.getKind () == ProjectionKind::Tuple) {
393
+ SILValue opVal = builder.emitBeginBorrowOperation (LI->getLoc (), val);
394
+ if (opVal != val) {
395
+ borrowedVals.push_back (opVal);
396
+ val = opVal;
397
+ }
398
+ }
361
399
val = projection.createObjectProjection (builder, LI->getLoc (), val).get ();
362
400
}
401
+
363
402
op = LI->getOperand ();
364
- LI->replaceAllUsesWith (val);
403
+ // Replace users of the loaded value with `val`
404
+ // If we have a load [copy], replace the users with copy_value of `val`
405
+ if (LI->getOwnershipQualifier () == LoadOwnershipQualifier::Copy) {
406
+ LI->replaceAllUsesWith (builder.createCopyValue (LI->getLoc (), val));
407
+ } else {
408
+ assert (!ASI->getFunction ()->hasOwnership () ||
409
+ val.getOwnershipKind () != ValueOwnershipKind::Guaranteed);
410
+ LI->replaceAllUsesWith (val);
411
+ }
412
+
413
+ for (auto borrowedVal : borrowedVals) {
414
+ builder.emitEndBorrowOperation (LI->getLoc (), borrowedVal);
415
+ }
416
+
417
+ // Delete the load
365
418
LI->eraseFromParent ();
419
+
366
420
while (op != ASI && op->use_empty ()) {
367
421
assert (isa<UncheckedAddrCastInst>(op) || isa<StructElementAddrInst>(op) ||
368
422
isa<TupleElementAddrInst>(op));
@@ -399,6 +453,7 @@ StoreInst *
399
453
StackAllocationPromoter::promoteAllocationInBlock (SILBasicBlock *BB) {
400
454
LLVM_DEBUG (llvm::dbgs () << " *** Promoting ASI in block: " << *ASI);
401
455
456
+ // RunningVal is the current value in the stack location.
402
457
// We don't know the value of the alloca until we find the first store.
403
458
SILValue RunningVal = SILValue ();
404
459
// Keep track of the last StoreInst that we found.
@@ -415,12 +470,16 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) {
415
470
// If we are loading from the AllocStackInst and we already know the
416
471
// content of the Alloca then use it.
417
472
LLVM_DEBUG (llvm::dbgs () << " *** Promoting load: " << *Load);
418
-
419
473
replaceLoad (Load, RunningVal, ASI);
420
474
++NumInstRemoved;
421
- } else if (Load->getOperand () == ASI) {
475
+ } else if (Load->getOperand () == ASI &&
476
+ Load->getOwnershipQualifier () !=
477
+ LoadOwnershipQualifier::Copy) {
422
478
// If we don't know the content of the AllocStack then the loaded
423
479
// value *is* the new value;
480
+ // Don't use result of load [copy] as a RunningVal, it necessitates
481
+ // additional logic for cleanup of consuming instructions of the result.
482
+ // StackAllocationPromoter::fixBranchesAndUses will later handle it.
424
483
LLVM_DEBUG (llvm::dbgs () << " *** First load: " << *Load);
425
484
RunningVal = Load;
426
485
}
@@ -433,16 +492,51 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) {
433
492
if (SI->getDest () != ASI)
434
493
continue ;
435
494
436
- // The stored value is the new running value.
437
- RunningVal = SI->getSrc ();
495
+ // Special handling of entry block
496
+ // If we have a store [assign] in the first block, OSSA guarantees we can
497
+ // find the previous value stored in the stack location in RunningVal.
498
+ // Create destroy_value of the RunningVal.
499
+ // For all other blocks we may not know the previous value stored in the
500
+ // stack location. So we will create destroy_value in
501
+ // StackAllocationPromoter::fixBranchesAndUses, by getting the live-in
502
+ // value to the block.
503
+ if (BB->isEntry ()) {
504
+ if (SI->getOwnershipQualifier () == StoreOwnershipQualifier::Assign) {
505
+ assert (RunningVal);
506
+ SILBuilderWithScope (SI).createDestroyValue (SI->getLoc (), RunningVal);
507
+ }
508
+ }
438
509
439
510
// If we met a store before this one, delete it.
511
+ // If the LastStore was a store with [assign], delete it only if we know
512
+ // the RunningValue to destroy. If not, it will be deleted in
513
+ // StackAllocationPromoter::fixBranchesAndUses.
440
514
if (LastStore) {
441
- ++NumInstRemoved;
442
- LLVM_DEBUG (llvm::dbgs () << " *** Removing redundant store: "
443
- << *LastStore);
444
- LastStore->eraseFromParent ();
515
+ if (LastStore->getOwnershipQualifier () ==
516
+ StoreOwnershipQualifier::Assign) {
517
+ if (RunningVal) {
518
+ // For entry block, we would have already created the destroy_value,
519
+ // skip it.
520
+ if (!BB->isEntry ()) {
521
+ SILBuilderWithScope (LastStore).createDestroyValue (
522
+ LastStore->getLoc (), RunningVal);
523
+ }
524
+ LLVM_DEBUG (llvm::dbgs ()
525
+ << " *** Removing redundant store: " << *LastStore);
526
+ ++NumInstRemoved;
527
+ LastStore->eraseFromParent ();
528
+ }
529
+ } else {
530
+ LLVM_DEBUG (llvm::dbgs ()
531
+ << " *** Removing redundant store: " << *LastStore);
532
+ ++NumInstRemoved;
533
+ LastStore->eraseFromParent ();
534
+ }
445
535
}
536
+
537
+ // The stored value is the new running value.
538
+ RunningVal = SI->getSrc ();
539
+ // The current store is now the LastStore
446
540
LastStore = SI;
447
541
continue ;
448
542
}
@@ -466,6 +560,15 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) {
466
560
continue ;
467
561
}
468
562
563
+ if (auto *DVI = dyn_cast<DestroyValueInst>(Inst)) {
564
+ if (DVI->getOperand () == RunningVal) {
565
+ // Reset LastStore.
566
+ // So that we don't end up passing dead values as phi args in
567
+ // StackAllocationPromoter::fixBranchesAndUses
568
+ LastStore = nullptr ;
569
+ }
570
+ }
571
+
469
572
// Stop on deallocation.
470
573
if (auto *DSI = dyn_cast<DeallocStackInst>(Inst)) {
471
574
if (DSI->getOperand () == ASI) {
@@ -516,6 +619,10 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *ASI) {
516
619
// value.
517
620
if (auto *SI = dyn_cast<StoreInst>(Inst)) {
518
621
if (SI->getDest () == ASI) {
622
+ if (SI->getOwnershipQualifier () == StoreOwnershipQualifier::Assign) {
623
+ assert (RunningVal);
624
+ SILBuilderWithScope (SI).createDestroyValue (SI->getLoc (), RunningVal);
625
+ }
519
626
RunningVal = SI->getSrc ();
520
627
Inst->eraseFromParent ();
521
628
++NumInstRemoved;
@@ -647,6 +754,21 @@ void StackAllocationPromoter::fixPhiPredBlock(BlockSet &PhiBlocks,
647
754
TI->eraseFromParent ();
648
755
}
649
756
757
+ static bool hasOnlyUndefIncomingValues (SILPhiArgument *phiArg) {
758
+ SmallVector<SILValue, 8 > incomingValues;
759
+ phiArg->getIncomingPhiValues (incomingValues);
760
+ for (auto predArg : incomingValues) {
761
+ if (isa<SILUndef>(predArg))
762
+ continue ;
763
+ if (isa<SILPhiArgument>(predArg) &&
764
+ hasOnlyUndefIncomingValues (cast<SILPhiArgument>(predArg))) {
765
+ continue ;
766
+ }
767
+ return false ;
768
+ }
769
+ return true ;
770
+ }
771
+
650
772
void StackAllocationPromoter::fixBranchesAndUses (BlockSet &PhiBlocks) {
651
773
// First update uses of the value.
652
774
SmallVector<LoadInst *, 4 > collectedLoads;
@@ -683,6 +805,16 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSet &PhiBlocks) {
683
805
// on.
684
806
SILBasicBlock *BB = Inst->getParent ();
685
807
808
+ if (!BB->isEntry ()) {
809
+ if (auto *SI = dyn_cast<StoreInst>(Inst)) {
810
+ if (SI->getOwnershipQualifier () == StoreOwnershipQualifier::Assign) {
811
+ SILValue Def = getLiveInValue (PhiBlocks, BB);
812
+ SILBuilderWithScope (SI).createDestroyValue (SI->getLoc (), Def);
813
+ continue ;
814
+ }
815
+ }
816
+ }
817
+
686
818
if (auto *DVAI = dyn_cast<DebugValueAddrInst>(Inst)) {
687
819
// Replace DebugValueAddr with DebugValue.
688
820
SILValue Def = getLiveInValue (PhiBlocks, BB);
@@ -714,6 +846,22 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSet &PhiBlocks) {
714
846
fixPhiPredBlock (PhiBlocks, Block, PBB);
715
847
}
716
848
}
849
+
850
+ // If the owned phi arg we added did not have any uses, create end_lifetime to
851
+ // end its lifetime. In asserts mode, make sure we have only undef incoming
852
+ // values for such phi args.
853
+ if (ASI->getFunction ()->hasOwnership ()) {
854
+ for (auto Block : PhiBlocks) {
855
+ auto *phiArg = cast<SILPhiArgument>(
856
+ Block->getArgument (Block->getNumArguments () - 1 ));
857
+ if (phiArg->getOwnershipKind () == ValueOwnershipKind::Owned &&
858
+ phiArg->use_empty ()) {
859
+ assert (hasOnlyUndefIncomingValues (phiArg));
860
+ SILBuilderWithScope (&Block->front ())
861
+ .createEndLifetime (Block->front ().getLoc (), phiArg);
862
+ }
863
+ }
864
+ }
717
865
}
718
866
719
867
void StackAllocationPromoter::pruneAllocStackUsage () {
@@ -960,10 +1108,6 @@ class SILMem2Reg : public SILFunctionTransform {
960
1108
void run () override {
961
1109
SILFunction *F = getFunction ();
962
1110
963
- // FIXME: We should be able to support ownership.
964
- if (F->hasOwnership ())
965
- return ;
966
-
967
1111
LLVM_DEBUG (llvm::dbgs () << " ** Mem2Reg on function: " << F->getName ()
968
1112
<< " **\n " );
969
1113
0 commit comments