Skip to content

Commit ef0355b

Browse files
Merge pull request #11503 from aschwaighofer/optimize_for_size
Add an option to optimize for size -Osize
2 parents 15a5b84 + 9d1ae9a commit ef0355b

20 files changed

+126
-12
lines changed

include/swift/AST/IRGenOptions.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ class IRGenOptions {
9797
/// Whether or not to run optimization passes.
9898
unsigned Optimize : 1;
9999

100+
/// Whether or not to optimize for code size.
101+
unsigned OptimizeForSize : 1;
102+
100103
/// Which sanitizer is turned on.
101104
OptionSet<SanitizerKind> Sanitizers;
102105

@@ -173,7 +176,8 @@ class IRGenOptions {
173176

174177
IRGenOptions()
175178
: DWARFVersion(2), OutputKind(IRGenOutputKind::LLVMAssembly),
176-
Verify(true), Optimize(false), Sanitizers(OptionSet<SanitizerKind>()),
179+
Verify(true), Optimize(false), OptimizeForSize(false),
180+
Sanitizers(OptionSet<SanitizerKind>()),
177181
DebugInfoKind(IRGenDebugInfoKind::None), UseJIT(false),
178182
DisableLLVMOptzns(false), DisableLLVMARCOpts(false),
179183
DisableLLVMSLPVectorizer(false), DisableFPElim(true), Playground(false),
@@ -197,6 +201,7 @@ class IRGenOptions {
197201
unsigned getLLVMCodeGenOptionsHash() {
198202
unsigned Hash = 0;
199203
Hash = (Hash << 1) | Optimize;
204+
Hash = (Hash << 1) | OptimizeForSize;
200205
Hash = (Hash << 1) | DisableLLVMOptzns;
201206
Hash = (Hash << 1) | DisableLLVMARCOpts;
202207
return Hash;

include/swift/AST/SILOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class SILOptions {
5252
None,
5353
Debug,
5454
Optimize,
55+
OptimizeForSize,
5556
OptimizeUnchecked
5657
};
5758

include/swift/Option/Options.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@ def Onone : Flag<["-"], "Onone">, Group<O_Group>, Flags<[FrontendOption]>,
374374
HelpText<"Compile without any optimization">;
375375
def O : Flag<["-"], "O">, Group<O_Group>, Flags<[FrontendOption]>,
376376
HelpText<"Compile with optimizations">;
377+
def Osize : Flag<["-"], "Osize">, Group<O_Group>, Flags<[FrontendOption]>,
378+
HelpText<"Compile with optimizations and target small code size">;
377379
def Ounchecked : Flag<["-"], "Ounchecked">, Group<O_Group>,
378380
Flags<[FrontendOption]>,
379381
HelpText<"Compile with optimizations and remove runtime safety checks">;

lib/Frontend/CompilerInvocation.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,23 +1380,31 @@ static bool ParseSILArgs(SILOptions &Opts, ArgList &Args,
13801380
// Parse the optimization level.
13811381
// Default to Onone settings if no option is passed.
13821382
IRGenOpts.Optimize = false;
1383+
IRGenOpts.OptimizeForSize = false;
13831384
Opts.Optimization = SILOptions::SILOptMode::None;
13841385
if (const Arg *A = Args.getLastArg(OPT_O_Group)) {
13851386
if (A->getOption().matches(OPT_Onone)) {
13861387
// Already set.
13871388
} else if (A->getOption().matches(OPT_Ounchecked)) {
13881389
// Turn on optimizations and remove all runtime checks.
13891390
IRGenOpts.Optimize = true;
1391+
IRGenOpts.OptimizeForSize = false;
13901392
Opts.Optimization = SILOptions::SILOptMode::OptimizeUnchecked;
13911393
// Removal of cond_fail (overflow on binary operations).
13921394
Opts.RemoveRuntimeAsserts = true;
13931395
Opts.AssertConfig = SILOptions::Unchecked;
13941396
} else if (A->getOption().matches(OPT_Oplayground)) {
13951397
// For now -Oplayground is equivalent to -Onone.
13961398
IRGenOpts.Optimize = false;
1399+
IRGenOpts.OptimizeForSize = false;
13971400
Opts.Optimization = SILOptions::SILOptMode::None;
1401+
} else if (A->getOption().matches(OPT_Osize)) {
1402+
IRGenOpts.Optimize = true;
1403+
IRGenOpts.OptimizeForSize = true;
1404+
Opts.Optimization = SILOptions::SILOptMode::OptimizeForSize;
13981405
} else {
13991406
assert(A->getOption().matches(OPT_O));
1407+
IRGenOpts.OptimizeForSize = false;
14001408
IRGenOpts.Optimize = true;
14011409
Opts.Optimization = SILOptions::SILOptMode::Optimize;
14021410
}

lib/FrontendTool/FrontendTool.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,6 +1066,7 @@ static bool emitIndexData(SourceFile *PrimarySourceFile,
10661066
isDebugCompilation = true;
10671067
break;
10681068
case SILOptions::SILOptMode::Optimize:
1069+
case SILOptions::SILOptMode::OptimizeForSize:
10691070
case SILOptions::SILOptMode::OptimizeUnchecked:
10701071
isDebugCompilation = false;
10711072
break;
@@ -1165,6 +1166,8 @@ silOptModeArgStr(SILOptions::SILOptMode mode) {
11651166
return "O";
11661167
case SILOptions::SILOptMode::OptimizeUnchecked:
11671168
return "Ounchecked";
1169+
case SILOptions::SILOptMode::OptimizeForSize:
1170+
return "Osize";
11681171
default:
11691172
return "Onone";
11701173
}

lib/IRGen/GenMeta.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,6 +1369,9 @@ static llvm::Function *getTypeMetadataAccessFunction(IRGenModule &IGM,
13691369
if (!isTypeMetadataAccessTrivial(IGM, type)) {
13701370
cacheVariable = cast<llvm::GlobalVariable>(
13711371
IGM.getAddrOfTypeMetadataLazyCacheVariable(type, ForDefinition));
1372+
1373+
if (IGM.getOptions().OptimizeForSize)
1374+
accessor->addFnAttr(llvm::Attribute::NoInline);
13721375
}
13731376

13741377
emitLazyCacheAccessFunction(IGM, accessor, cacheVariable,
@@ -1411,6 +1414,9 @@ static llvm::Function *getGenericTypeMetadataAccessFunction(IRGenModule &IGM,
14111414
if (!shouldDefine || !accessor->empty())
14121415
return accessor;
14131416

1417+
if (IGM.getOptions().OptimizeForSize)
1418+
accessor->addFnAttr(llvm::Attribute::NoInline);
1419+
14141420
emitLazyCacheAccessFunction(IGM, accessor, /*cacheVariable=*/nullptr,
14151421
[&](IRGenFunction &IGF) -> llvm::Value* {
14161422
return emitGenericMetadataAccessFunction(IGF, nominal, genericArgs);

lib/IRGen/GenProto.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,9 @@ getWitnessTableLazyAccessFunction(IRGenModule &IGM,
986986
if (!accessor->empty())
987987
return accessor;
988988

989+
if (IGM.getOptions().OptimizeForSize)
990+
accessor->addFnAttr(llvm::Attribute::NoInline);
991+
989992
// Okay, define the accessor.
990993
auto cacheVariable = cast<llvm::GlobalVariable>(
991994
IGM.getAddrOfWitnessTableLazyCacheVariable(conformance, conformingType,
@@ -1312,6 +1315,8 @@ getAssociatedTypeMetadataAccessFunction(AssociatedType requirement,
13121315
llvm::Function *accessor =
13131316
IGM.getAddrOfAssociatedTypeMetadataAccessFunction(&Conformance,
13141317
requirement);
1318+
if (IGM.getOptions().OptimizeForSize)
1319+
accessor->addFnAttr(llvm::Attribute::NoInline);
13151320

13161321
IRGenFunction IGF(IGM, accessor);
13171322
if (IGM.DebugInfo)
@@ -1420,6 +1425,9 @@ getAssociatedTypeWitnessTableAccessFunction(AssociatedConformance requirement,
14201425
if (IGM.DebugInfo)
14211426
IGM.DebugInfo->emitArtificialFunction(IGF, accessor);
14221427

1428+
if (IGM.getOptions().OptimizeForSize)
1429+
accessor->addFnAttr(llvm::Attribute::NoInline);
1430+
14231431
Explosion parameters = IGF.collectParameters();
14241432

14251433
llvm::Value *associatedTypeMetadata = parameters.claimNext();

lib/IRGen/IRGenModule.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,8 @@ void IRGenModule::constructInitialFnAttributes(llvm::AttrBuilder &Attrs) {
805805
});
806806
Attrs.addAttribute("target-features", allFeatures);
807807
}
808+
if (IRGen.Opts.OptimizeForSize)
809+
Attrs.addAttribute(llvm::Attribute::OptimizeForSize);
808810
}
809811

810812
llvm::AttributeList IRGenModule::constructInitialAttributes() {

lib/SILOptimizer/LoopTransforms/COWArrayOpt.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2292,9 +2292,16 @@ class SwiftArrayOptPass : public SILFunctionTransform {
22922292
if (!ShouldSpecializeArrayProps)
22932293
return;
22942294

2295+
auto *Fn = getFunction();
2296+
2297+
// Don't hoist array property calls at Osize.
2298+
auto OptMode = Fn->getModule().getOptions().Optimization;
2299+
if (OptMode == SILOptions::SILOptMode::OptimizeForSize)
2300+
return;
2301+
22952302
DominanceAnalysis *DA = PM->getAnalysis<DominanceAnalysis>();
22962303
SILLoopAnalysis *LA = PM->getAnalysis<SILLoopAnalysis>();
2297-
SILLoopInfo *LI = LA->get(getFunction());
2304+
SILLoopInfo *LI = LA->get(Fn);
22982305

22992306
bool HasChanged = false;
23002307

lib/SILOptimizer/Transforms/FunctionSignatureOpts.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,11 @@ class FunctionSignatureOpts : public SILFunctionTransform {
11401140
void run() override {
11411141
auto *F = getFunction();
11421142

1143+
// Don't run function signature optimizations at -Os.
1144+
if (F->getModule().getOptions().Optimization ==
1145+
SILOptions::SILOptMode::OptimizeForSize)
1146+
return;
1147+
11431148
// Don't optimize callees that should not be optimized.
11441149
if (!F->shouldOptimize())
11451150
return;

lib/SILOptimizer/Transforms/PerformanceInliner.cpp

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ class SILPerformanceInliner {
108108
DefaultApplyLength = 10
109109
};
110110

111+
SILOptions::SILOptMode OptMode;
112+
111113
#ifndef NDEBUG
112114
SILFunction *LastPrintedCaller = nullptr;
113115
void dumpCaller(SILFunction *Caller) {
@@ -146,8 +148,10 @@ class SILPerformanceInliner {
146148

147149
public:
148150
SILPerformanceInliner(InlineSelection WhatToInline, DominanceAnalysis *DA,
149-
SILLoopAnalysis *LA, SideEffectAnalysis *SEA)
150-
: WhatToInline(WhatToInline), DA(DA), LA(LA), SEA(SEA), CBI(DA) {}
151+
SILLoopAnalysis *LA, SideEffectAnalysis *SEA,
152+
SILOptions::SILOptMode OptMode)
153+
: WhatToInline(WhatToInline), DA(DA), LA(LA), SEA(SEA), CBI(DA),
154+
OptMode(OptMode) {}
151155

152156
bool inlineCallsIntoFunction(SILFunction *F);
153157
};
@@ -170,6 +174,22 @@ bool SILPerformanceInliner::isProfitableToInline(FullApplySite AI,
170174

171175
assert(EnableSILInliningOfGenerics || !IsGeneric);
172176

177+
// Start with a base benefit.
178+
int BaseBenefit = RemovedCallBenefit;
179+
180+
// Osize heuristic.
181+
if (OptMode == SILOptions::SILOptMode::OptimizeForSize) {
182+
// Don't inline into thunks.
183+
if (AI.getFunction()->isThunk())
184+
return false;
185+
186+
// Don't inline methods.
187+
if (Callee->getRepresentation() == SILFunctionTypeRepresentation::Method)
188+
return false;
189+
190+
BaseBenefit = BaseBenefit / 2;
191+
}
192+
173193
// Bail out if this generic call can be optimized by means of
174194
// the generic specialization, because we prefer generic specialization
175195
// to inlining of generics.
@@ -190,21 +210,16 @@ bool SILPerformanceInliner::isProfitableToInline(FullApplySite AI,
190210
int CalleeCost = 0;
191211
int Benefit = 0;
192212

193-
// Start with a base benefit.
194-
int BaseBenefit = RemovedCallBenefit;
195-
196213
SubstitutionMap CalleeSubstMap;
197214
if (IsGeneric) {
198215
CalleeSubstMap = Callee->getLoweredFunctionType()
199216
->getGenericSignature()
200217
->getSubstitutionMap(AI.getSubstitutions());
201218
}
202219

203-
const SILOptions &Opts = Callee->getModule().getOptions();
204-
205220
// For some reason -Ounchecked can accept a higher base benefit without
206221
// increasing the code size too much.
207-
if (Opts.Optimization == SILOptions::SILOptMode::OptimizeUnchecked)
222+
if (OptMode == SILOptions::SILOptMode::OptimizeUnchecked)
208223
BaseBenefit *= 2;
209224

210225
CallerWeight.updateBenefit(Benefit, BaseBenefit);
@@ -696,7 +711,9 @@ class SILPerformanceInlinerPass : public SILFunctionTransform {
696711
return;
697712
}
698713

699-
SILPerformanceInliner Inliner(WhatToInline, DA, LA, SEA);
714+
auto OptMode = getFunction()->getModule().getOptions().Optimization;
715+
716+
SILPerformanceInliner Inliner(WhatToInline, DA, LA, SEA, OptMode);
700717

701718
assert(getFunction()->isDefinition() &&
702719
"Expected only functions with bodies!");

lib/SILOptimizer/Transforms/SpeculativeDevirtualizer.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,13 @@ namespace {
556556
~SpeculativeDevirtualization() override {}
557557

558558
void run() override {
559+
560+
auto &CurFn = *getFunction();
561+
// Don't perform speculative devirtualization at -Os.
562+
if (CurFn.getModule().getOptions().Optimization ==
563+
SILOptions::SILOptMode::OptimizeForSize)
564+
return;
565+
559566
ClassHierarchyAnalysis *CHA = PM->getAnalysis<ClassHierarchyAnalysis>();
560567

561568
bool Changed = false;

test/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ set(profdata_merge_worker
135135
"${CMAKE_CURRENT_SOURCE_DIR}/../utils/profdata_merge/main.py")
136136

137137
set(TEST_MODES
138-
optimize_none optimize optimize_unchecked
138+
optimize_none optimize optimize_unchecked optimize_size
139139
only_executable only_non_executable
140140
)
141141
set(TEST_SUBSETS

test/IRGen/generic_classes.sil

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil %s -emit-ir | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-runtime
2+
// RUN: %target-swift-frontend -Osize -assume-parsing-unqualified-ownership-sil %s -emit-ir | %FileCheck %s --check-prefix=OSIZE
23

34
// REQUIRES: CPU=x86_64
45

@@ -365,3 +366,6 @@ entry(%c : $RootGeneric<Int32>):
365366
// CHECK: call %swift.type* @swift_initClassMetadata_UniversalStrategy(%swift.type* [[METADATA]], i64 1, i8*** [[FIELDS_ADDR]], i64* [[OFFSETS]])
366367
// CHECK: ret %swift.type* [[METADATA]]
367368
// CHECK: }
369+
370+
// OSIZE: define hidden %swift.type* @_T015generic_classes11RootGenericCMa(%swift.type*) [[ATTRS:#[0-9]+]] {
371+
// OSIZE: [[ATTRS]] = {{{.*}}noinline

test/IRGen/optimize_for_size.sil

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %target-swift-frontend -Osize -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s
2+
// RUN: %target-swift-frontend -O -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s --check-prefix=O
3+
sil_stage canonical
4+
5+
import Builtin
6+
7+
// CHECK-LABEL: define{{.*}} swiftcc void @optimize_for_size_attribute({{.*}}) #0 {
8+
// O-LABEL: define{{.*}} swiftcc void @optimize_for_size_attribute({{.*}}) #0 {
9+
sil @optimize_for_size_attribute : $@convention(thin) (Builtin.Int32) -> () {
10+
bb0(%0 : $Builtin.Int32):
11+
%1 = tuple ()
12+
return %1 : $()
13+
}
14+
15+
// O-NOT: attributes #0 = {{{.*}}optsize
16+
// CHECK: attributes #0 = {{{.*}}optsize

test/IRGen/same_type_constraints.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir -primary-file %s -disable-objc-attr-requires-foundation-module | %FileCheck %s
2+
// RUN: %target-swift-frontend -Osize -assume-parsing-unqualified-ownership-sil -emit-ir -primary-file %s -disable-objc-attr-requires-foundation-module | %FileCheck %s --check-prefix=OSIZE
23

34
// <rdar://problem/21665983> IRGen crash with protocol extension involving same-type constraint to X<T>
45
public struct DefaultFoo<T> {
@@ -58,3 +59,6 @@ where Self : CodingType,
5859
Self.ValueType.Coder == Self {
5960
print(Self.ValueType.self)
6061
}
62+
63+
// OSIZE: define internal i8** @_T021same_type_constraints12GenericKlazzCyxq_GAA1EAA4DataQy_RszAaER_r0_lAfA0F4TypePWT(%swift.type* %"GenericKlazz<T, R>.Data", %swift.type* nocapture readonly %"GenericKlazz<T, R>", i8** nocapture readnone %"GenericKlazz<T, R>.E") [[ATTRS:#[0-9]+]] {
64+
// OSIZE: [[ATTRS]] = {{{.*}}noinline

test/IRGen/typemetadata.sil

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s
2+
// RUN: %target-swift-frontend -Osize -assume-parsing-unqualified-ownership-sil -emit-ir %s | %FileCheck %s --check-prefix=OSIZE
23

34
// REQUIRES: CPU=x86_64
45
// XFAIL: linux
@@ -47,3 +48,7 @@ bb0:
4748
// CHECK: [[RES:%.*]] = phi
4849
// CHECK-NEXT: ret %swift.type* [[RES]]
4950

51+
52+
// OSIZE: define hidden %swift.type* @_T012typemetadata1CCMa() [[ATTR:#[0-9]+]] {
53+
// OSIZE: [[ATTR]] = {{{.*}}noinline
54+

test/Interpreter/SDK/protocol_lookup_foreign.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
// rdar://20990451 is tracking the fix for compiling this test optimized.
88
// XFAIL: swift_test_mode_optimize
9+
// XFAIL: swift_test_mode_optimize_size
910
// XFAIL: swift_test_mode_optimize_unchecked
1011

1112
import Foundation

test/SILOptimizer/devirt_speculate.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-swift-frontend %s -parse-as-library -O -emit-sil | %FileCheck %s
2+
// RUN: %target-swift-frontend %s -parse-as-library -Osize -emit-sil | %FileCheck %s --check-prefix=OSIZE
23
//
34
// Test speculative devirtualization.
45

@@ -40,6 +41,10 @@ class Sub7 : Base {
4041
// CHECK-NOT: checked_cast_br
4142
// CHECK: %[[CM:[0-9]+]] = class_method %0 : $Base, #Base.foo!1 : (Base) -> () -> (), $@convention(method) (@guaranteed Base) -> ()
4243
// CHECK: apply %[[CM]](%0) : $@convention(method) (@guaranteed Base) -> ()
44+
45+
// OSIZE: @_T016devirt_speculate28testMaxNumSpeculativeTargetsyAA4BaseCF
46+
// OSIZE-NOT: checked_cast_br [exact] %0 : $Base to $Base
47+
// OSIZE-NOT: checked_cast_br [exact] %0 : $Base to $Sub
4348
public func testMaxNumSpeculativeTargets(_ b: Base) {
4449
b.foo()
4550
}

test/lit.cfg

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,14 @@ elif swift_test_mode == 'optimize':
458458
# optimize mode for a cpu.
459459
config.available_features.add("swift_test_mode_optimize_" + run_cpu)
460460
swift_execution_tests_extra_flags = '-O'
461+
elif swift_test_mode == 'optimize_size':
462+
config.available_features.add("executable_test")
463+
config.limit_to_features.add("executable_test")
464+
config.available_features.add("swift_test_mode_optimize_size")
465+
# Add the cpu as a feature so we can selectively disable tests in an
466+
# optimize mode for a cpu.
467+
config.available_features.add("swift_test_mode_optimize_" + run_cpu)
468+
swift_execution_tests_extra_flags = '-Osize'
461469
elif swift_test_mode == 'optimize_unchecked':
462470
config.available_features.add("executable_test")
463471
config.limit_to_features.add("executable_test")

0 commit comments

Comments
 (0)