Skip to content

Commit d8a0143

Browse files
committed
libswift: implement ReleaseDevirtualizer in Swift
1 parent df63a19 commit d8a0143

File tree

21 files changed

+228
-34
lines changed

21 files changed

+228
-34
lines changed

SwiftCompilerSources/Sources/AST/CMakeLists.txt

Lines changed: 0 additions & 11 deletions
This file was deleted.

SwiftCompilerSources/Sources/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@
99
if(SWIFT_ENABLE_EXPERIMENTAL_STRING_PROCESSING)
1010
add_subdirectory(ExperimentalRegex)
1111
endif()
12-
add_subdirectory(AST)
1312
add_subdirectory(SIL)
1413
add_subdirectory(Optimizer)

SwiftCompilerSources/Sources/Optimizer/Analysis/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
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
@@ -11,4 +11,5 @@ swift_compiler_sources(Optimizer
1111
CalleeAnalysis.swift
1212
DeadEndBlocksAnalysis.swift
1313
DominatorTree.swift
14-
PostDominatorTree.swift)
14+
PostDominatorTree.swift
15+
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 rootObject = 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(rootObject === root)
79+
80+
guard rootObject === allocRefInstruction else {
81+
return
82+
}
83+
84+
guard rootObject === 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.getDestructor(ofClass: 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: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
import AST
14-
import SIL
1513
import OptimizerBridging
14+
import SIL
1615

1716
public typealias BridgedFunctionPassCtxt =
1817
OptimizerBridging.BridgedFunctionPassCtxt
@@ -64,6 +63,10 @@ struct PassContext {
6463
PassContext_notifyChanges(_bridged, branchesChanged)
6564
}
6665

66+
var rcIdentityAnalysis: RCIdentityAnalysis {
67+
.init(bridged: PassContext_getRCIdentityAnalysis(_bridged))
68+
}
69+
6770
enum EraseMode {
6871
case onlyInstruction, includingDebugUses
6972
}
@@ -117,12 +120,12 @@ struct PassContext {
117120
PassContext_fixStackNesting(_bridged, function.bridged)
118121
}
119122

120-
func getDeallocRef(for type: Type) -> Function? {
121-
PassContext_getDeallocRef(passContext, type.bridged).function
123+
func getDestructor(ofClass type: Type) -> Function? {
124+
PassContext_getDestructor(_bridged, type.bridged).function
122125
}
123126

124127
func getContextSubstitutionMap(for type: Type) -> SubstitutionMap {
125-
SubstitutionMap(PassContext_getContextSubstitutionMap(passContext, type.bridged))
128+
SubstitutionMap(PassContext_getContextSubstitutionMap(_bridged, type.bridged))
126129
}
127130
}
128131

SwiftCompilerSources/Sources/SIL/Builder.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
import AST
1413
import SILBridging
1514

1615
/// A utility to create new instructions at a given insertion point.
@@ -102,14 +101,16 @@ public struct Builder {
102101
function: Value,
103102
_ substitutionMap: SubstitutionMap,
104103
arguments: [Value]
105-
) -> FunctionRefInst {
104+
) -> ApplyInst {
106105
notifyInstructionsChanged()
107-
let functionRef = arguments.withBridgedValues { valuesRef in
106+
notifyCallsChanged()
107+
108+
let apply = arguments.withBridgedValues { valuesRef in
108109
SILBuilder_createApply(
109110
bridgedInsPoint, location.bridgedLocation, function.bridged,
110111
substitutionMap.bridged, valuesRef
111112
)
112113
}
113-
return functionRef.getAs(FunctionRefInst.self)
114+
return apply.getAs(ApplyInst.self)
114115
}
115116
}

SwiftCompilerSources/Sources/SIL/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ add_swift_compiler_module(SIL
1717
Location.swift
1818
Operand.swift
1919
Registration.swift
20+
SubstitutionMap.swift
2021
Type.swift
2122
Utils.swift
2223
Value.swift)

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ public class Instruction : ListNode, CustomStringConvertible, Hashable {
9898
}
9999
}
100100

