Skip to content

Commit 82734b6

Browse files
committed
Swift Optimizer: simplification for apply, try_apply, begin_apply and partial_apply
* move the apply of partial_apply transformation from simplify-apply to simplify-partial_apply * delete dead partial_apply instructions * devirtualize apply, try_apply and begin_apply
1 parent 6488da9 commit 82734b6

21 files changed

+368
-159
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ swift_compiler_sources(Optimizer
1717
SimplifyDestructure.swift
1818
SimplifyGlobalValue.swift
1919
SimplifyLoad.swift
20+
SimplifyPartialApply.swift
2021
SimplifyStrongRetainRelease.swift
2122
SimplifyStructExtract.swift
2223
SimplifyTupleExtract.swift

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyApply.swift

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,48 +14,18 @@ import SIL
1414

1515
extension ApplyInst : OnoneSimplifyable {
1616
func simplify(_ context: SimplifyContext) {
17-
tryReplaceTrivialApplyOfPartialApply(context)
17+
_ = context.tryDevirtualize(apply: self, isMandatory: false)
1818
}
1919
}
2020

21-
private extension ApplyInst {
22-
func tryReplaceTrivialApplyOfPartialApply(_ context: SimplifyContext) {
23-
guard let pa = callee as? PartialApplyInst else {
24-
return
25-
}
26-
27-
if pa.referencedFunction == nil {
28-
return
29-
}
30-
31-
// Currently we don't handle generic closures. For Onone this is good enough.
32-
// TODO: handle it once we replace the SILCombine simplification with this.
33-
if !allArgumentsAreTrivial(arguments) {
34-
return
35-
}
36-
37-
if !allArgumentsAreTrivial(pa.arguments) {
38-
return
39-
}
40-
41-
if !substitutionMap.isEmpty {
42-
return
43-
}
44-
45-
let allArgs = Array<Value>(arguments) + Array<Value>(pa.arguments)
46-
let builder = Builder(before: self, context)
47-
let newApply = builder.createApply(function: pa.callee, pa.substitutionMap, arguments: allArgs,
48-
isNonThrowing: isNonThrowing, isNonAsync: isNonAsync,
49-
specializationInfo: specializationInfo)
50-
uses.replaceAll(with: newApply, context)
51-
context.erase(instruction: self)
52-
53-
if context.tryDeleteDeadClosure(closure: pa) {
54-
context.notifyInvalidatedStackNesting()
55-
}
21+
extension TryApplyInst : OnoneSimplifyable {
22+
func simplify(_ context: SimplifyContext) {
23+
_ = context.tryDevirtualize(apply: self, isMandatory: false)
5624
}
5725
}
5826

59-
private func allArgumentsAreTrivial(_ args: LazyMapSequence<OperandArray, Value>) -> Bool {
60-
return !args.contains { !$0.hasTrivialType }
27+
extension BeginApplyInst : OnoneSimplifyable {
28+
func simplify(_ context: SimplifyContext) {
29+
_ = context.tryDevirtualize(apply: self, isMandatory: false)
30+
}
6131
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===--- SimplifyPartialApply.swift ---------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SIL
14+
15+
extension PartialApplyInst : OnoneSimplifyable {
16+
func simplify(_ context: SimplifyContext) {
17+
let optimizedApplyOfPartialApply = context.tryOptimizeApplyOfPartialApply(closure: self)
18+
if optimizedApplyOfPartialApply {
19+
context.notifyInvalidatedStackNesting()
20+
}
21+
22+
if context.preserveDebugInfo && uses.contains(where: { $0.instruction is DebugValueInst }) {
23+
return
24+
}
25+
26+
// Try to delete the partial_apply.
27+
// In case it became dead because of tryOptimizeApplyOfPartialApply, we don't
28+
// need to copy all arguments again (to extend their lifetimes), because it
29+
// was already done in tryOptimizeApplyOfPartialApply.
30+
if context.tryDeleteDeadClosure(closure: self, needKeepArgsAlive: !optimizedApplyOfPartialApply) {
31+
context.notifyInvalidatedStackNesting()
32+
}
33+
}
34+
}

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,44 @@ extension MutatingContext {
7171
erase(instruction: inst)
7272
}
7373

74+
func tryOptimizeApplyOfPartialApply(closure: PartialApplyInst) -> Bool {
75+
if _bridged.tryOptimizeApplyOfPartialApply(closure.bridged) {
76+
notifyInstructionsChanged()
77+
notifyCallsChanged()
78+
79+
for use in closure.callee.uses {
80+
if use.instruction is FullApplySite {
81+
notifyInstructionChanged(use.instruction)
82+
}
83+
}
84+
return true
85+
}
86+
return false
87+
}
88+
89+
func tryDeleteDeadClosure(closure: SingleValueInstruction, needKeepArgsAlive: Bool = true) -> Bool {
90+
if _bridged.tryDeleteDeadClosure(closure.bridged, needKeepArgsAlive) {
91+
notifyInstructionsChanged()
92+
return true
93+
}
94+
return false
95+
}
96+
97+
func tryDevirtualize(apply: FullApplySite, isMandatory: Bool) -> ApplySite? {
98+
let result = _bridged.tryDevirtualizeApply(apply.bridged, isMandatory)
99+
if let newApply = result.newApply.instruction {
100+
erase(instruction: apply)
101+
notifyInstructionsChanged()
102+
notifyCallsChanged()
103+
if result.cfgChanged {
104+
notifyBranchesChanged()
105+
}
106+
notifyInstructionChanged(newApply)
107+
return newApply as! FullApplySite
108+
}
109+
return nil
110+
}
111+
74112
/// Copies all instructions of a static init value of a global to the insertion point of `builder`.
75113
func copyStaticInitializer(fromInitValue: Value, to builder: Builder) -> Value? {
76114
let range = _bridged.copyStaticInitializer(fromInitValue.bridged, builder.bridged)
@@ -98,10 +136,6 @@ extension MutatingContext {
98136
}
99137
}
100138

101-
func tryDeleteDeadClosure(closure: SingleValueInstruction) -> Bool {
102-
_bridged.tryDeleteDeadClosure(closure.bridged)
103-
}
104-
105139
func getContextSubstitutionMap(for type: Type) -> SubstitutionMap {
106140
SubstitutionMap(_bridged.getContextSubstitutionMap(type.bridged))
107141
}

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,17 @@ struct BridgedPassContext {
202202
block.getBlock()->eraseFromParent();
203203
}
204204

205-
bool tryDeleteDeadClosure(BridgedInstruction closure) const;
205+
bool tryOptimizeApplyOfPartialApply(BridgedInstruction closure) const;
206+
207+
bool tryDeleteDeadClosure(BridgedInstruction closure, bool needKeepArgsAlive) const;
208+
209+
struct DevirtResult {
210+
OptionalBridgedInstruction newApply;
211+
bool cfgChanged;
212+
};
213+
214+
SWIFT_IMPORT_UNSAFE
215+
DevirtResult tryDevirtualizeApply(BridgedInstruction apply, bool isMandatory) const;
206216

207217
SWIFT_IMPORT_UNSAFE
208218
OptionalBridgedValue constantFoldBuiltin(BridgedInstruction builtin) const;

include/swift/SILOptimizer/Utils/Devirtualize.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ SubstitutionMap getWitnessMethodSubstitutions(SILModule &Module, ApplySite AI,
6969
/// Return the new apply and true if the CFG was also modified.
7070
std::pair<ApplySite, bool>
7171
tryDevirtualizeApply(ApplySite AI, ClassHierarchyAnalysis *CHA,
72-
OptRemark::Emitter *ORE = nullptr);
72+
OptRemark::Emitter *ORE = nullptr,
73+
bool isMandatory = false);
7374
bool canDevirtualizeApply(FullApplySite AI, ClassHierarchyAnalysis *CHA);
7475
bool canDevirtualizeClassMethod(FullApplySite AI, ClassDecl *CD,
7576
OptRemark::Emitter *ORE = nullptr,
@@ -108,7 +109,9 @@ tryDevirtualizeClassMethod(FullApplySite AI, SILValue ClassInstance,
108109
/// the original apply site.
109110
///
110111
/// Return the new apply and true if the CFG was also modified.
111-
std::pair<ApplySite, bool> tryDevirtualizeWitnessMethod(ApplySite AI, OptRemark::Emitter *ORE);
112+
std::pair<ApplySite, bool> tryDevirtualizeWitnessMethod(ApplySite AI,
113+
OptRemark::Emitter *ORE,
114+
bool isMandatory);
112115

113116
/// Delete a successfully-devirtualized apply site. This must always be
114117
/// called after devirtualizing an apply; not only is it not semantically

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
3131
#include "swift/SILOptimizer/Utils/ConstantFolding.h"
3232
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
33+
#include "swift/SILOptimizer/Utils/Devirtualize.h"
3334
#include "swift/SILOptimizer/Utils/OptimizerStatsUtils.h"
3435
#include "swift/SILOptimizer/Utils/StackNesting.h"
3536
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
@@ -1427,8 +1428,25 @@ std::string BridgedPassContext::getModuleDescription() const {
14271428
return str;
14281429
}
14291430

1430-
bool BridgedPassContext::tryDeleteDeadClosure(BridgedInstruction closure) const {
1431-
return ::tryDeleteDeadClosure(closure.getAs<SingleValueInstruction>(), InstModCallbacks());
1431+
bool BridgedPassContext::tryOptimizeApplyOfPartialApply(BridgedInstruction closure) const {
1432+
auto *pa = closure.getAs<PartialApplyInst>();
1433+
SILBuilder builder(pa);
1434+
return ::tryOptimizeApplyOfPartialApply(pa, builder.getBuilderContext(), InstModCallbacks());
1435+
}
1436+
1437+
bool BridgedPassContext::tryDeleteDeadClosure(BridgedInstruction closure, bool needKeepArgsAlive) const {
1438+
return ::tryDeleteDeadClosure(closure.getAs<SingleValueInstruction>(), InstModCallbacks(), needKeepArgsAlive);
1439+
}
1440+
1441+
BridgedPassContext::DevirtResult BridgedPassContext::tryDevirtualizeApply(BridgedInstruction apply,
1442+
bool isMandatory) const {
1443+
auto cha = invocation->getPassManager()->getAnalysis<ClassHierarchyAnalysis>();
1444+
auto result = ::tryDevirtualizeApply(ApplySite(apply.getInst()), cha, nullptr, isMandatory);
1445+
if (result.first) {
1446+
OptionalBridgedInstruction newApply(result.first.getInstruction()->asSILNode());
1447+
return {newApply, result.second};
1448+
}
1449+
return {{nullptr}, false};
14321450
}
14331451

14341452
OptionalBridgedValue BridgedPassContext::constantFoldBuiltin(BridgedInstruction builtin) const {

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,7 @@ static bool isNonGenericThunkOfGenericExternalFunction(SILFunction *thunk) {
11401140
return false;
11411141
}
11421142

1143-
static bool canDevirtualizeWitnessMethod(ApplySite applySite) {
1143+
static bool canDevirtualizeWitnessMethod(ApplySite applySite, bool isMandatory) {
11441144
SILFunction *f;
11451145
SILWitnessTable *wt;
11461146

@@ -1183,7 +1183,7 @@ static bool canDevirtualizeWitnessMethod(ApplySite applySite) {
11831183
// ```
11841184
// In the defining module, the generic conformance can be specialized (which is not
11851185
// possible in the client module, because it's not inlinable).
1186-
if (isNonGenericThunkOfGenericExternalFunction(f)) {
1186+
if (!isMandatory && isNonGenericThunkOfGenericExternalFunction(f)) {
11871187
return false;
11881188
}
11891189

@@ -1237,8 +1237,9 @@ static bool canDevirtualizeWitnessMethod(ApplySite applySite) {
12371237
/// of a function_ref, returning the new apply.
12381238
std::pair<ApplySite, bool>
12391239
swift::tryDevirtualizeWitnessMethod(ApplySite applySite,
1240-
OptRemark::Emitter *ore) {
1241-
if (!canDevirtualizeWitnessMethod(applySite))
1240+
OptRemark::Emitter *ore,
1241+
bool isMandatory) {
1242+
if (!canDevirtualizeWitnessMethod(applySite, isMandatory))
12421243
return {ApplySite(), false};
12431244

12441245
SILFunction *f;
@@ -1262,7 +1263,7 @@ swift::tryDevirtualizeWitnessMethod(ApplySite applySite,
12621263
/// Return the new apply and true if the CFG was also modified.
12631264
std::pair<ApplySite, bool>
12641265
swift::tryDevirtualizeApply(ApplySite applySite, ClassHierarchyAnalysis *cha,
1265-
OptRemark::Emitter *ore) {
1266+
OptRemark::Emitter *ore, bool isMandatory) {
12661267
LLVM_DEBUG(llvm::dbgs() << " Trying to devirtualize: "
12671268
<< *applySite.getInstruction());
12681269

@@ -1272,7 +1273,7 @@ swift::tryDevirtualizeApply(ApplySite applySite, ClassHierarchyAnalysis *cha,
12721273
// %9 = apply %8<Self = CodeUnit?>(%6#1) : ...
12731274
//
12741275
if (isa<WitnessMethodInst>(applySite.getCallee()))
1275-
return tryDevirtualizeWitnessMethod(applySite, ore);
1276+
return tryDevirtualizeWitnessMethod(applySite, ore, isMandatory);
12761277

12771278
// TODO: check if we can also de-virtualize partial applies of class methods.
12781279
FullApplySite fas = FullApplySite::isa(applySite.getInstruction());
@@ -1344,7 +1345,7 @@ bool swift::canDevirtualizeApply(FullApplySite applySite,
13441345
// %9 = apply %8<Self = CodeUnit?>(%6#1) : ...
13451346
//
13461347
if (isa<WitnessMethodInst>(applySite.getCallee()))
1347-
return canDevirtualizeWitnessMethod(applySite);
1348+
return canDevirtualizeWitnessMethod(applySite, /*isMandatory*/ false);
13481349

13491350
/// Optimize a class_method and alloc_ref pair into a direct function
13501351
/// reference:

test/AutoDiff/SILOptimizer/differentiation_subset_parameters_thunk.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -emit-sil %s | %FileCheck %s
1+
// RUN: %target-swift-frontend -emit-sil -Xllvm -sil-disable-pass=OnoneSimplification %s | %FileCheck %s
22

33
import _Differentiation
44

test/ClangImporter/serialization-sil.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// RUN: %empty-directory(%t)
2-
// RUN: %target-swift-frontend -enable-copy-propagation=requested-passes-only -enable-lexical-lifetimes=false -emit-module-path %t/Test.swiftmodule -emit-sil -o /dev/null -module-name Test %s -sdk "" -import-objc-header %S/Inputs/serialization-sil.h
2+
// RUN: %target-swift-frontend -enable-copy-propagation=requested-passes-only -enable-lexical-lifetimes=false -Xllvm -sil-disable-pass=Simplification -emit-module-path %t/Test.swiftmodule -emit-sil -o /dev/null -module-name Test %s -sdk "" -import-objc-header %S/Inputs/serialization-sil.h
33
// RUN: %target-sil-func-extractor %t/Test.swiftmodule -sil-print-debuginfo -func='$s4Test16testPartialApplyyySoAA_pF' -o - | %FileCheck %s
44

55
// REQUIRES: objc_interop

test/IRGen/async/partial_apply.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ entry(%i : $*ResilientInt, %c : $SwiftClass):
531531
// Make sure that we use the heap header size (16) for the initial offset.
532532
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swift{{(tail)?}}cc void @test_initial_offset(
533533

534-
sil @test_initial_offset : $@async @convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> () {
534+
sil @test_initial_offset : $@async @convention(thin) (@in ResilientInt, @guaranteed SwiftClass) -> () {
535535
bb0(%x : $*ResilientInt, %y : $SwiftClass):
536536
%f = function_ref @closure : $@async @convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> ()
537537
%p = partial_apply [callee_guaranteed] %f(%x, %y) : $@async @convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> ()

test/IRGen/error_self_conformance.sil

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ sil @partial_apply_test : $@convention(thin) (@in Error) -> () {
2121
entry(%0 : $*Error):
2222
%take = function_ref @take_any_error : $@convention(thin) <T: Error> (@in T) -> ()
2323
%fn = partial_apply %take<Error>(%0) : $@convention(thin) <T: Error> (@in T) -> ()
24+
release_value %fn : $@callee_owned () ->()
2425
%ret = tuple ()
2526
return %ret : $()
2627
}

test/IRGen/partial_apply.sil

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// RUN: %empty-directory(%t)
22
// 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
3-
// RUN: %target-swift-frontend -I %t -emit-ir %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize
3+
// RUN: %target-swift-frontend -Xllvm -sil-disable-pass=OnoneSimplification -I %t -emit-ir %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize
44

5-
// REQUIRES: CPU=x86_64
5+
// REQUIRES: PTRSIZE=64
66

77
import Builtin
88
import Swift
@@ -743,7 +743,7 @@ sil public_external @closure : $@convention(thin) (@in_guaranteed ResilientInt,
743743
// CHECK: = xor i64 [[ALIGNMASK]], -1
744744
// CHECK: = add i64 16, [[ALIGNMASK]]
745745

746-
sil @test_initial_offset : $@convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> () {
746+
sil @test_initial_offset : $@convention(thin) (@in ResilientInt, @guaranteed SwiftClass) -> () {
747747
bb0(%x : $*ResilientInt, %y : $SwiftClass):
748748
%f = function_ref @closure : $@convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> ()
749749
%p = partial_apply [callee_guaranteed] %f(%x, %y) : $@convention(thin) (@in_guaranteed ResilientInt, @guaranteed SwiftClass) -> ()

test/IRGen/partial_apply_run_generic_method1.sil

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule
33
// RUN: %target-codesign %t/%target-library-name(PrintShims)
4-
// RUN: %target-build-swift -g -parse-sil %s -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL
5-
// RUN: %target-build-swift -g -parse-sil %s -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t)
4+
// RUN: %target-build-swift -g -parse-sil %s -Xllvm -sil-disable-pass=Simplification -emit-ir -I %t -L %t -lPrintShim | %FileCheck %s --check-prefix=CHECK-LL
5+
// RUN: %target-build-swift -g -parse-sil %s -Xllvm -sil-disable-pass=Simplification -module-name main -o %t/main -I %t -L %t -lPrintShims %target-rpath(%t)
66
// RUN: %target-codesign %t/main
77
// RUN: %target-run %t/main %t/%target-library-name(PrintShims) | %FileCheck %s
88

test/IRGen/protocol_resilience.sil

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %empty-directory(%t)
22
// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_protocol.swiftmodule -module-name=resilient_protocol %S/../Inputs/resilient_protocol.swift
3-
// RUN: %target-swift-frontend -I %t -emit-ir -enable-library-evolution %s | %FileCheck %s -DINT=i%target-ptrsize
4-
// RUN: %target-swift-frontend -I %t -emit-ir -enable-library-evolution -O %s
3+
// RUN: %target-swift-frontend -I %t -emit-ir -Xllvm -sil-disable-pass=Simplification -enable-library-evolution %s | %FileCheck %s -DINT=i%target-ptrsize
4+
// RUN: %target-swift-frontend -I %t -emit-ir -Xllvm -sil-disable-pass=Simplification -enable-library-evolution -O %s
55

66
sil_stage canonical
77

test/IRGen/simple_partial_apply_or_not.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %target-swift-emit-ir -module-name test %s | %FileCheck %s
2-
// RUN: %target-run-simple-swift %s | %FileCheck %s --check-prefix=CHECK-EXEC
1+
// RUN: %target-swift-emit-ir -Xllvm -sil-disable-pass=Simplification -module-name test %s | %FileCheck %s
2+
// RUN: %target-run-simple-swift -Xllvm -sil-disable-pass=Simplification %s | %FileCheck %s --check-prefix=CHECK-EXEC
33

44
// REQUIRES: executable_test
55

0 commit comments

Comments
 (0)