Skip to content

Commit 3591ae4

Browse files
committed
libswift: implement ReleaseDevirtualizer in Swift
1 parent 781ed4a commit 3591ae4

File tree

17 files changed

+219
-15
lines changed

17 files changed

+219
-15
lines changed

SwiftCompilerSources/Sources/AST/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This source file is part of the Swift.org open source project
22
#
3-
# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
3+
# Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
44
# Licensed under Apache License v2.0 with Runtime Library Exception
55
#
66
# See http://swift.org/LICENSE.txt for license information

SwiftCompilerSources/Sources/AST/SubstitutionMap.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===--- PassUtils.swift - Utilities for optimzation passes ---------------===//
1+
//===--- SubstitutionMap.swift - Swift Substitution Map ASTs --------------===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# This source file is part of the Swift.org open source project
22
#
3-
# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
3+
# Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
44
# Licensed under Apache License v2.0 with Runtime Library Exception
55
#
66
# See http://swift.org/LICENSE.txt for license information
77
# See http://swift.org/CONTRIBUTORS.txt for Swift project authors
88

99
swift_compiler_sources(Optimizer
1010
AliasAnalysis.swift
11-
CalleeAnalysis.swift)
11+
CalleeAnalysis.swift
12+
RCIdentityAnalysis.swift)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===--- CalleeAnalysis.swift - the callee analysis -----------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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 OptimizerBridging
14+
import SIL
15+
16+
struct RCIdentityAnalysis {
17+
let bridged: BridgedRCIdentityAnalysis
18+
19+
func getFunctionInfo(_ function: Function) -> RCIdentityFunctionInfo {
20+
RCIdentityFunctionInfo(bridged: RCIdentityAnalysis_getFunctionInfo(bridged, function.bridged))
21+
}
22+
}
23+
24+
struct RCIdentityFunctionInfo {
25+
let bridged: BridgedRCIdentityFunctionInfo
26+
27+
func getRCIdentityRoot(_ value: Value) -> AnyObject {
28+
RCIdentityFunctionInfo_getRCIdentityRoot(bridged, value.bridged).getAs(AnyObject.self)
29+
}
30+
}

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This source file is part of the Swift.org open source project
22
#
3-
# Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
3+
# Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
44
# Licensed under Apache License v2.0 with Runtime Library Exception
55
#
66
# See http://swift.org/LICENSE.txt for license information
@@ -10,4 +10,5 @@ swift_compiler_sources(Optimizer
1010
AssumeSingleThreaded.swift
1111
SILPrinter.swift
1212
MergeCondFails.swift
13+
ReleaseDevirtualizer.swift
1314
)
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//===--- ReleaseDevirtualizer.swift - Devirtualizes release-instructions --===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 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 release instructions which are known to destruct the object.
16+
///
17+
/// This means, it replaces a sequence of
18+
/// %x = alloc_ref [stack] $X
19+
/// ...
20+
/// strong_release %x
21+
/// dealloc_stack_ref %x
22+
/// with
23+
/// %x = alloc_ref [stack] $X
24+
/// ...
25+
/// set_deallocating %x
26+
/// %d = function_ref @dealloc_of_X
27+
/// %a = apply %d(%x)
28+
/// dealloc_stack_ref %x
29+
///
30+
/// The optimization is only done for stack promoted objects because they are
31+
/// known to have no associated objects (which are not explicitly released
32+
/// in the deinit method).
33+
let releaseDevirtualizerPass = FunctionPass(
34+
name: "release-devirtualizer", { function, context in
35+
let functionInfo = context.rcIdentityAnalysis.getFunctionInfo(function)
36+
37+
for block in function.blocks {
38+
// The last `release_value`` or `strong_release`` instruction before the
39+
// deallocation.
40+
var lastRelease: RefCountingInst?
41+
42+
for instruction in block.instructions {
43+
if let release = lastRelease {
44+
// We only do the optimization for stack promoted object, because for
45+
// these we know that they don't have associated objects, which are
46+
// _not_ released by the deinit method.
47+
if let deallocStackRef = instruction as? DeallocStackRefInst {
48+
devirtualizeReleaseOfObject(context, release, deallocStackRef, functionInfo)
49+
lastRelease = nil
50+
continue
51+
}
52+
}
53+
54+
if instruction is ReleaseValueInst || instruction is StrongReleaseInst {
55+
lastRelease = instruction as? RefCountingInst
56+
} else if instruction.mayReleaseOrReadRefCount {
57+
lastRelease = nil
58+
}
59+
}
60+
}
61+
}
62+
)
63+
64+
/// Devirtualize releases of array buffers.
65+
private func devirtualizeReleaseOfObject(
66+
_ context: PassContext,
67+
_ release: RefCountingInst,
68+
_ deallocStackRef: DeallocStackRefInst,
69+
_ functionInfo: RCIdentityFunctionInfo
70+
) {
71+
let allocRefInstruction = deallocStackRef.allocRef
72+
let rcRoot = functionInfo.getRCIdentityRoot(release.operands[0].value)
73+
var root = release.operands[0].value
74+
while let newRoot = getRCIdentityPreservingCastOperand(root) {
75+
root = newRoot
76+
}
77+
78+
precondition(rcRoot === root)
79+
80+
guard rcRoot === allocRefInstruction else {
81+
return
82+
}
83+
84+
guard rcRoot === release.operands[0].value else {
85+
print(release.description)
86+
print(release.block.function.description)
87+
fatalError()
88+
}
89+
90+
createDeallocCall(context, allocRefInstruction.type, release, allocRefInstruction)
91+
}
92+
93+
/// Replaces the release-instruction `release` with an explicit call to
94+
/// the deallocating destructor of `type` for `object`.
95+
private func createDeallocCall(
96+
_ context: PassContext,
97+
_ type: Type,
98+
_ release: RefCountingInst,
99+
_ object: Value
100+
) {
101+
guard let dealloc = context.getDealloc(for: type) else {
102+
return
103+
}
104+
105+
let substitutionMap = context.getContextSubstitutionMap(for: type)
106+
107+
let builder = Builder(at: release, location: release.location, context)
108+
109+
var object = object
110+
if object.type != type {
111+
object = builder.createUncheckedRefCast(object: object, type: type)
112+
}
113+
114+
// Do what a release would do before calling the deallocator: set the object
115+
// in deallocating state, which means set the RC_DEALLOCATING_FLAG flag.
116+
builder.createSetDeallocating(operand: object, isAtomic: release.isAtomic)
117+
118+
// Create the call to the destructor with the allocated object as self
119+
// argument.
120+
let functionRef = builder.createFunctionRef(dealloc)
121+
122+
builder.createApply(function: functionRef, substitutionMap, arguments: [object])
123+
context.erase(instruction: release)
124+
}
125+
126+
private func getRCIdentityPreservingCastOperand(_ value: Value) -> Value? {
127+
switch value {
128+
case let inst as Instruction where inst is UpcastInst ||
129+
inst is UncheckedRefCastInst ||
130+
inst is InitExistentialRefInst ||
131+
inst is OpenExistentialRefInst ||
132+
inst is RefToBridgeObjectInst ||
133+
inst is BridgeObjectToRefInst ||
134+
inst is ConvertFunctionInst:
135+
return inst.operands[0].value
136+
137+
default:
138+
return nil
139+
}
140+
}

