@@ -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).
@@ -302,8 +323,10 @@ promoteDebugValueAddr(DebugValueAddrInst *DVAI, SILValue Value, SILBuilder &B) {
302
323
// Avoid inserting the same debug_value twice.
303
324
for (Operand *Use : Value->getUses ())
304
325
if (auto *DVI = dyn_cast<DebugValueInst>(Use->getUser ()))
305
- if (*DVI->getVarInfo () == *DVAI->getVarInfo ())
326
+ if (*DVI->getVarInfo () == *DVAI->getVarInfo ()) {
327
+ DVAI->eraseFromParent ();
306
328
return ;
329
+ }
307
330
B.setInsertionPoint (DVAI);
308
331
B.setCurrentDebugScope (DVAI->getDebugScope ());
309
332
B.createDebugValue (DVAI->getLoc (), Value, *DVAI->getVarInfo ());
@@ -348,21 +371,54 @@ static void collectLoads(SILInstruction *I, SmallVectorImpl<LoadInst *> &Loads)
348
371
static void replaceLoad (LoadInst *LI, SILValue val, AllocStackInst *ASI) {
349
372
ProjectionPath projections (val->getType ());
350
373
SILValue op = LI->getOperand ();
374
+ SILBuilderWithScope builder (LI);
375
+
351
376
while (op != ASI) {
352
377
assert (isa<UncheckedAddrCastInst>(op) || isa<StructElementAddrInst>(op) ||
353
378
isa<TupleElementAddrInst>(op));
354
379
auto *Inst = cast<SingleValueInstruction>(op);
355
380
projections.push_back (Projection (Inst));
356
381
op = Inst->getOperand (0 );
357
382
}
358
- SILBuilder builder (LI);
383
+
384
+ SmallVector<SILValue, 4 > borrowedVals;
359
385
for (auto iter = projections.rbegin (); iter != projections.rend (); ++iter) {
360
386
const Projection &projection = *iter;
387
+ assert (projection.getKind () == ProjectionKind::BitwiseCast ||
388
+ projection.getKind () == ProjectionKind::Struct ||
389
+ projection.getKind () == ProjectionKind::Tuple);
390
+
391
+ // struct_extract and tuple_extract expect guaranteed operand ownership
392
+ // non-trivial RunningVal is owned. Insert borrow operation to convert
393
+ if (projection.getKind () == ProjectionKind::Struct ||
394
+ projection.getKind () == ProjectionKind::Tuple) {
395
+ SILValue opVal = builder.emitBeginBorrowOperation (LI->getLoc (), val);
396
+ if (opVal != val) {
397
+ borrowedVals.push_back (opVal);
398
+ val = opVal;
399
+ }
400
+ }
361
401
val = projection.createObjectProjection (builder, LI->getLoc (), val).get ();
362
402
}
403
+
363
404
op = LI->getOperand ();
364
- LI->replaceAllUsesWith (val);
405
+ // Replace users of the loaded value with `val`
406
+ // If we have a load [copy], replace the users with copy_value of `val`
407
+ if (LI->getOwnershipQualifier () == LoadOwnershipQualifier::Copy) {
408
+ LI->replaceAllUsesWith (builder.createCopyValue (LI->getLoc (), val));
409
+ } else {
410
+ assert (!ASI->getFunction ()->hasOwnership () ||
411
+ val.getOwnershipKind () != ValueOwnershipKind::Guaranteed);
412
+ LI->replaceAllUsesWith (val);
413
+ }
414
+
415
+ for (auto borrowedVal : borrowedVals) {
416
+ builder.emitEndBorrowOperation (LI->getLoc (), borrowedVal);
417
+ }
418
+
419
+ // Delete the load
365
420
LI->eraseFromParent ();
421
+
366
422
while (op != ASI && op->use_empty ()) {
367
423
assert (isa<UncheckedAddrCastInst>(op) || isa<StructElementAddrInst>(op) ||
368
424
isa<TupleElementAddrInst>(op));
@@ -399,6 +455,7 @@ StoreInst *
399
455
StackAllocationPromoter::promoteAllocationInBlock (SILBasicBlock *BB) {
400
456
LLVM_DEBUG (llvm::dbgs () << " *** Promoting ASI in block: " << *ASI);
401
457
458
+ // RunningVal is the current value in the stack location.
402
459
// We don't know the value of the alloca until we find the first store.
403
460
SILValue RunningVal = SILValue ();
404
461
// Keep track of the last StoreInst that we found.
@@ -415,12 +472,16 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) {
415
472
// If we are loading from the AllocStackInst and we already know the
416
473
// content of the Alloca then use it.
417
474
LLVM_DEBUG (llvm::dbgs () << " *** Promoting load: " << *Load);
418
-
419
475
replaceLoad (Load, RunningVal, ASI);
420
476
++NumInstRemoved;
421
- } else if (Load->getOperand () == ASI) {
477
+ } else if (Load->getOperand () == ASI &&
478
+ Load->getOwnershipQualifier () !=
479
+ LoadOwnershipQualifier::Copy) {
422
480
// If we don't know the content of the AllocStack then the loaded
423
481
// value *is* the new value;
482
+ // Don't use result of load [copy] as a RunningVal, it necessitates
483
+ // additional logic for cleanup of consuming instructions of the result.
484
+ // StackAllocationPromoter::fixBranchesAndUses will later handle it.
424
485
LLVM_DEBUG (llvm::dbgs () << " *** First load: " << *Load);
425
486
RunningVal = Load;
426
487
}
@@ -433,16 +494,51 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) {
433
494
if (SI->getDest () != ASI)
434
495
continue ;
435
496
436
- // The stored value is the new running value.
437
- RunningVal = SI->getSrc ();
497
+ // Special handling of entry block
498
+ // If we have a store [assign] in the first block, OSSA guarantees we can
499
+ // find the previous value stored in the stack location in RunningVal.
500
+ // Create destroy_value of the RunningVal.
501
+ // For all other blocks we may not know the previous value stored in the
502
+ // stack location. So we will create destroy_value in
503
+ // StackAllocationPromoter::fixBranchesAndUses, by getting the live-in
504
+ // value to the block.
505
+ if (BB->isEntry ()) {
506
+ if (SI->getOwnershipQualifier () == StoreOwnershipQualifier::Assign) {
507
+ assert (RunningVal);
508
+ SILBuilderWithScope (SI).createDestroyValue (SI->getLoc (), RunningVal);
509
+ }
510
+ }
438
511
439
512
// If we met a store before this one, delete it.
513
+ // If the LastStore was a store with [assign], delete it only if we know
514
+ // the RunningValue to destroy. If not, it will be deleted in
515
+ // StackAllocationPromoter::fixBranchesAndUses.
440
516
if (LastStore) {
441
- ++NumInstRemoved;
442
- LLVM_DEBUG (llvm::dbgs () << " *** Removing redundant store: "
443
- << *LastStore);
444
- LastStore->eraseFromParent ();
517
+ if (LastStore->getOwnershipQualifier () ==
518
+ StoreOwnershipQualifier::Assign) {
519
+ if (RunningVal) {
520
+ // For entry block, we would have already created the destroy_value,
521
+ // skip it.
522
+ if (!BB->isEntry ()) {
523
+ SILBuilderWithScope (LastStore).createDestroyValue (
524
+ LastStore->getLoc (), RunningVal);
525
+ }
526
+ LLVM_DEBUG (llvm::dbgs ()
527
+ << " *** Removing redundant store: " << *LastStore);
528
+ ++NumInstRemoved;
529
+ LastStore->eraseFromParent ();
530
+ }
531
+ } else {
532
+ LLVM_DEBUG (llvm::dbgs ()
533
+ << " *** Removing redundant store: " << *LastStore);
534
+ ++NumInstRemoved;
535
+ LastStore->eraseFromParent ();
536
+ }
445
537
}
538
+
539
+ // The stored value is the new running value.
540
+ RunningVal = SI->getSrc ();
541
+ // The current store is now the LastStore
446
542
LastStore = SI;
447
543
continue ;
448
544
}
@@ -466,10 +562,23 @@ StackAllocationPromoter::promoteAllocationInBlock(SILBasicBlock *BB) {
466
562
continue ;
467
563
}
468
564
565
+ if (auto *DVI = dyn_cast<DestroyValueInst>(Inst)) {
566
+ if (DVI->getOperand () == RunningVal) {
567
+ // Reset LastStore.
568
+ // So that we don't end up passing dead values as phi args in
569
+ // StackAllocationPromoter::fixBranchesAndUses
570
+ LastStore = nullptr ;
571
+ }
572
+ }
573
+
469
574
// Stop on deallocation.
470
575
if (auto *DSI = dyn_cast<DeallocStackInst>(Inst)) {
471
- if (DSI->getOperand () == ASI)
576
+ if (DSI->getOperand () == ASI) {
577
+ // Reset LastStore.
578
+ // So that we don't pass RunningVal as a phi arg beyond dealloc_stack
579
+ LastStore = nullptr ;
472
580
break ;
581
+ }
473
582
}
474
583
}
475
584
if (LastStore) {
@@ -512,6 +621,10 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *ASI) {
512
621
// value.
513
622
if (auto *SI = dyn_cast<StoreInst>(Inst)) {
514
623
if (SI->getDest () == ASI) {
624
+ if (SI->getOwnershipQualifier () == StoreOwnershipQualifier::Assign) {
625
+ assert (RunningVal);
626
+ SILBuilderWithScope (SI).createDestroyValue (SI->getLoc (), RunningVal);
627
+ }
515
628
RunningVal = SI->getSrc ();
516
629
Inst->eraseFromParent ();
517
630
++NumInstRemoved;
@@ -643,6 +756,21 @@ void StackAllocationPromoter::fixPhiPredBlock(BlockSet &PhiBlocks,
643
756
TI->eraseFromParent ();
644
757
}
645
758
759
+ static bool hasOnlyUndefIncomingValues (SILPhiArgument *phiArg) {
760
+ SmallVector<SILValue, 8 > incomingValues;
761
+ phiArg->getIncomingPhiValues (incomingValues);
762
+ for (auto predArg : incomingValues) {
763
+ if (isa<SILUndef>(predArg))
764
+ continue ;
765
+ if (isa<SILPhiArgument>(predArg) &&
766
+ hasOnlyUndefIncomingValues (cast<SILPhiArgument>(predArg))) {
767
+ continue ;
768
+ }
769
+ return false ;
770
+ }
771
+ return true ;
772
+ }
773
+
646
774
void StackAllocationPromoter::fixBranchesAndUses (BlockSet &PhiBlocks) {
647
775
// First update uses of the value.
648
776
SmallVector<LoadInst *, 4 > collectedLoads;
@@ -679,6 +807,16 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSet &PhiBlocks) {
679
807
// on.
680
808
SILBasicBlock *BB = Inst->getParent ();
681
809
810
+ if (!BB->isEntry ()) {
811
+ if (auto *SI = dyn_cast<StoreInst>(Inst)) {
812
+ if (SI->getOwnershipQualifier () == StoreOwnershipQualifier::Assign) {
813
+ SILValue Def = getLiveInValue (PhiBlocks, BB);
814
+ SILBuilderWithScope (SI).createDestroyValue (SI->getLoc (), Def);
815
+ continue ;
816
+ }
817
+ }
818
+ }
819
+
682
820
if (auto *DVAI = dyn_cast<DebugValueAddrInst>(Inst)) {
683
821
// Replace DebugValueAddr with DebugValue.
684
822
SILValue Def = getLiveInValue (PhiBlocks, BB);
@@ -710,6 +848,22 @@ void StackAllocationPromoter::fixBranchesAndUses(BlockSet &PhiBlocks) {
710
848
fixPhiPredBlock (PhiBlocks, Block, PBB);
711
849
}
712
850
}
851
+
852
+ // If the owned phi arg we added did not have any uses, create end_lifetime to
853
+ // end its lifetime. In asserts mode, make sure we have only undef incoming
854
+ // values for such phi args.
855
+ if (ASI->getFunction ()->hasOwnership ()) {
856
+ for (auto Block : PhiBlocks) {
857
+ auto *phiArg = cast<SILPhiArgument>(
858
+ Block->getArgument (Block->getNumArguments () - 1 ));
859
+ if (phiArg->getOwnershipKind () == ValueOwnershipKind::Owned &&
860
+ phiArg->use_empty ()) {
861
+ assert (hasOnlyUndefIncomingValues (phiArg));
862
+ SILBuilderWithScope (&Block->front ())
863
+ .createEndLifetime (Block->front ().getLoc (), phiArg);
864
+ }
865
+ }
866
+ }
713
867
}
714
868
715
869
void StackAllocationPromoter::pruneAllocStackUsage () {
@@ -956,10 +1110,6 @@ class SILMem2Reg : public SILFunctionTransform {
956
1110
void run () override {
957
1111
SILFunction *F = getFunction ();
958
1112
959
- // FIXME: We should be able to support ownership.
960
- if (F->hasOwnership ())
961
- return ;
962
-
963
1113
LLVM_DEBUG (llvm::dbgs () << " ** Mem2Reg on function: " << F->getName ()
964
1114
<< " **\n " );
965
1115
0 commit comments