Skip to content

Commit 889baf7

Browse files
authored
Merge pull request #84411 from kavon/copyprop-onone
sil: provide ability to run CopyPropagation in -Onone
2 parents 7fcc72f + 4a943d4 commit 889baf7

File tree

11 files changed

+184
-74
lines changed

11 files changed

+184
-74
lines changed

include/swift/AST/SILOptions.h

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,14 @@ enum class CopyPropagationOption : uint8_t {
4949
// just -enable-ossa-modules.
5050
RequestedPassesOnly,
5151

52-
// Add all relevant copy propagation passes. If a setting, e.g.
53-
// -enable-ossa-modules, requests to add copy propagation to the pipeline, do
54-
// so.
55-
On
52+
// Run copy propagation during optimized builds only.
53+
//
54+
// If a setting, e.g. -enable-ossa-modules, requests to add copy propagation
55+
// to the performance pipeline, do so.
56+
Optimizing,
57+
58+
// Run copy propagation during all builds and whenever requested.
59+
Always
5660
};
5761

5862
enum class DestroyHoistingOption : uint8_t {
@@ -90,11 +94,8 @@ class SILOptions {
9094
/// observable end of lexical scope.
9195
LexicalLifetimesOption LexicalLifetimes = LexicalLifetimesOption::On;
9296

93-
/// Whether to run SIL copy propagation to shorten object lifetime in whatever
94-
/// optimization pipeline is currently used.
95-
///
96-
/// When this is 'On' the pipeline has default behavior.
97-
CopyPropagationOption CopyPropagation = CopyPropagationOption::On;
97+
/// Controls when to run SIL copy propagation, which shortens object lifetimes
98+
CopyPropagationOption CopyPropagation = CopyPropagationOption::Optimizing;
9899

99100
/// Whether to run the DestroyAddrHoisting pass.
100101
///

include/swift/Option/FrontendOptions.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,12 +282,12 @@ def no_parallel_scan : Flag<["-"], "no-parallel-scan">,
282282
HelpText<"Perform dependency scanning in a single-threaded fashion.">;
283283

284284
def enable_copy_propagation : Flag<["-"], "enable-copy-propagation">,
285-
HelpText<"Run SIL copy propagation with lexical lifetimes to shorten object "
285+
HelpText<"Always run SIL copy propagation with lexical lifetimes to shorten object "
286286
"lifetimes while preserving variable lifetimes.">;
287287
def copy_propagation_state_EQ :
288288
Joined<["-"], "enable-copy-propagation=">,
289289
HelpText<"Whether to enable copy propagation">,
290-
MetaVarName<"true|requested-passes-only|false">;
290+
MetaVarName<"always|optimizing|requested-passes-only|false">;
291291

292292
def disable_infer_public_concurrent_value : Flag<["-"], "disable-infer-public-sendable">,
293293
HelpText<"Disable inference of Sendable conformances for public structs and enums">;

lib/DriverTool/sil_opt_main.cpp

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,11 @@ operator<<(raw_ostream &os, const std::optional<CopyPropagationOption> option) {
115115
case CopyPropagationOption::RequestedPassesOnly:
116116
os << "requested-passes-only";
117117
break;
118-
case CopyPropagationOption::On:
119-
os << "on";
118+
case CopyPropagationOption::Optimizing:
119+
os << "optimizing";
120+
break;
121+
case CopyPropagationOption::Always:
122+
os << "always";
120123
break;
121124
}
122125
} else {
@@ -135,24 +138,27 @@ class parser<std::optional<CopyPropagationOption>>
135138
// parse - Return true on error.
136139
bool parse(Option &O, StringRef ArgName, StringRef Arg,
137140
std::optional<CopyPropagationOption> &Value) {
138-
if (Arg == "" || Arg == "true" || Arg == "TRUE" || Arg == "True" ||
139-
Arg == "1") {
140-
Value = CopyPropagationOption::On;
141+
if (Arg.compare_insensitive("always")) {
142+
Value = CopyPropagationOption::Always;
143+
return false;
144+
}
145+
if (Arg.compare_insensitive("optimizing")) {
146+
Value = CopyPropagationOption::Optimizing;
141147
return false;
142148
}
143-
if (Arg == "false" || Arg == "FALSE" || Arg == "False" || Arg == "0") {
149+
if (Arg.compare_insensitive("false") || Arg.compare_insensitive("off") ||
150+
Arg == "0") {
144151
Value = CopyPropagationOption::Off;
145152
return false;
146153
}
147-
if (Arg == "requested-passes-only" || Arg == "REQUESTED-PASSES-ONLY" ||
148-
Arg == "Requested-Passes-Only") {
154+
if (Arg.compare_insensitive("requested-passes-only")) {
149155
Value = CopyPropagationOption::RequestedPassesOnly;
150156
return false;
151157
}
152158

153159
return O.error("'" + Arg +
154-
"' is invalid for CopyPropagationOption! Try true, false, "
155-
"or requested-passes-only.");
160+
"' is invalid for CopyPropagationOption!"
161+
" Try always, optimizing, or requested-passes-only.");
156162
}
157163

158164
void initialize() {}
@@ -907,7 +913,7 @@ int sil_opt_main(ArrayRef<const char *> argv, void *MainAddr) {
907913

908914
// Unless overridden below, enabling copy propagation means enabling lexical
909915
// lifetimes.
910-
if (SILOpts.CopyPropagation == CopyPropagationOption::On)
916+
if (SILOpts.CopyPropagation >= CopyPropagationOption::Optimizing)
911917
SILOpts.LexicalLifetimes = LexicalLifetimesOption::On;
912918

913919
// Unless overridden below, disable copy propagation means disabling lexical

lib/Frontend/CompilerInvocation.cpp

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2908,33 +2908,22 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
29082908
if (Arg *A = Args.getLastArg(OPT_copy_propagation_state_EQ)) {
29092909
specifiedCopyPropagationOption =
29102910
llvm::StringSwitch<std::optional<CopyPropagationOption>>(A->getValue())
2911-
.Case("true", CopyPropagationOption::On)
2911+
.Case("always", CopyPropagationOption::Always)
2912+
.Case("optimizing", CopyPropagationOption::Optimizing)
29122913
.Case("false", CopyPropagationOption::Off)
29132914
.Case("requested-passes-only",
29142915
CopyPropagationOption::RequestedPassesOnly)
29152916
.Default(std::nullopt);
2917+
2918+
// Error if copy propagation has been set via the flag at the same time.
2919+
if (auto *Flag = Args.getLastArg(OPT_enable_copy_propagation)) {
2920+
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_combination,
2921+
Flag->getAsString(Args),
2922+
A->getAsString(Args));
2923+
}
29162924
}
29172925
if (Args.hasArg(OPT_enable_copy_propagation)) {
2918-
if (specifiedCopyPropagationOption) {
2919-
if (*specifiedCopyPropagationOption == CopyPropagationOption::Off) {
2920-
// Error if copy propagation has been set to ::Off via the meta-var form
2921-
// and enabled via the flag.
2922-
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_combination,
2923-
"enable-copy-propagation",
2924-
"enable-copy-propagation=false");
2925-
return true;
2926-
} else if (*specifiedCopyPropagationOption ==
2927-
CopyPropagationOption::RequestedPassesOnly) {
2928-
// Error if copy propagation has been set to ::RequestedPassesOnly via
2929-
// the meta-var form and enabled via the flag.
2930-
Diags.diagnose(SourceLoc(), diag::error_invalid_arg_combination,
2931-
"enable-copy-propagation",
2932-
"enable-copy-propagation=requested-passes-only");
2933-
return true;
2934-
}
2935-
} else {
2936-
specifiedCopyPropagationOption = CopyPropagationOption::On;
2937-
}
2926+
specifiedCopyPropagationOption = CopyPropagationOption::Always;
29382927
}
29392928
if (specifiedCopyPropagationOption) {
29402929
Opts.CopyPropagation = *specifiedCopyPropagationOption;
@@ -2966,7 +2955,7 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
29662955

29672956
// Unless overridden below, enabling copy propagation means enabling lexical
29682957
// lifetimes.
2969-
if (Opts.CopyPropagation == CopyPropagationOption::On) {
2958+
if (Opts.CopyPropagation >= CopyPropagationOption::Optimizing) {
29702959
Opts.LexicalLifetimes = LexicalLifetimesOption::On;
29712960
Opts.DestroyHoisting = DestroyHoistingOption::On;
29722961
}

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) {
255255

256256
// Only issue weak lifetime warnings for users who select object lifetime
257257
// optimization. The risk of spurious warnings outweighs the benefits.
258-
if (P.getOptions().CopyPropagation == CopyPropagationOption::On) {
258+
if (P.getOptions().CopyPropagation >= CopyPropagationOption::Optimizing) {
259259
P.addDiagnoseLifetimeIssues();
260260
}
261261

@@ -278,6 +278,23 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) {
278278

279279
P.addDiagnoseUnknownConstValues();
280280
P.addEmbeddedSwiftDiagnostics();
281+
282+
/// FIXME: Ideally, we'd have this relative order:
283+
/// 1. DiagnoseLifetimeIssues
284+
/// 2. CopyPropagation
285+
/// 3. AddressLowering
286+
/// to get the maximum benefits of CopyPropagation + OpaqueValues in -Onone.
287+
if (P.getOptions().CopyPropagation == CopyPropagationOption::Always) {
288+
// FIXME: ComputeSideEffects helps CopyPropagation simplify across
289+
// call-sites, but PerformanceDiagnostics is sensitive to the # of copies.
290+
// If ManualOwnership is used in the compiler itself, we wouldn't be able
291+
// to bootstrap the compiler on different platforms with same diagnostics.
292+
#ifdef SWIFT_ENABLE_SWIFT_IN_SWIFT
293+
P.addComputeSideEffects();
294+
#endif
295+
P.addMandatoryCopyPropagation();
296+
}
297+
281298
P.addPerformanceDiagnostics();
282299
}
283300

@@ -655,7 +672,7 @@ static void addPerfEarlyModulePassPipeline(SILPassPipelinePlan &P) {
655672
// Cleanup after SILGen: remove trivial copies to temporaries.
656673
P.addTempRValueElimination();
657674
// Cleanup after SILGen: remove unneeded borrows/copies.
658-
if (P.getOptions().CopyPropagation == CopyPropagationOption::On) {
675+
if (P.getOptions().CopyPropagation >= CopyPropagationOption::Optimizing) {
659676
P.addComputeSideEffects();
660677
P.addCopyPropagation();
661678
}

lib/SILOptimizer/SILCombiner/SILCombine.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ class SILCombine : public SILFunctionTransform {
668668
/// The entry point to the transformation.
669669
void run() override {
670670
bool enableCopyPropagation =
671-
getOptions().CopyPropagation == CopyPropagationOption::On;
671+
getOptions().CopyPropagation >= CopyPropagationOption::Optimizing;
672672
if (getOptions().EnableOSSAModules) {
673673
enableCopyPropagation =
674674
getOptions().CopyPropagation != CopyPropagationOption::Off;

lib/SILOptimizer/Transforms/CopyPropagation.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -688,10 +688,10 @@ void CopyPropagation::verifyOwnership() {
688688
: deBlocksAnalysis->get(f));
689689
}
690690

691-
// MandatoryCopyPropagation is not currently enabled in the -Onone pipeline
692-
// because it may negatively affect the debugging experience.
691+
// MandatoryCopyPropagation runs in the -Onone pipeline and needs to be more
692+
// conservative, preserving debug information.
693693
SILTransform *swift::createMandatoryCopyPropagation() {
694-
return new CopyPropagation(PruneDebugInsts, /*canonicalizeAll*/ true,
694+
return new CopyPropagation(DontPruneDebugInsts, /*canonicalizeAll*/ true,
695695
/*canonicalizeBorrows*/ false);
696696
}
697697

test/SIL/manual_ownership.swift

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// RUN: %target-swift-frontend %s -emit-sil -verify -enable-experimental-feature ManualOwnership
1+
// RUN: %target-swift-frontend %s -emit-sil -verify \
2+
// RUN: -enable-experimental-feature ManualOwnership \
3+
// RUN: -enable-copy-propagation=always
24

35
// REQUIRES: swift_feature_ManualOwnership
46

@@ -12,6 +14,7 @@ public struct Pair {
1214
var x: Int
1315
var y: Int
1416

17+
@_manualOwnership
1518
consuming func midpoint(_ other: borrowing Pair) -> Pair {
1619
return Pair(x: (x + other.x) / 2, y: (y + other.y) / 2)
1720
}
@@ -24,7 +27,10 @@ public class Triangle {
2427

2528
var nontrivial = Whatever()
2629

30+
@_manualOwnership
2731
consuming func consuming() {}
32+
33+
@_manualOwnership
2834
borrowing func borrowing() {}
2935
}
3036

@@ -33,14 +39,8 @@ public class Triangle {
3339
@_manualOwnership
3440
public func basic_return1() -> Triangle {
3541
let x = Triangle()
36-
return x // expected-error {{explicit 'copy' required here}}
42+
return x
3743
}
38-
@_manualOwnership
39-
public func basic_return1_fixed() -> Triangle {
40-
let x = Triangle()
41-
return copy x
42-
}
43-
4444

4545
@_manualOwnership
4646
public func basic_return2(t: Triangle) -> Triangle {
@@ -56,24 +56,39 @@ public func basic_return3() -> Triangle {
5656
return Triangle()
5757
}
5858

59-
// FIXME: we need copy propagation in -Onone to eliminate all these copies
6059
@_manualOwnership
6160
func reassign_with_lets() -> Triangle {
6261
let x = Triangle()
63-
let y = x // expected-error {{explicit 'copy' required here}}
64-
let z = y // expected-error {{explicit 'copy' required here}}
65-
return z // expected-error {{explicit 'copy' required here}}
62+
let y = x
63+
let z = y
64+
return z
6665
}
6766

68-
// FIXME: we need copy propagation in -Onone to eliminate all but the copies for returning
6967
@_manualOwnership
7068
func renamed_return(_ cond: Bool, _ a: Triangle) -> Triangle {
71-
let b = a // expected-error {{explicit 'copy' required here}}
72-
let c = b // expected-error {{explicit 'copy' required here}}
69+
let b = a
70+
let c = b
7371
if cond { return b } // expected-error {{explicit 'copy' required here}}
7472
return c // expected-error {{explicit 'copy' required here}}
7573
}
7674

75+
@_manualOwnership
76+
func renamed_return_fix1(_ cond: Bool, _ a: Triangle) -> Triangle {
77+
let b = copy a
78+
let c = copy b // FIXME: not needed! Is explicit_copy_value is blocking propagation?
79+
if cond { return b }
80+
return c
81+
}
82+
83+
// FIXME: this crashes CopyPropagation!
84+
//@_manualOwnership
85+
//func renamed_return_fix2(_ cond: Bool, _ a: Triangle) -> Triangle {
86+
// let b = a
87+
// let c = b
88+
// if cond { return copy b }
89+
// return copy c
90+
//}
91+
7792
/// MARK: method calls
7893

7994
@_manualOwnership
@@ -87,7 +102,7 @@ func basic_methods_borrowing(_ t1: Triangle) {
87102
func basic_methods_consuming(_ t1: Triangle) {
88103
let t2 = Triangle()
89104
t1.consuming() // expected-error {{explicit 'copy' required here}}
90-
t2.consuming() // expected-error {{explicit 'copy' required here}}
105+
t2.consuming()
91106
}
92107
@_manualOwnership
93108
func basic_methods_consuming_fixed(_ t1: Triangle) {
@@ -96,7 +111,7 @@ func basic_methods_consuming_fixed(_ t1: Triangle) {
96111
t3 = Triangle()
97112

98113
(copy t1).consuming()
99-
(copy t2).consuming()
114+
(copy t2).consuming() // FIXME: why is this not propagated?
100115
(copy t3).consuming()
101116
}
102117

@@ -113,12 +128,12 @@ func basic_function_call(_ t1: Triangle) {
113128
/// MARK: control-flow
114129

115130

116-
// FIXME: var's and assignments are a little busted
131+
// FIXME: var assignments are somtimes impossible to satisfy with 'copy'
117132

118133
// @_manualOwnership
119134
// func reassignments_1() {
120135
// var t3 = Triangle()
121-
// t3 = Triangle()
136+
// t3 = copy Triangle() // FIXME: should not be needed
122137
// t3.borrowing()
123138
// }
124139
// @_manualOwnership

0 commit comments

Comments
 (0)