@@ -61,6 +61,17 @@ namespace {
61
61
// / it finds cases in which it is easy to determine that the source is
62
62
// / unmodified during the copy destination's lifetime. Thus, the destination can
63
63
// / be viewed as a short-lived "rvalue".
64
+ // /
65
+ // / As a second optimization, also stores into temporaries are handled. This is
66
+ // / a simple form of redundant-load-elimination (RLE).
67
+ // /
68
+ // / %temp = alloc_stack $T
69
+ // / store %src to [initialization] %temp : $*T
70
+ // / // no writes to %temp
71
+ // / %v = load [take] %temp : $*T
72
+ // / dealloc_stack %temp : $*T
73
+ // /
74
+ // / TODO: Check if we still need to handle stores when RLE supports OSSA.
64
75
class TempRValueOptPass : public SILFunctionTransform {
65
76
AliasAnalysis *aa = nullptr ;
66
77
@@ -94,14 +105,6 @@ class TempRValueOptPass : public SILFunctionTransform {
94
105
bool TempRValueOptPass::collectLoadsFromProjection (
95
106
SingleValueInstruction *projection, SILValue srcAddr,
96
107
SmallPtrSetImpl<SILInstruction *> &loadInsts) {
97
- if (!srcAddr) {
98
- LLVM_DEBUG (
99
- llvm::dbgs ()
100
- << " Temp has addr_projection use?! Can not yet promote to value"
101
- << *projection);
102
- return false ;
103
- }
104
-
105
108
// Transitively look through projections on stack addresses.
106
109
for (auto *projUseOper : projection->getUses ()) {
107
110
auto *user = projUseOper->getUser ();
@@ -127,28 +130,15 @@ bool TempRValueOptPass::canApplyBeTreatedAsLoad(
127
130
return false ;
128
131
}
129
132
130
- if (srcAddr) {
131
- // If the function may write to the source of the copy_addr, the apply
132
- // cannot be treated as a load: all (potential) writes of the source must
133
- // appear _after_ all loads of the temporary. But in case of a function call
134
- // we don't know in which order the writes and loads are executed inside the
135
- // called function. The source may be written before the temporary is
136
- // loaded, which would make the optization invalid.
137
- if (aa->mayWriteToMemory (apply.getInstruction (), srcAddr))
138
- return false ;
139
- } else {
140
- // If we do not have an src address, but are indirect, bail. We would need
141
- // to perform function signature specialization to change the functions
142
- // signature to pass something direct.
143
- if (convention.isIndirectConvention ()) {
144
- LLVM_DEBUG (
145
- llvm::dbgs ()
146
- << " Temp used to materialize value for indirect convention?! Can "
147
- " not remove temporary without func sig opts"
148
- << *apply.getInstruction ());
149
- return false ;
150
- }
151
- }
133
+ // If the function may write to the source of the copy_addr, the apply
134
+ // cannot be treated as a load: all (potential) writes of the source must
135
+ // appear _after_ all loads of the temporary. But in case of a function call
136
+ // we don't know in which order the writes and loads are executed inside the
137
+ // called function. The source may be written before the temporary is
138
+ // loaded, which would make the optization invalid.
139
+ if (aa->mayWriteToMemory (apply.getInstruction (), srcAddr))
140
+ return false ;
141
+
152
142
return true ;
153
143
}
154
144
@@ -237,14 +227,6 @@ bool TempRValueOptPass::collectLoads(
237
227
return true ;
238
228
}
239
229
case SILInstructionKind::OpenExistentialAddrInst: {
240
- // If we do not have an srcAddr, bail. We do not support promoting this yet.
241
- if (!srcAddr) {
242
- LLVM_DEBUG (llvm::dbgs () << " Temp has open_existential_addr use?! Can "
243
- " not yet promote to value"
244
- << *user);
245
- return false ;
246
- }
247
-
248
230
// We only support open existential addr if the access is immutable.
249
231
auto *oeai = cast<OpenExistentialAddrInst>(user);
250
232
if (oeai->getAccessKind () != OpenedExistentialAccess::Immutable) {
@@ -294,11 +276,6 @@ bool TempRValueOptPass::collectLoads(
294
276
return true ;
295
277
296
278
case SILInstructionKind::LoadBorrowInst:
297
- // If we do not have a source addr, we must be trying to eliminate a
298
- // store. Until we check that the source object is not destroyed within the
299
- // given range, we need bail.
300
- if (!srcAddr)
301
- return false ;
302
279
loadInsts.insert (user);
303
280
return true ;
304
281
case SILInstructionKind::FixLifetimeInst:
@@ -598,29 +575,21 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
598
575
if (user == si)
599
576
continue ;
600
577
601
- // Destroys and deallocations are allowed to be in a different block.
602
- if (isa<DestroyAddrInst>(user) || isa<DeallocStackInst>(user))
603
- continue ;
604
-
605
- // Same for load [take] on the top level temp object. SILGen always takes
606
- // whole values from temporaries. If we have load [take] on projections from
607
- // our base, we fail since those would be re-initializations.
608
- if (auto *li = dyn_cast<LoadInst>(user)) {
609
- if (li->getOwnershipQualifier () == LoadOwnershipQualifier::Take) {
610
- continue ;
611
- }
612
- }
613
-
614
- // We pass in SILValue() since we do not have a source address.
615
- if (!collectLoads (useOper, user, tempObj, SILValue (), loadInsts))
616
- return {std::next (si->getIterator ()), false };
617
-
618
578
// Bail if there is any kind of user which is not handled in the code below.
619
579
switch (user->getKind ()) {
620
- case SILInstructionKind::CopyAddrInst:
580
+ case SILInstructionKind::DestroyAddrInst:
581
+ case SILInstructionKind::DeallocStackInst:
582
+ case SILInstructionKind::LoadInst:
621
583
case SILInstructionKind::FixLifetimeInst:
584
+ break ;
585
+ case SILInstructionKind::CopyAddrInst:
586
+ if (cast<CopyAddrInst>(user)->getDest () == tempObj)
587
+ return {std::next (si->getIterator ()), false };
588
+ break ;
622
589
case SILInstructionKind::MarkDependenceInst:
623
- continue ;
590
+ if (cast<MarkDependenceInst>(user)->getValue () == tempObj)
591
+ return {std::next (si->getIterator ()), false };
592
+ break ;
624
593
default :
625
594
return {std::next (si->getIterator ()), false };
626
595
}
@@ -690,11 +659,13 @@ TempRValueOptPass::tryOptimizeStoreIntoTemp(StoreInst *si) {
690
659
break ;
691
660
}
692
661
case SILInstructionKind::MarkDependenceInst: {
693
- SILBuilderWithScope builder (user);
694
662
auto mdi = cast<MarkDependenceInst>(user);
695
- auto newInst = builder.createMarkDependence (user->getLoc (), mdi->getValue (), si->getSrc ());
663
+ assert (mdi->getBase () == tempObj);
664
+ SILBuilderWithScope builder (user);
665
+ auto newInst = builder.createMarkDependence (user->getLoc (),
666
+ mdi->getValue (), si->getSrc ());
696
667
mdi->replaceAllUsesWith (newInst);
697
- toDelete.push_back (user );
668
+ toDelete.push_back (mdi );
698
669
break ;
699
670
}
700
671
// ASSUMPTION: no operations that may be handled by this default clause can
0 commit comments