Skip to content

Commit 9a71f23

Browse files
authored
Merge pull request #27444 from atrick/fix-sink-address
Generalize and fix SinkAddressProjections.
2 parents 7c0ed24 + 38c29e2 commit 9a71f23

File tree

9 files changed

+371
-139
lines changed

9 files changed

+371
-139
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545

4646
namespace swift {
4747

48+
class AllocationInst;
4849
class DeclRefExpr;
4950
class FloatLiteralExpr;
5051
class FuncDecl;
@@ -618,6 +619,15 @@ class SILInstruction
618619
return getMemoryBehavior() != MemoryBehavior::None;
619620
}
620621

622+
/// Return true if the instruction is "pure" in the sense that it may execute
623+
/// multiple times without affecting behavior. This implies that it can be
624+
/// trivially cloned at multiple use sites without preserving path
625+
/// equivalence.
626+
bool isPure() const {
627+
return !mayReadOrWriteMemory() && !mayTrap() && !isa<AllocationInst>(this)
628+
&& !isa<TermInst>(this);
629+
}
630+
621631
/// Returns true if the result of this instruction is a pointer to stack
622632
/// allocated memory. In this case there must be an adjacent deallocating
623633
/// instruction.

include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h

Lines changed: 109 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@
2222
#ifndef SWIFT_SILOPTIMIZER_UTILS_BASICBLOCKOPTUTILS_H
2323
#define SWIFT_SILOPTIMIZER_UTILS_BASICBLOCKOPTUTILS_H
2424

25-
#include "swift/SIL/SILInstruction.h"
2625
#include "swift/SIL/SILBasicBlock.h"
2726
#include "swift/SIL/SILCloner.h"
27+
#include "swift/SIL/SILInstruction.h"
28+
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
2829