SwiftCompilerSources/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,5 @@ private func registerSwiftPasses() {
5151
registerPass(simplifyStrongRetainPass, { simplifyStrongRetainPass.run($0) })
5252
registerPass(simplifyStrongReleasePass, { simplifyStrongReleasePass.run($0) })
5353
registerPass(assumeSingleThreadedPass, { assumeSingleThreadedPass.run($0) })
54+
registerPass(releaseDevirtualizerPass, { releaseDevirtualizerPass.run($0) })
5455
}

SwiftCompilerSources/Sources/Optimizer/PassManager/PassUtils.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ struct PassContext {
3737
return CalleeAnalysis(bridged: bridgeCA)
3838
}
3939

40+
var rcIdentityAnalysis: RCIdentityAnalysis {
41+
.init(bridged: PassContext_getRCIdentityAnalysis(passContext))
42+
}
43+
4044
enum EraseMode {
4145
case onlyInstruction, includingDebugUses
4246
}
@@ -86,8 +90,8 @@ struct PassContext {
8690
RefCountingInst_setIsAtomic(instruction.bridged, isAtomic)
8791
}
8892

89-
func getDeallocRef(for type: Type) -> Function? {
90-
PassContext_getDeallocRef(passContext, type.bridged).function
93+
func getDealloc(for type: Type) -> Function? {
94+
PassContext_getDealloc(passContext, type.bridged).function
9195
}
9296

9397
func getContextSubstitutionMap(for type: Type) -> SubstitutionMap {

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ public class Instruction : ListNode, CustomStringConvertible, Hashable {
9191
}
9292
}
9393

94+
final public var mayReleaseOrReadRefCount: Bool {
95+
return SILInstruction_mayReleaseOrReadRefCount(bridged)
96+
}
97+
9498
public static func ==(lhs: Instruction, rhs: Instruction) -> Bool {
9599
lhs === rhs
96100
}
@@ -230,7 +234,9 @@ final public class SetDeallocatingInst : Instruction, UnaryInstruction {}
230234

231235
final public class DeallocRefInst : Instruction, UnaryInstruction {}
232236

233-
public class RefCountingInst : Instruction, UnaryInstruction {}
237+
public class RefCountingInst : Instruction, UnaryInstruction {
238+
public var isAtomic: Bool { RefCountingInst_getIsAtomic(bridged) }
239+
}
234240

235241
final public class StrongRetainInst : RefCountingInst {
236242
}
@@ -478,6 +484,9 @@ final public class BeginApplyInst : MultipleValueInstruction, FullApplySite {
478484
public var singleDirectResult: Value? { nil }
479485
}
480486

487+
final public class RefToBridgeObjectInst: MultipleValueInstruction {
488+
}
489+
481490
//===----------------------------------------------------------------------===//
482491
// terminator instructions
483492
//===----------------------------------------------------------------------===//

SwiftCompilerSources/Sources/SIL/Registration.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ public func registerSILClasses() {
120120
register(DestructureStructInst.self)
121121
register(DestructureTupleInst.self)
122122
register(BeginApplyInst.self)
123+
register(RefToBridgeObjectInst.self)
123124

124125
register(UnreachableInst.self)
125126
register(ReturnInst.self)

SwiftCompilerSources/Sources/SIL/Type.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,9 @@ public struct Type {
2222
return SILType_isTrivial(bridged, function.bridged) != 0
2323
}
2424
}
25+
26+
extension Type: Equatable {
27+
public static func == (lhs: Type, rhs: Type) -> Bool {
28+
lhs.bridged.typePtr == rhs.bridged.typePtr
29+
}
30+
}

include/swift/SIL/SILBridging.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ void SILInstruction_setOperand(BridgedInstruction inst, SwiftInt index,
218218
BridgedValue value);
219219
BridgedLocation SILInstruction_getLocation(BridgedInstruction inst);
220220
BridgedMemoryBehavior SILInstruction_getMemBehavior(BridgedInstruction inst);
221+
bool SILInstruction_mayReleaseOrReadRefCount(BridgedInstruction inst);
221222

222223
BridgedInstruction MultiValueInstResult_getParent(BridgedMultiValueResult result);
223224
SwiftInt MultipleValueInstruction_getNumResults(BridgedInstruction inst);
@@ -243,6 +244,7 @@ BridgedBasicBlock BranchInst_getTargetBlock(BridgedInstruction bi);
243244
SwiftInt SwitchEnumInst_getNumCases(BridgedInstruction se);
244245
SwiftInt SwitchEnumInst_getCaseIndex(BridgedInstruction se, SwiftInt idx);
245246
SwiftInt StoreInst_getStoreOwnership(BridgedInstruction store);
247+
bool RefCountingInst_getIsAtomic(BridgedInstruction rc);
246248
void RefCountingInst_setIsAtomic(BridgedInstruction rc, bool isAtomic);
247249

248250
BridgedInstruction SILBuilder_createBuiltinBinaryFunction(

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ RCIdentityAnalysis_getFunctionInfo(BridgedRCIdentityAnalysis bridgedAnalysis,
8989
BridgedValue RCIdentityFunctionInfo_getRCIdentityRoot(
9090
BridgedRCIdentityFunctionInfo bridgedInfo, BridgedValue value);
9191

92-
OptionalBridgedFunction PassContext_getDeallocRef(BridgedPassContext context,
93-
BridgedType type);
92+
OptionalBridgedFunction PassContext_getDealloc(BridgedPassContext context,
93+
BridgedType type);
9494

9595
BridgedSubstitutionMap
9696
PassContext_getContextSubstitutionMap(BridgedPassContext context,

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ PASS(RedundantPhiElimination, "redundant-phi-elimination",
336336
"Redundant Phi Block Argument Elimination")
337337
PASS(PhiExpansion, "phi-expansion",
338338
"Replace Phi arguments by their only used field")
339-
PASS(ReleaseDevirtualizer, "release-devirtualizer",
339+
SWIFT_FUNCTION_PASS_WITH_LEGACY(ReleaseDevirtualizer, "release-devirtualizer",
340340
"SIL release Devirtualization")
341341
PASS(RetainSinking, "retain-sinking",
342342
"SIL retain Sinking")

lib/SIL/Utils/SILBridging.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,10 @@ BridgedMemoryBehavior SILInstruction_getMemBehavior(BridgedInstruction inst) {
394394
return (BridgedMemoryBehavior)castToInst(inst)->getMemoryBehavior();
395395
}
396396

397+
bool SILInstruction_mayReleaseOrReadRefCount(BridgedInstruction inst) {
398+
return castToInst(inst)->mayReleaseOrReadRefCount();
399+
}
400+
397401
BridgedInstruction MultiValueInstResult_getParent(BridgedMultiValueResult result) {
398402
return {static_cast<MultipleValueInstructionResult *>(result.obj)->getParent()};
399403
}
@@ -483,6 +487,11 @@ SwiftInt StoreInst_getStoreOwnership(BridgedInstruction store) {
483487
return (SwiftInt)castToInst<StoreInst>(store)->getOwnershipQualifier();
484488
}
485489

490+
bool RefCountingInst_getIsAtomic(BridgedInstruction rc) {
491+
return castToInst<RefCountingInst>(rc)->getAtomicity() ==
492+
RefCountingInst::Atomicity::Atomic;
493+
}
494+
486495
void RefCountingInst_setIsAtomic(BridgedInstruction rc, bool isAtomic) {
487496
castToInst<RefCountingInst>(rc)->setAtomicity(
488497
isAtomic ? RefCountingInst::Atomicity::Atomic
@@ -558,4 +567,4 @@ BridgedInstruction SILBuilder_createApply(BridgedInstruction insertionPoint,
558567
return {builder.createApply(getRegularLocation(loc), castToSILValue(function),
559568
castToSubstitutionMap(subMap),
560569
getSILValues(arguments, argValues))};
561-
}
570+
}

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,8 +1217,8 @@ PassContext_getRCIdentityAnalysis(BridgedPassContext context) {
12171217
return {pm->getAnalysis<RCIdentityAnalysis>()};
12181218
}
12191219

1220-
OptionalBridgedFunction PassContext_getDeallocRef(BridgedPassContext context,
1221-
BridgedType type) {
1220+
OptionalBridgedFunction PassContext_getDealloc(BridgedPassContext context,
1221+
BridgedType type) {
12221222
auto *cd = castToSILType(type).getClassOrBoundGenericClass();
12231223
assert(cd && "no class type allocated with alloc_ref");
12241224

lib/SILOptimizer/Transforms/ReleaseDevirtualizer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,6 @@ bool ReleaseDevirtualizer::createDeallocCall(SILType AllocType,
159159

160160
} // end anonymous namespace
161161

162-
SILTransform *swift::createReleaseDevirtualizer() {
162+
SILTransform *swift::createLegacyReleaseDevirtualizer() {
163163
return new ReleaseDevirtualizer();
164164
}

0 commit comments

Comments
 (0)