Skip to content

Commit 67ebbee

Browse files
authored
Merge pull request #69955 from eeckstein/deinit-devirtualizer
Optimizer: de-virtualize deinits of non-copyable types
2 parents 587a527 + 17f246e commit 67ebbee

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1228
-306
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ swift_compiler_sources(Optimizer
1313
ComputeEscapeEffects.swift
1414
ComputeSideEffects.swift
1515
DeadStoreElimination.swift
16+
DeinitDevirtualizer.swift
1617
InitializeStaticGlobals.swift
1718
LetPropertyLowering.swift
1819
ObjectOutliner.swift
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===--- DeinitDevirtualizer.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+
/// Devirtualizes destroys of non-copyable values.
16+
///
17+
let deinitDevirtualizer = FunctionPass(name: "deinit-devirtualizer") {
18+
(function: Function, context: FunctionPassContext) in
19+
20+
for inst in function.instructions {
21+
switch inst {
22+
case let destroyValue as DestroyValueInst:
23+
_ = devirtualizeDeinits(of: destroyValue, context)
24+
case let destroyAddr as DestroyAddrInst:
25+
_ = devirtualizeDeinits(of: destroyAddr, context)
26+
default:
27+
break
28+
}
29+
}
30+
}

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,13 @@ private func getSequenceOfElementStores(firstStore: StoreInst) -> ([StoreInst],
181181
return nil
182182
}
183183
let structAddr = elementAddr.struct
184-
let numElements = structAddr.type.getNominalFields(in: firstStore.parentFunction).count
184+
if structAddr.type.isMoveOnly {
185+
return nil
186+
}
187+
guard let fields = structAddr.type.getNominalFields(in: firstStore.parentFunction) else {
188+
return nil
189+
}
190+
let numElements = fields.count
185191
var elementStores = Array<StoreInst?>(repeating: nil, count: numElements)
186192
var numStoresFound = 0
187193

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjCBridgingOptimization.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,9 @@ private func optimizeNonOptionalBridging(_ apply: ApplyInst,
185185
// empty value.
186186
// Create the needed blocks of the `switch_enum` CFG diamond.
187187
let origBlock = bridgeToSwiftCall.parentBlock
188-
let someBlock = context.splitBlock(at: bridgeToSwiftCall)
189-
let noneBlock = context.splitBlock(at: bridgeToSwiftCall)
190-
let continueBlock = context.splitBlock(at: bridgeToSwiftCall)
188+
let someBlock = context.splitBlock(before: bridgeToSwiftCall)
189+
let noneBlock = context.splitBlock(before: bridgeToSwiftCall)
190+
let continueBlock = context.splitBlock(before: bridgeToSwiftCall)
191191

192192

193193
let builder = Builder(atEndOf: origBlock, location: bridgeToSwiftCall.location, context)

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjectOutliner.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ private extension AllocRefInstBase {
410410

411411
var numClassFields: Int {
412412
assert(type.isClass)
413-
return type.getNominalFields(in: parentFunction).count
413+
return type.getNominalFields(in: parentFunction)!.count
414414
}
415415

416416
var numStoresPerTailElement: Int {
@@ -475,7 +475,7 @@ private func replace(findStringCall: ApplyInst,
475475
with cachedFindStringFunc: Function,
476476
_ context: FunctionPassContext) {
477477
let cacheType = cachedFindStringFunc.argumentTypes[2].objectType
478-
let wordTy = cacheType.getNominalFields(in: findStringCall.parentFunction)[0]
478+
let wordTy = cacheType.getNominalFields(in: findStringCall.parentFunction)![0]
479479

480480
let name = context.mangleOutlinedVariable(from: findStringCall.parentFunction)
481481

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ReleaseDevirtualizer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ private extension Type {
167167
return true
168168
}
169169
if isStruct {
170-
return getNominalFields(in: function).containsSingleReference(in: function)
170+
return getNominalFields(in: function)?.containsSingleReference(in: function) ?? false
171171
} else if isTuple {
172172
return tupleElements.containsSingleReference(in: function)
173173
} else {

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyBeginBorrow.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ private extension ForwardingInstruction {
192192
return false
193193
}
194194
let structFields = si.struct.type.getNominalFields(in: parentFunction)
195-
return structFields.hasSingleNonTrivialElement(at: si.fieldIndex, in: parentFunction)
195+
return structFields?.hasSingleNonTrivialElement(at: si.fieldIndex, in: parentFunction) ?? false
196196
case let ti as TupleExtractInst:
197197
return ti.tuple.type.tupleElements.hasSingleNonTrivialElement(at: ti.fieldIndex, in: parentFunction)
198198
default:

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,10 @@ extension LoadInst : OnoneSimplifyable, SILCombineSimplifyable {
135135
case let sea as StructElementAddrInst:
136136
let structType = sea.struct.type
137137
if structType.nominal.name == "_SwiftArrayBodyStorage" {
138-
switch structType.getNominalFields(in: parentFunction).getNameOfField(withIndex: sea.fieldIndex) {
138+
guard let fields = structType.getNominalFields(in: parentFunction) else {
139+
return false
140+
}
141+
switch fields.getNameOfField(withIndex: sea.fieldIndex) {
139142
case "count":
140143
break
141144
case "_capacityAndFlags":
@@ -153,7 +156,10 @@ extension LoadInst : OnoneSimplifyable, SILCombineSimplifyable {
153156
case "__RawDictionaryStorage",
154157
"__RawSetStorage":
155158
// For Dictionary and Set we support "count" and "capacity".
156-
switch classType.getNominalFields(in: parentFunction).getNameOfField(withIndex: rea.fieldIndex) {
159+
guard let fields = classType.getNominalFields(in: parentFunction) else {
160+
return false
161+
}
162+
switch fields.getNameOfField(withIndex: rea.fieldIndex) {
157163
case "_count", "_capacity":
158164
break
159165
default:

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,16 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ work
9090
_ = context.specializeClassMethodInst(classMethod)
9191
}
9292

93+
// We need to de-virtualize deinits of non-copyable types to be able to specialize the deinitializers.
94+
case let destroyValue as DestroyValueInst:
95+
if !devirtualizeDeinits(of: destroyValue, simplifyCtxt) {
96+
context.diagnosticEngine.diagnose(destroyValue.location.sourceLoc, .deinit_not_visible)
97+
}
98+
case let destroyAddr as DestroyAddrInst:
99+
if !devirtualizeDeinits(of: destroyAddr, simplifyCtxt) {
100+
context.diagnosticEngine.diagnose(destroyAddr.location.sourceLoc, .deinit_not_visible)
101+
}
102+
93103
default:
94104
break
95105
}

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ protocol Context {
2121
extension Context {
2222
var options: Options { Options(_bridged: _bridged) }
2323

24+
var diagnosticEngine: DiagnosticEngine {
25+
return DiagnosticEngine(bridged: _bridged.getDiagnosticEngine())
26+
}
27+
2428
// The calleeAnalysis is not specific to a function and therefore can be provided in
2529
// all contexts.
2630
var calleeAnalysis: CalleeAnalysis {
@@ -38,6 +42,10 @@ extension Context {
3842
default: fatalError("unhandled SILStage case")
3943
}
4044
}
45+
46+
func lookupDeinit(ofNominal: NominalTypeDecl) -> Function? {
47+
_bridged.lookUpNominalDeinitFunction(ofNominal.bridged).function
48+
}
4149
}
4250

4351
/// A context which allows mutation of a function's SIL.
@@ -55,9 +63,24 @@ extension MutatingContext {
5563
///
5664
/// `inst` and all subsequent instructions are moved to the new block, while all
5765
/// instructions _before_ `inst` remain in the original block.
58-
func splitBlock(at inst: Instruction) -> BasicBlock {
66+
func splitBlock(before inst: Instruction) -> BasicBlock {
67+
notifyBranchesChanged()
68+
return _bridged.splitBlockBefore(inst.bridged).block
69+
}
70+
71+
/// Splits the basic block, which contains `inst`, after `inst` and returns the
72+
/// new block.
73+
///
74+
/// All subsequent instructions after `inst` are moved to the new block, while `inst` and all
75+
/// instructions _before_ `inst` remain in the original block.
76+
func splitBlock(after inst: Instruction) -> BasicBlock {
77+
notifyBranchesChanged()
78+
return _bridged.splitBlockAfter(inst.bridged).block
79+
}
80+
81+
func createBlock(after block: BasicBlock) -> BasicBlock {
5982
notifyBranchesChanged()
60-
return _bridged.splitBlock(inst.bridged).block
83+
return _bridged.createBlockAfter(block.bridged).block
6184
}
6285

6386
func erase(instruction: Instruction) {

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ private func registerSwiftPasses() {
8686
registerPass(deadStoreElimination, { deadStoreElimination.run($0) })
8787
registerPass(redundantLoadElimination, { redundantLoadElimination.run($0) })
8888
registerPass(earlyRedundantLoadElimination, { earlyRedundantLoadElimination.run($0) })
89+
registerPass(deinitDevirtualizer, { deinitDevirtualizer.run($0) })
8990

9091
// Instruction passes
9192
registerForSILCombine(BeginCOWMutationInst.self, { run(BeginCOWMutationInst.self, $0) })

SwiftCompilerSources/Sources/Optimizer/TestPasses/TestInstructionIteration.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ private func handle(instruction: Instruction, _ context: FunctionPassContext) {
6161
case "delete_branches":
6262
deleteAllInstructions(ofType: BranchInst.self, in: instruction.parentBlock, context)
6363
case "split_block":
64-
_ = context.splitBlock(at: instruction)
64+
_ = context.splitBlock(before: instruction)
6565
default:
6666
break
6767
}

SwiftCompilerSources/Sources/Optimizer/Utilities/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
swift_compiler_sources(Optimizer
10+
DiagnosticEngine.swift
11+
Devirtualization.swift
1012
EscapeUtils.swift
1113
OptUtils.swift
1214
ForwardingUtils.swift

0 commit comments

Comments
 (0)