Skip to content

Commit c7f2130

Browse files
authored
Merge pull request #36245 from atrick/mandatory-copyprop
Add support for a mandatory-copy-propagation pass.
2 parents 109a33c + 291467c commit c7f2130

24 files changed

+675
-562
lines changed

include/swift/AST/SILOptions.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ class SILOptions {
4444
/// Remove all runtime assertions during optimizations.
4545
bool RemoveRuntimeAsserts = false;
4646

47+
/// Force-run SIL copy propagation to shorten object lifetime in whatever
48+
/// optimization pipeline is currently used.
49+
/// When this is 'false' the pipeline has default behavior.
50+
bool EnableCopyPropagation = false;
51+
52+
/// Disable SIL copy propagation to preserve object lifetime in whatever
53+
/// optimization pipeline is currently used.
54+
/// When this is 'false' the pipeline has default behavior.
55+
bool DisableCopyPropagation = false;
56+
4757
/// Controls whether the SIL ARC optimizations are run.
4858
bool EnableARCOptimizations = true;
4959

include/swift/Option/FrontendOptions.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ def batch_scan_input_file
188188
def import_prescan : Flag<["-"], "import-prescan">,
189189
HelpText<"When performing a dependency scan, only dentify all imports of the main Swift module sources">;
190190

191+
def enable_copy_propagation : Flag<["-"], "enable-copy-propagation">,
192+
HelpText<"Run SIL copy propagation to shorten object lifetime.">;
193+
def disable_copy_propagation : Flag<["-"], "disable-copy-propagation">,
194+
HelpText<"Don't run SIL copy propagation to preserve object lifetime.">;
195+
191196
def enable_infer_public_concurrent_value : Flag<["-"], "enable-infer-public-concurrent-value">,
192197
HelpText<"Enable inference of ConcurrentValue conformances for public structs and enums">;
193198

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,8 @@ PASS(GlobalOpt, "global-opt",
202202
"SIL Global Optimization")
203203
PASS(GlobalPropertyOpt, "global-property-opt",
204204
"Global Property Optimization")
205-
PASS(GuaranteedARCOpts, "guaranteed-arc-opts",
206-
"Guaranteed ARC Optimization")
205+
PASS(MandatoryARCOpts, "mandatory-arc-opts",
206+
"Mandatory ARC Optimization")
207207
PASS(HighLevelCSE, "high-level-cse",
208208
"Common Subexpression Elimination on High-Level SIL")
209209
PASS(HighLevelLICM, "high-level-licm",

include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ class CanonicalizeOSSALifetime {
187187
private:
188188
/// If true, then debug_value instructions outside of non-debug
189189
/// liveness may be pruned during canonicalization.
190-
bool pruneDebug;
190+
bool pruneDebugMode;
191191

192192
NonLocalAccessBlockAnalysis *accessBlockAnalysis;
193193
// Lazily initialize accessBlocks only when
@@ -237,12 +237,13 @@ class CanonicalizeOSSALifetime {
237237
CanonicalOSSAConsumeInfo consumes;
238238

239239
public:
240-
CanonicalizeOSSALifetime(bool pruneDebug,
240+
CanonicalizeOSSALifetime(bool pruneDebugMode,
241241
NonLocalAccessBlockAnalysis *accessBlockAnalysis,
242242
DominanceAnalysis *dominanceAnalysis,
243243
DeadEndBlocks *deBlocks)
244-
: pruneDebug(pruneDebug), accessBlockAnalysis(accessBlockAnalysis),
245-
dominanceAnalysis(dominanceAnalysis), deBlocks(deBlocks) {}
244+
: pruneDebugMode(pruneDebugMode),
245+
accessBlockAnalysis(accessBlockAnalysis),
246+
dominanceAnalysis(dominanceAnalysis), deBlocks(deBlocks) {}
246247

247248
SILValue getCurrentDef() const { return currentDef; }
248249

lib/Frontend/CompilerInvocation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,8 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
11891189
// -Ounchecked might also set removal of runtime asserts (cond_fail).
11901190
Opts.RemoveRuntimeAsserts |= Args.hasArg(OPT_RemoveRuntimeAsserts);
11911191

1192+
Opts.EnableCopyPropagation |= Args.hasArg(OPT_enable_copy_propagation);
1193+
Opts.DisableCopyPropagation |= Args.hasArg(OPT_disable_copy_propagation);
11921194
Opts.EnableARCOptimizations &= !Args.hasArg(OPT_disable_arc_opts);
11931195
Opts.EnableOSSAModules |= Args.hasArg(OPT_enable_ossa_modules);
11941196
Opts.EnableOSSAOptimizations &= !Args.hasArg(OPT_disable_ossa_opts);

lib/SIL/Verifier/MemoryLifetime.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,13 @@ void MemoryLocations::analyzeLocations(SILFunction *function) {
168168
case SILArgumentConvention::Indirect_In:
169169
case SILArgumentConvention::Indirect_In_Constant:
170170
case SILArgumentConvention::Indirect_In_Guaranteed:
171-
case SILArgumentConvention::Indirect_Inout:
172171
case SILArgumentConvention::Indirect_Out:
172+
// These are not SIL addresses under -enable-sil-opaque-values
173+
if (!function->getConventions().useLoweredAddresses())
174+
break;
175+
176+
LLVM_FALLTHROUGH;
177+
case SILArgumentConvention::Indirect_Inout:
173178
analyzeLocation(funcArg);
174179
break;
175180
default:

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,9 @@ void addFunctionPasses(SILPassPipelinePlan &P,
350350
if (P.getOptions().EnableOSSAModules) {
351351
// We earlier eliminated ownership if we are not compiling the stdlib. Now
352352
// handle the stdlib functions, re-simplifying, eliminating ARC as we do.
353-
P.addCopyPropagation();
353+
if (!P.getOptions().DisableCopyPropagation) {
354+
P.addCopyPropagation();
355+
}
354356
P.addSemanticARCOpts();
355357
}
356358

@@ -372,7 +374,9 @@ void addFunctionPasses(SILPassPipelinePlan &P,
372374

373375
// Clean up Semantic ARC before we perform additional post-inliner opts.
374376
if (P.getOptions().EnableOSSAModules) {
375-
P.addCopyPropagation();
377+
if (!P.getOptions().DisableCopyPropagation) {
378+
P.addCopyPropagation();
379+
}
376380
P.addSemanticARCOpts();
377381
}
378382

@@ -437,7 +441,9 @@ void addFunctionPasses(SILPassPipelinePlan &P,
437441

438442
// Run a final round of ARC opts when ownership is enabled.
439443
if (P.getOptions().EnableOSSAModules) {
440-
P.addCopyPropagation();
444+
if (!P.getOptions().DisableCopyPropagation) {
445+
P.addCopyPropagation();
446+
}
441447
P.addSemanticARCOpts();
442448
}
443449
}
@@ -471,7 +477,9 @@ static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) {
471477
// Cleanup after SILGen: remove trivial copies to temporaries.
472478
P.addTempRValueOpt();
473479
// Cleanup after SILGen: remove unneeded borrows/copies.
474-
P.addCopyPropagation();
480+
if (!P.getOptions().DisableCopyPropagation) {
481+
P.addCopyPropagation();
482+
}
475483
P.addSemanticARCOpts();
476484

477485
// Devirtualizes differentiability witnesses into functions that reference them.
@@ -785,7 +793,9 @@ SILPassPipelinePlan::getPerformancePassPipeline(const SILOptions &Options) {
785793
// Run one last copy propagation/semantic arc opts run before serialization/us
786794
// lowering ownership.
787795
if (P.getOptions().EnableOSSAModules) {
788-
P.addCopyPropagation();
796+
if (!P.getOptions().DisableCopyPropagation) {
797+
P.addCopyPropagation();
798+
}
789799
P.addSemanticARCOpts();
790800
}
791801

@@ -835,7 +845,13 @@ SILPassPipelinePlan::getOnonePassPipeline(const SILOptions &Options) {
835845
P.startPipeline("non-Diagnostic Enabling Mandatory Optimizations");
836846
P.addForEachLoopUnroll();
837847
P.addMandatoryCombine();
838-
P.addGuaranteedARCOpts();
848+
if (P.getOptions().EnableCopyPropagation) {
849+
// MandatoryCopyPropagation should only be run at -Onone, not -O.
850+
P.addMandatoryCopyPropagation();
851+
}
852+
// TODO: MandatoryARCOpts should be subsumed by CopyPropagation. There should
853+
// be no need to run another analysis of copies at -Onone.
854+
P.addMandatoryARCOpts();
839855

840856
// First serialize the SIL if we are asked to.
841857
P.startPipeline("Serialization");

lib/SILOptimizer/SemanticARC/Context.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ struct LLVM_LIBRARY_VISIBILITY Context {
109109
/// As an example, we do not do load [copy] optimizations here since they
110110
/// generally involve more complex analysis, but simple peepholes of
111111
/// copy_values we /do/ allow.
112-
bool onlyGuaranteedOpts;
112+
bool onlyMandatoryOpts;
113113

114114
/// Callbacks that we must use to remove or RAUW values.
115115
InstModCallbacks instModCallbacks;
@@ -119,11 +119,11 @@ struct LLVM_LIBRARY_VISIBILITY Context {
119119

120120
DeadEndBlocks &getDeadEndBlocks() { return deadEndBlocks; }
121121

122-
Context(SILFunction &fn, DeadEndBlocks &deBlocks, bool onlyGuaranteedOpts,
122+
Context(SILFunction &fn, DeadEndBlocks &deBlocks, bool onlyMandatoryOpts,
123123
InstModCallbacks callbacks)
124124
: fn(fn), deadEndBlocks(deBlocks), lifetimeFrontier(),
125125
addressToExhaustiveWriteListCache(constructCacheValue),
126-
onlyGuaranteedOpts(onlyGuaranteedOpts), instModCallbacks(callbacks) {}
126+
onlyMandatoryOpts(onlyMandatoryOpts), instModCallbacks(callbacks) {}
127127

128128
void verify() const;
129129

lib/SILOptimizer/SemanticARC/CopyValueOpts.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,11 @@ using namespace swift::semanticarc;
6666
// are within the borrow scope.
6767
//
6868
// TODO: This needs a better name.
69-
//
70-
// FIXME: CanonicalizeOSSALifetime replaces this once it supports borrows.
7169
bool SemanticARCOptVisitor::performGuaranteedCopyValueOptimization(
7270
CopyValueInst *cvi) {
73-
// For now, do not run this optimization. This is just to be careful.
74-
if (ctx.onlyGuaranteedOpts)
71+
// All mandatory copy optimization is handled by CanonicalizeOSSALifetime,
72+
// which knows how to preserve lifetimes for debugging.
73+
if (ctx.onlyMandatoryOpts)
7574
return false;
7675

7776
SmallVector<BorrowedValue, 4> borrowScopeIntroducers;
@@ -721,11 +720,11 @@ bool SemanticARCOptVisitor::tryJoiningCopyValueLiveRangeWithOperand(
721720

722721
/// Given an owned value that is completely enclosed within its parent owned
723722
/// value and is not consumed, eliminate the copy.
724-
///
725-
/// FIXME: CanonicalizeOSSALifetime replaces this.
726723
bool SemanticARCOptVisitor::tryPerformOwnedCopyValueOptimization(
727724
CopyValueInst *cvi) {
728-
if (ctx.onlyGuaranteedOpts)
725+
// All mandatory copy optimization is handled by CanonicalizeOSSALifetime,
726+
// which knows how to preserve lifetimes for debugging.
727+
if (ctx.onlyMandatoryOpts)
729728
return false;
730729

731730
auto originalValue = cvi->getOperand();

lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ static bool isWrittenTo(Context &ctx, LoadInst *load,
321321
bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) {
322322
// This optimization can use more complex analysis. We should do some
323323
// experiments before enabling this by default as a guaranteed optimization.
324-
if (ctx.onlyGuaranteedOpts)
324+
if (ctx.onlyMandatoryOpts)
325325
return false;
326326

327327
// If we are not supposed to perform this transform, bail.

lib/SILOptimizer/SemanticARC/OwnershipConversionElimination.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ using namespace semanticarc;
2424
bool SemanticARCOptVisitor::visitUncheckedOwnershipConversionInst(
2525
UncheckedOwnershipConversionInst *uoci) {
2626
// Return false if we are supposed to only be running guaranteed opts.
27-
if (ctx.onlyGuaranteedOpts)
27+
if (ctx.onlyMandatoryOpts)
2828
return false;
2929

3030
// Then check if we are running tests and shouldn't perform this optimization

lib/SILOptimizer/SemanticARC/SemanticARCOptVisitor.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ struct LLVM_LIBRARY_VISIBILITY SemanticARCOptVisitor
5050
Context ctx;
5151

5252
explicit SemanticARCOptVisitor(SILFunction &fn, DeadEndBlocks &deBlocks,
53-
bool onlyGuaranteedOpts)
54-
: ctx(fn, deBlocks, onlyGuaranteedOpts,
53+
bool onlyMandatoryOpts)
54+
: ctx(fn, deBlocks, onlyMandatoryOpts,
5555
InstModCallbacks(
5656
[this](SILInstruction *inst) { eraseInstruction(inst); },
5757
[this](Operand *use, SILValue newValue) {

lib/SILOptimizer/SemanticARC/SemanticARCOpts.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@ namespace {
6666
// case DiagnosticConstantPropagation exposed anything new in this assert
6767
// configuration.
6868
struct SemanticARCOpts : SILFunctionTransform {
69-
bool guaranteedOptsOnly;
69+
bool mandatoryOptsOnly;
7070

71-
SemanticARCOpts(bool guaranteedOptsOnly)
72-
: guaranteedOptsOnly(guaranteedOptsOnly) {}
71+
SemanticARCOpts(bool mandatoryOptsOnly)
72+
: mandatoryOptsOnly(mandatoryOptsOnly) {}
7373

7474
#ifndef NDEBUG
7575
void performCommandlineSpecifiedTransforms(SemanticARCOptVisitor &visitor) {
@@ -153,7 +153,7 @@ struct SemanticARCOpts : SILFunctionTransform {
153153

154154
auto *deBlocksAnalysis = getAnalysis<DeadEndBlocksAnalysis>();
155155
SemanticARCOptVisitor visitor(f, *deBlocksAnalysis->get(&f),
156-
guaranteedOptsOnly);
156+
mandatoryOptsOnly);
157157

158158
#ifndef NDEBUG
159159
// If we are being asked for testing purposes to run a series of transforms
@@ -185,9 +185,9 @@ struct SemanticARCOpts : SILFunctionTransform {
185185
} // end anonymous namespace
186186

187187
SILTransform *swift::createSemanticARCOpts() {
188-
return new SemanticARCOpts(false /*guaranteed*/);
188+
return new SemanticARCOpts(false /*mandatory*/);
189189
}
190190

191-
SILTransform *swift::createGuaranteedARCOpts() {
192-
return new SemanticARCOpts(true /*guaranteed*/);
191+
SILTransform *swift::createMandatoryARCOpts() {
192+
return new SemanticARCOpts(true /*mandatory*/);
193193
}

lib/SILOptimizer/Transforms/CopyPropagation.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,12 @@ namespace {
4242
class CopyPropagation : public SILFunctionTransform {
4343
/// True if debug_value instructions should be pruned.
4444
bool pruneDebug;
45+
/// True of all values should be canonicalized.
46+
bool canonicalizeAll;
4547

4648
public:
47-
CopyPropagation(bool pruneDebug): pruneDebug(pruneDebug) {}
49+
CopyPropagation(bool pruneDebug, bool canonicalizeAll)
50+
: pruneDebug(pruneDebug), canonicalizeAll(canonicalizeAll) {}
4851

4952
/// The entry point to this function transformation.
5053
void run() override;
@@ -83,9 +86,15 @@ void CopyPropagation::run() {
8386
llvm::SmallSetVector<SILValue, 16> copiedDefs;
8487
for (auto &bb : *f) {
8588
for (auto &i : bb) {
86-
if (auto *copy = dyn_cast<CopyValueInst>(&i))
89+
if (auto *copy = dyn_cast<CopyValueInst>(&i)) {
8790
copiedDefs.insert(
8891
CanonicalizeOSSALifetime::getCanonicalCopiedDef(copy));
92+
} else if (canonicalizeAll) {
93+
if (auto *destroy = dyn_cast<DestroyValueInst>(&i)) {
94+
copiedDefs.insert(CanonicalizeOSSALifetime::getCanonicalCopiedDef(
95+
destroy->getOperand()));
96+
}
97+
}
8998
}
9099
}
91100
// Perform copy propgation for each copied value.
@@ -122,14 +131,16 @@ void CopyPropagation::run() {
122131
accessBlockAnalysis->lockInvalidation();
123132
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
124133
accessBlockAnalysis->unlockInvalidation();
125-
f->verifyOwnership(deBlocksAnalysis->get(f));
134+
if (f->getModule().getOptions().VerifySILOwnership) {
135+
f->verifyOwnership(deBlocksAnalysis->get(f));
136+
}
126137
}
127138
}
128139

129-
SILTransform *swift::createCopyPropagation() {
130-
return new CopyPropagation(/*pruneDebug*/ true);
140+
SILTransform *swift::createMandatoryCopyPropagation() {
141+
return new CopyPropagation(/*pruneDebug*/ true, /*canonicalizeAll*/ true);
131142
}
132143

133-
SILTransform *swift::createMandatoryCopyPropagation() {
134-
return new CopyPropagation(/*pruneDebug*/ false);
144+
SILTransform *swift::createCopyPropagation() {
145+
return new CopyPropagation(/*pruneDebug*/ true, /*canonicalizeAll*/ false);
135146
}

lib/SILOptimizer/Utils/CanonicalOSSALifetime.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ bool CanonicalizeOSSALifetime::computeCanonicalLiveness() {
369369
continue;
370370
}
371371
// Handle debug_value instructions separately.
372-
if (pruneDebug) {
372+
if (pruneDebugMode) {
373373
if (auto *dvi = dyn_cast<DebugValueInst>(user)) {
374374
// Only instructions potentially outside current pruned liveness are
375375
// interesting.
@@ -647,7 +647,7 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) {
647647
while (true) {
648648
auto *inst = &*instIter;
649649

650-
if (pruneDebug) {
650+
if (pruneDebugMode) {
651651
if (auto *dvi = dyn_cast<DebugValueInst>(inst)) {
652652
if (debugValues.erase(dvi))
653653
consumes.recordDebugAfterConsume(dvi);
@@ -675,7 +675,8 @@ void CanonicalizeOSSALifetime::findOrInsertDestroyInBlock(SILBasicBlock *bb) {
675675
return;
676676
}
677677
// This is not a potential last user. Keep scanning.
678-
// Allow lifetimes to be artificially extended up to the next call.
678+
// Allow lifetimes to be artificially extended up to the next call. The goal
679+
// is to prevent repeated destroy rewriting without inhibiting optimization.
679680
if (ApplySite::isa(inst)) {
680681
existingDestroy = nullptr;
681682
} else if (!existingDestroy) {
@@ -842,9 +843,10 @@ void CanonicalizeOSSALifetime::rewriteCopies() {
842843
if (reusedCopyOp) {
843844
reusedCopyOp->set(srcCopy);
844845
} else {
845-
instsToDelete.insert(srcCopy);
846-
LLVM_DEBUG(llvm::dbgs() << " Removing " << *srcCopy);
847-
++NumCopiesEliminated;
846+
if (instsToDelete.insert(srcCopy)) {
847+
LLVM_DEBUG(llvm::dbgs() << " Removing " << *srcCopy);
848+
++NumCopiesEliminated;
849+
}
848850
}
849851
}
850852
}

test/IRGen/struct_resilience.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// RUN: %empty-directory(%t)
33
// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_struct.swiftmodule -module-name=resilient_struct %S/../Inputs/resilient_struct.swift
44
// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_enum.swiftmodule -module-name=resilient_enum -I %t %S/../Inputs/resilient_enum.swift
5-
// RUN: %target-swift-frontend -module-name struct_resilience -Xllvm -sil-disable-pass=GuaranteedARCOpts -I %t -emit-ir -enable-library-evolution %s | %FileCheck %s
5+
// RUN: %target-swift-frontend -module-name struct_resilience -Xllvm -sil-disable-pass=MandatoryARCOpts -I %t -emit-ir -enable-library-evolution %s | %FileCheck %s
66
// RUN: %target-swift-frontend -module-name struct_resilience -I %t -emit-ir -enable-library-evolution -O %s
77

88
import resilient_struct

0 commit comments

Comments
 (0)