Skip to content

Commit 6da1df2

Browse files
authored
Merge pull request #66825 from meg-gupta/addressphilast
Sink address projections in ArrayPropertyOpt and enable verification to ensure we have no address phis
2 parents 821e260 + 1e89ad6 commit 6da1df2

File tree

6 files changed

+218
-50
lines changed

6 files changed

+218
-50
lines changed

include/swift/SILOptimizer/Utils/BasicBlockOptUtils.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,14 +135,20 @@ bool canCloneTerminator(TermInst *termInst);
135135
/// BasicBlockCloner handles this internally.
136136
class SinkAddressProjections {
137137
// Projections ordered from last to first in the chain.
138-
SmallVector<SingleValueInstruction *, 4> projections;
138+
SmallVector<SingleValueInstruction *, 4> oldProjections;
139+
// Cloned projections to avoid address phis.
140+
SmallVectorImpl<SingleValueInstruction *> *newProjections;
139141
SmallSetVector<SILValue, 4> inBlockDefs;
140142

141143
// Transient per-projection data for use during cloning.
142144
SmallVector<Operand *, 4> usesToReplace;
143145
llvm::SmallDenseMap<SILBasicBlock *, Operand *, 4> firstBlockUse;
144146

145147
public:
148+
SinkAddressProjections(
149+
SmallVectorImpl<SingleValueInstruction *> *newProjections = nullptr)
150+
: newProjections(newProjections) {}
151+
146152
/// Check for an address projection chain ending at \p inst. Return true if
147153
/// the given instruction is successfully analyzed.
148154
///
@@ -163,6 +169,7 @@ class SinkAddressProjections {
163169
ArrayRef<SILValue> getInBlockDefs() const {
164170
return inBlockDefs.getArrayRef();
165171
}
172+
166173
/// Clone the chain of projections at their use sites.
167174
///
168175
/// Return true if anything was done.

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,24 +1065,6 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
10651065
return InstNumbers[a] < InstNumbers[b];
10661066
}
10671067

1068-
// FIXME: For sanity, address-type phis should be prohibited at all SIL
1069-
// stages. However, the optimizer currently breaks the invariant in three
1070-
// places:
1071-
// 1. Normal Simplify CFG during conditional branch simplification
1072-
// (sneaky jump threading).
1073-
// 2. Simplify CFG via Jump Threading.
1074-
// 3. Loop Rotation.
1075-
//
1076-
// BasicBlockCloner::canCloneInstruction and sinkAddressProjections is
1077-
// designed to avoid this issue, we just need to make sure all passes use it
1078-
// correctly.
1079-
//
1080-
// Minimally, we must prevent address-type phis as long as access markers are
1081-
// preserved. A goal is to preserve access markers in OSSA.
1082-
bool prohibitAddressPhis() {
1083-
return F.hasOwnership();
1084-
}
1085-
10861068
void visitSILPhiArgument(SILPhiArgument *arg) {
10871069
// Verify that the `isPhiArgument` property is sound:
10881070
// - Phi arguments come from branches.
@@ -1106,7 +1088,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
11061088
"All phi argument inputs must be from branches.");
11071089
}
11081090
}
1109-
if (arg->isPhi() && prohibitAddressPhis()) {
1091+
if (arg->isPhi()) {
11101092
// As a property of well-formed SIL, we disallow address-type
11111093
// phis. Supporting them would prevent reliably reasoning about the
11121094
// underlying storage of memory access. This reasoning is important for

lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,19 @@
5353
#define DEBUG_TYPE "array-property-opt"
5454

5555
#include "ArrayOpt.h"
56+
#include "swift/SIL/CFG.h"
57+
#include "swift/SIL/DebugUtils.h"
58+
#include "swift/SIL/InstructionUtils.h"
59+
#include "swift/SIL/LoopInfo.h"
60+
#include "swift/SIL/Projection.h"
61+
#include "swift/SIL/SILCloner.h"
5662
#include "swift/SILOptimizer/Analysis/ArraySemantic.h"
5763
#include "swift/SILOptimizer/Analysis/LoopAnalysis.h"
5864
#include "swift/SILOptimizer/PassManager/Transforms.h"
65+
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
5966
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
6067
#include "swift/SILOptimizer/Utils/LoopUtils.h"
6168
#include "swift/SILOptimizer/Utils/SILSSAUpdater.h"
62-
#include "swift/SIL/CFG.h"
63-
#include "swift/SIL/DebugUtils.h"
64-
#include "swift/SIL/InstructionUtils.h"
65-
#include "swift/SIL/Projection.h"
66-
#include "swift/SIL/LoopInfo.h"
67-
#include "swift/SIL/BasicBlockBits.h"
68-
#include "swift/SIL/SILCloner.h"
6969
#include "llvm/ADT/SmallSet.h"
7070
#include "llvm/Support/CommandLine.h"
7171
#include "llvm/Support/Debug.h"
@@ -86,6 +86,8 @@ class ArrayPropertiesAnalysis {
8686
SILBasicBlock *Preheader;
8787
DominanceInfo *DomTree;
8888

89+
SinkAddressProjections sinkProj;
90+
8991
llvm::DenseMap<SILFunction *, uint32_t> InstCountCache;
9092
llvm::SmallSet<SILValue, 16> HoistableArray;
9193

@@ -169,13 +171,18 @@ class ArrayPropertiesAnalysis {
169171

170172
bool FoundHoistable = false;
171173
uint32_t LoopInstCount = 0;
174+
172175
for (auto *BB : Loop->getBlocks()) {
173176
for (auto &Inst : *BB) {
174177
// Can't clone alloc_stack instructions whose dealloc_stack is outside
175178
// the loop.
176179
if (!canDuplicateLoopInstruction(Loop, &Inst))
177180
return false;
178181

182+
if (!sinkProj.analyzeAddressProjections(&Inst)) {
183+
return false;
184+
}
185+
179186
ArraySemanticsCall ArrayPropsInst(&Inst, "array.props", true);
180187
if (!ArrayPropsInst)
181188
continue;
@@ -512,10 +519,11 @@ class RegionCloner : public SILCloner<RegionCloner> {
512519
SILSSAUpdater &SSAUp) {
513520
// Collect outside uses.
514521
SmallVector<UseWrapper, 16> UseList;
515-
for (auto Use : V->getUses())
522+
for (auto Use : V->getUses()) {
516523
if (!isBlockCloned(Use->getUser()->getParent())) {
517524
UseList.push_back(UseWrapper(Use));
518525
}
526+
}
519527
if (UseList.empty())
520528
return;
521529

@@ -532,15 +540,40 @@ class RegionCloner : public SILCloner<RegionCloner> {
532540

533541
void updateSSAForm() {
534542
SILSSAUpdater SSAUp;
543+
SmallVector<SingleValueInstruction *, 4> newProjections;
544+
SinkAddressProjections sinkProj(&newProjections);
545+
535546
for (auto *origBB : originalPreorderBlocks()) {
536547
// Update outside used phi values.
537-
for (auto *arg : origBB->getArguments())
548+
for (auto *arg : origBB->getArguments()) {
538549
updateSSAForValue(origBB, arg, SSAUp);
550+
}
539551

540552
// Update outside used instruction values.
541553
for (auto &inst : *origBB) {
542-
for (auto result : inst.getResults())
543-
updateSSAForValue(origBB, result, SSAUp);
554+
for (auto result : inst.getResults()) {
555+
bool success = sinkProj.analyzeAddressProjections(&inst);
556+
assert(success);
557+
// Sink address projections by cloning to avoid address phis.
558+
sinkProj.cloneProjections();
559+
560+
// If no new projections were created, update ssa for the result only.
561+
if (newProjections.empty()) {
562+
updateSSAForValue(origBB, result, SSAUp);
563+
continue;
564+
}
565+
566+
for (auto *newProj : newProjections) {
567+
// Operand values of new projections may need ssa update.
568+
for (auto opVal : newProj->getOperandValues()) {
569+
if (!isBlockCloned(opVal->getParentBlock())) {
570+
continue;
571+
}
572+
updateSSAForValue(origBB, opVal, SSAUp);
573+
}
574+
}
575+
newProjections.clear();
576+
}
544577
}
545578
}
546579
}

lib/SILOptimizer/Utils/BasicBlockOptUtils.cpp

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ void BasicBlockCloner::sinkAddressProjections() {
223223
//
224224
// Return true on success, even if projections is empty.
225225
bool SinkAddressProjections::analyzeAddressProjections(SILInstruction *inst) {
226-
projections.clear();
226+
oldProjections.clear();
227227
inBlockDefs.clear();
228228

229229
SILBasicBlock *bb = inst->getParent();
@@ -237,7 +237,7 @@ bool SinkAddressProjections::analyzeAddressProjections(SILInstruction *inst) {
237237
}
238238
if (auto *addressProj = dyn_cast<SingleValueInstruction>(def)) {
239239
if (addressProj->isPure()) {
240-
projections.push_back(addressProj);
240+
oldProjections.push_back(addressProj);
241241
return true;
242242
}
243243
}
@@ -252,12 +252,12 @@ bool SinkAddressProjections::analyzeAddressProjections(SILInstruction *inst) {
252252
return false;
253253
}
254254
// Recurse upward through address projections.
255-
for (unsigned idx = 0; idx < projections.size(); ++idx) {
255+
for (unsigned idx = 0; idx < oldProjections.size(); ++idx) {
256256
// Only one address result/operand can be handled per instruction.
257-
if (projections.size() != idx + 1)
257+
if (oldProjections.size() != idx + 1)
258258
return false;
259259

260-
for (SILValue operandVal : projections[idx]->getOperandValues())
260+
for (SILValue operandVal : oldProjections[idx]->getOperandValues())
261261
if (!pushOperandVal(operandVal))
262262
return false;
263263
}
@@ -267,13 +267,13 @@ bool SinkAddressProjections::analyzeAddressProjections(SILInstruction *inst) {
267267
// Clone the projections gathered by 'analyzeAddressProjections' at
268268
// their use site outside this block.
269269
bool SinkAddressProjections::cloneProjections() {
270-
if (projections.empty())
270+
if (oldProjections.empty())
271271
return false;
272272

273-
SILBasicBlock *bb = projections.front()->getParent();
273+
SILBasicBlock *bb = oldProjections.front()->getParent();
274274
// Clone projections in last-to-first order.
275-
for (unsigned idx = 0; idx < projections.size(); ++idx) {
276-
auto *oldProj = projections[idx];
275+
for (unsigned idx = 0; idx < oldProjections.size(); ++idx) {
276+
auto *oldProj = oldProjections[idx];
277277
assert(oldProj->getParent() == bb);
278278
// Reset transient per-projection sets.
279279
usesToReplace.clear();
@@ -297,9 +297,12 @@ bool SinkAddressProjections::cloneProjections() {
297297
auto *useBB = use->getUser()->getParent();
298298
auto *firstUse = firstBlockUse.lookup(useBB);
299299
SingleValueInstruction *newProj;
300-
if (use == firstUse)
300+
if (use == firstUse) {
301301
newProj = cast<SingleValueInstruction>(oldProj->clone(use->getUser()));
302-
else {
302+
if (newProjections) {
303+
newProjections->push_back(newProj);
304+
}
305+
} else {
303306
newProj = cast<SingleValueInstruction>(firstUse->get());
304307
assert(newProj->getParent() == useBB);
305308
newProj->moveFront(useBB);

lib/SILOptimizer/Utils/SILSSAUpdater.cpp

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,20 +86,30 @@ areIdentical(llvm::DenseMap<SILBasicBlock *, SILValue> &availableValues) {
8686
return true;
8787
}
8888

89-
auto *mvir =
90-
dyn_cast<MultipleValueInstructionResult>(availableValues.begin()->second);
91-
if (!mvir)
92-
return false;
89+
if (auto *mvir = dyn_cast<MultipleValueInstructionResult>(
90+
availableValues.begin()->second)) {
91+
for (auto value : availableValues) {
92+
auto *result = dyn_cast<MultipleValueInstructionResult>(value.second);
93+
if (!result)
94+
return false;
95+
if (!result->getParent()->isIdenticalTo(mvir->getParent()) ||
96+
result->getIndex() != mvir->getIndex()) {
97+
return false;
98+
}
99+
}
100+
return true;
101+
}
93102

103+
auto *firstArg = cast<SILArgument>(availableValues.begin()->second);
94104
for (auto value : availableValues) {
95-
auto *result = dyn_cast<MultipleValueInstructionResult>(value.second);
96-
if (!result)
105+
auto *arg = dyn_cast<SILArgument>(value.second);
106+
if (!arg)
97107
return false;
98-
if (!result->getParent()->isIdenticalTo(mvir->getParent()) ||
99-
result->getIndex() != mvir->getIndex()) {
108+
if (arg != firstArg) {
100109
return false;
101110
}
102111
}
112+
103113
return true;
104114
}
105115

0 commit comments

Comments
 (0)