101+
final public var mayReleaseOrReadRefCount: Bool {
102+
return SILInstruction_mayReleaseOrReadRefCount(bridged)
103+
}
104+
101105
public static func ==(lhs: Instruction, rhs: Instruction) -> Bool {
102106
lhs === rhs
103107
}
@@ -237,7 +241,9 @@ final public class SetDeallocatingInst : Instruction, UnaryInstruction {}
237241

238242
final public class DeallocRefInst : Instruction, UnaryInstruction {}
239243

240-
public class RefCountingInst : Instruction, UnaryInstruction {}
244+
public class RefCountingInst : Instruction, UnaryInstruction {
245+
public var isAtomic: Bool { RefCountingInst_getIsAtomic(bridged) }
246+
}
241247

242248
final public class StrongRetainInst : RefCountingInst {
243249
}
@@ -355,8 +361,6 @@ final public class GlobalValueInst : GlobalAccessInst {}
355361

356362
final public class IntegerLiteralInst : SingleValueInstruction {}
357363

358-
final public class FunctionRefInst: SingleValueInstruction {}
359-
360364
final public class TupleInst : SingleValueInstruction {
361365
}
362366

@@ -517,6 +521,9 @@ final public class BeginApplyInst : MultipleValueInstruction, FullApplySite {
517521
public var singleDirectResult: Value? { nil }
518522
}
519523

524+
final public class RefToBridgeObjectInst: MultipleValueInstruction {
525+
}
526+
520527
//===----------------------------------------------------------------------===//
521528
// terminator instructions
522529
//===----------------------------------------------------------------------===//

SwiftCompilerSources/Sources/SIL/Registration.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ public func registerSILClasses() {
8080
register(GlobalAddrInst.self)
8181
register(GlobalValueInst.self)
8282
register(IntegerLiteralInst.self)
83-
register(FunctionRefInst.self)
8483
register(TupleInst.self)
8584
register(TupleExtractInst.self)
8685
register(TupleElementAddrInst.self)
@@ -122,6 +121,7 @@ public func registerSILClasses() {
122121
register(DestructureStructInst.self)
123122
register(DestructureTupleInst.self)
124123
register(BeginApplyInst.self)
124+
register(RefToBridgeObjectInst.self)
125125

126126
register(UnreachableInst.self)
127127
register(ReturnInst.self)

SwiftCompilerSources/Sources/AST/SubstitutionMap.swift renamed to SwiftCompilerSources/Sources/SIL/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
//

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
@@ -211,6 +211,7 @@ void SILInstruction_setOperand(BridgedInstruction inst, SwiftInt index,
211211
BridgedValue value);
212212
BridgedLocation SILInstruction_getLocation(BridgedInstruction inst);
213213
BridgedMemoryBehavior SILInstruction_getMemBehavior(BridgedInstruction inst);
214+
bool SILInstruction_mayReleaseOrReadRefCount(BridgedInstruction inst);
214215

215216
BridgedInstruction MultiValueInstResult_getParent(BridgedMultiValueResult result);
216217
SwiftInt MultipleValueInstruction_getNumResults(BridgedInstruction inst);
@@ -240,6 +241,7 @@ BridgedBasicBlock BranchInst_getTargetBlock(BridgedInstruction bi);
240241
SwiftInt SwitchEnumInst_getNumCases(BridgedInstruction se);
241242
SwiftInt SwitchEnumInst_getCaseIndex(BridgedInstruction se, SwiftInt idx);
242243
SwiftInt StoreInst_getStoreOwnership(BridgedInstruction store);
244+
bool RefCountingInst_getIsAtomic(BridgedInstruction rc);
243245
void RefCountingInst_setIsAtomic(BridgedInstruction rc, bool isAtomic);
244246

245247
BridgedInstruction SILBuilder_createBuiltinBinaryFunction(

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ RCIdentityAnalysis_getFunctionInfo(BridgedRCIdentityAnalysis bridgedAnalysis,
151151
BridgedValue RCIdentityFunctionInfo_getRCIdentityRoot(
152152
BridgedRCIdentityFunctionInfo bridgedInfo, BridgedValue value);
153153

154-
OptionalBridgedFunction PassContext_getDeallocRef(BridgedPassContext context,
154+
OptionalBridgedFunction PassContext_getDestructor(BridgedPassContext context,
155155
BridgedType type);
156156

157157
BridgedSubstitutionMap

0 commit comments

Comments
 (0)