2930
namespace swift {
3031

@@ -61,12 +62,65 @@ bool rotateLoop(SILLoop *loop, DominanceInfo *domInfo, SILLoopInfo *loopInfo,
6162
bool rotateSingleBlockLoops, SILBasicBlock *upToBB,
6263
bool shouldVerify);
6364

64-
/// Helper function to perform SSA updates in case of jump threading.
65-
void updateSSAAfterCloning(BasicBlockCloner &cloner, SILBasicBlock *srcBB,
66-
SILBasicBlock *destBB);
65+
/// Sink address projections to their out-of-block uses. This is
66+
/// required after cloning a block and before calling
67+
/// updateSSAAfterCloning to avoid address-type phis.
68+
///
69+
/// This clones address projections at their use points, but does not
70+
/// mutate the block containing the projections.
71+
///
72+
/// BasicBlockCloner handles this internally.
73+
class SinkAddressProjections {
74+
// Projections ordered from last to first in the chain.
75+
SmallVector<SingleValueInstruction *, 4> projections;
76+
SmallSetVector<SILValue, 4> inBlockDefs;
77+
78+
// Transient per-projection data for use during cloning.
79+
SmallVector<Operand *, 4> usesToReplace;
80+
llvm::SmallDenseMap<SILBasicBlock *, Operand *, 4> firstBlockUse;
81+
82+
public:
83+
/// Check for an address projection chain ending at \p inst. Return true if
84+
/// the given instruction is successfully analyzed.
85+
///
86+
/// If \p inst does not produce an address, then return
87+
/// true. getInBlockDefs() will contain \p inst if any of its
88+
/// (non-address) values are used outside its block.
89+
///
90+
/// If \p inst does produce an address, return true only of the
91+
/// chain of address projections within this block is clonable at
92+
/// their use sites. getInBlockDefs will return all non-address
93+
/// operands in the chain that are also defined in this block. These
94+
/// may require phis after cloning the projections.
95+
bool analyzeAddressProjections(SILInstruction *inst);
96+
97+
/// After analyzing projections, returns the list of (non-address) values
98+
/// defined in the same block as the projections which will have uses outside
99+
/// the block after cloning.
100+
ArrayRef<SILValue> getInBlockDefs() const {
101+
return inBlockDefs.getArrayRef();
102+
}
103+
/// Clone the chain of projections at their use sites.
104+
///
105+
/// Return true if anything was done.
106+
///
107+
/// getInBlockProjectionOperandValues() can be called before or after cloning.
108+
bool cloneProjections();
109+
};
67110

68111
/// Clone a single basic block and any required successor edges within the same
69112
/// function.
113+
///
114+
/// Before cloning, call either canCloneBlock or call canCloneInstruction for
115+
/// every instruction in the original block.
116+
///
117+
/// To clone just the block, call cloneBlock. To also update the original
118+
/// block's branch to jump to the newly cloned block, call cloneBranchTarget
119+
/// instead.
120+
///
121+
/// After cloning, call splitCriticalEdges, then updateSSAAfterCloning. This is
122+
/// decoupled from cloning becaused some clients perform CFG edges updates after
123+
/// cloning but before splitting CFG edges.
70124
class BasicBlockCloner : public SILCloner<BasicBlockCloner> {
71125
using SuperTy = SILCloner<BasicBlockCloner>;
72126
friend class SILCloner<BasicBlockCloner>;
@@ -75,18 +129,56 @@ class BasicBlockCloner : public SILCloner<BasicBlockCloner> {
75129
/// The original block to be cloned.
76130
SILBasicBlock *origBB;
77131

132+
/// Will cloning require an SSA update?
133+
bool needsSSAUpdate = false;
134+
135+
/// Transient object for analyzing a single address projction chain. It's
136+
/// state is reset each time analyzeAddressProjections is called.
137+
SinkAddressProjections sinkProj;
138+
78139
public:
79140
/// An ordered list of old to new available value pairs.
80141
///
81142
/// updateSSAAfterCloning() expects this public field to hold values that may
82143
/// be remapped in the cloned block and live out.
83-
SmallVector<std::pair<SILValue, SILValue>, 16> AvailVals;
144+
SmallVector<std::pair<SILValue, SILValue>, 16> availVals;
84145

85146
// Clone blocks starting at `origBB`, within the same function.
86147
BasicBlockCloner(SILBasicBlock *origBB)
87148
: SILCloner(*origBB->getParent()), origBB(origBB) {}
88149

150+
bool canCloneBlock() {
151+
for (auto &inst : *origBB) {
152+
if (!canCloneInstruction(&inst))
153+
return false;
154+
}
155+
return true;
156+
}
157+
158+
/// Returns true if \p inst can be cloned.
159+
///
160+
/// If canCloneBlock is not called, then this must be called for every
161+
/// instruction in origBB, both to ensure clonability and to handle internal
162+
/// book-keeping (needsSSAUpdate).
163+
bool canCloneInstruction(SILInstruction *inst) {
164+
assert(inst->getParent() == origBB);
165+
166+
if (!inst->isTriviallyDuplicatable())
167+
return false;
168+
169+
if (!sinkProj.analyzeAddressProjections(inst))
170+
return false;
171+
172+
// Check if any of the non-address defs in the cloned block (including the
173+
// current instruction) will still have uses outside the block after sinking
174+
// address projections.
175+
needsSSAUpdate |= !sinkProj.getInBlockDefs().empty();
176+
return true;
177+
}
178+
89179
void cloneBlock(SILBasicBlock *insertAfterBB = nullptr) {
180+
sinkAddressProjections();
181+
90182
SmallVector<SILBasicBlock *, 4> successorBBs;
91183
successorBBs.reserve(origBB->getSuccessors().size());
92184
llvm::copy(origBB->getSuccessors(), std::back_inserter(successorBBs));
@@ -95,6 +187,9 @@ class BasicBlockCloner : public SILCloner<BasicBlockCloner> {
95187

96188
/// Clone the given branch instruction's destination block, splitting
97189
/// its successors, and rewrite the branch instruction.
190+
///
191+
/// Return false if the branch's destination block cannot be cloned. When
192+
/// false is returned, no changes have been made.
98193
void cloneBranchTarget(BranchInst *bi) {
99194
assert(origBB == bi->getDestBB());
100195

@@ -110,10 +205,16 @@ class BasicBlockCloner : public SILCloner<BasicBlockCloner> {
110205
return remapBasicBlock(origBB);
111206
}
112207

208+
bool wasCloned() { return isBlockCloned(origBB); }
209+
113210
/// Call this after processing all instructions to fix the control flow
114211
/// graph. The branch cloner may have left critical edges.
115212
bool splitCriticalEdges(DominanceInfo *domInfo, SILLoopInfo *loopInfo);
116213

214+
/// Helper function to perform SSA updates after calling both
215+
/// cloneBranchTarget and splitCriticalEdges.
216+
void updateSSAAfterCloning();
217+
117218
protected:
118219
// MARK: CRTP overrides.
119220

@@ -137,8 +238,10 @@ class BasicBlockCloner : public SILCloner<BasicBlockCloner> {
137238

138239
void mapValue(SILValue origValue, SILValue mappedValue) {
139240
SuperTy::mapValue(origValue, mappedValue);
140-
AvailVals.emplace_back(origValue, mappedValue);
241+
availVals.emplace_back(origValue, mappedValue);
141242
}
243+
244+
void sinkAddressProjections();
142245
};
143246

144247
// Helper class that provides a callback that can be used in
@@ -173,46 +276,6 @@ class CloneCollector {
173276
}
174277
};
175278

176-
/// Sink address projections to their out-of-block uses. This is
177-
/// required after cloning a block and before calling
178-
/// updateSSAAfterCloning to avoid address-type phis.
179-
///
180-
/// This clones address projections at their use points, but does not
181-
/// mutate the block containing the projections.
182-
class SinkAddressProjections {
183-
// Projections ordered from last to first in the chain.
184-
SmallVector<SingleValueInstruction *, 4> projections;
185-
SmallSetVector<SILValue, 4> inBlockDefs;
186-
187-
public:
188-
/// Check for an address projection chain ending at \p inst. Return true if
189-
/// the given instruction is successfully analyzed.
190-
///
191-
/// If \p inst does not produce an address, then return
192-
/// true. getInBlockDefs() will contain \p inst if any of its
193-
/// (non-address) values are used outside its block.
194-
///
195-
/// If \p inst does produce an address, return true only of the
196-
/// chain of address projections within this block is clonable at
197-
/// their use sites. getInBlockDefs will return all non-address
198-
/// operands in the chain that are also defined in this block. These
199-
/// may require phis after cloning the projections.
200-
bool analyzeAddressProjections(SILInstruction *inst);
201-
202-
/// After analyzing projections, returns the list of (non-address) values
203-
/// defined in the same block as the projections which will have uses outside
204-
/// the block after cloning.
205-
ArrayRef<SILValue> getInBlockDefs() const {
206-
return inBlockDefs.getArrayRef();
207-
}
208-
/// Clone the chain of projections at their use sites.
209-
///
210-
/// Return true if anything was done.
211-
///
212-
/// getInBlockProjectionOperandValues() can be called before or after cloning.
213-
bool cloneProjections();
214-
};
215-
216279
/// Utility class for cloning init values into the static initializer of a
217280
/// SILGlobalVariable.
218281
class StaticInitCloner : public SILCloner<StaticInitCloner> {

include/swift/SILOptimizer/Utils/SILSSAUpdater.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,10 @@ class UseWrapper {
133133
/// reconstruct the use.
134134
UseWrapper(Operand *Use);
135135

136+
Operand *getOperand();
137+
136138
/// Return the operand we wrap. Reconstructing branch operands.
137-
operator Operand*();
139+
operator Operand*() { return getOperand(); }
138140
};
139141

140142
} // end namespace swift

0 commit comments

Comments
 (0)