Skip to content

Commit 38de5b1

Browse files
committed
Swift SIL/Optimizer: implement cloning of static init values of globals in Swift
* add the StaticInitCloner utility * remove bridging of `copyStaticInitializer` and `createStaticInitializer` * add `Context.mangleOutlinedVariable` and `Context.createGlobalVariable`
1 parent ee1c52b commit 38de5b1

File tree

10 files changed

+352
-48
lines changed

10 files changed

+352
-48
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,11 @@ let initializeStaticGlobalsPass = FunctionPass(name: "initialize-static-globals"
5656
return
5757
}
5858

59-
context.createStaticInitializer(for: allocInst.global,
60-
initValue: storeToGlobal.source as! SingleValueInstruction)
59+
var cloner = StaticInitCloner(cloneTo: allocInst.global, context)
60+
defer { cloner.deinitialize() }
61+
62+
_ = cloner.clone(storeToGlobal.source)
63+
6164
context.erase(instruction: allocInst)
6265
context.erase(instruction: storeToGlobal)
6366
}

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyLoad.swift

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@ extension LoadInst : OnoneSimplifyable {
2121
guard let globalInitVal = getGlobalInitValue(address: address) else {
2222
return
2323
}
24-
let builder = Builder(before: self, context)
25-
guard let initVal = context.copyStaticInitializer(fromInitValue: globalInitVal, to: builder) else {
24+
if !globalInitVal.canBeCopied(into: parentFunction, context) {
2625
return
2726
}
27+
var cloner = StaticInitCloner(cloneBefore: self, context)
28+
defer { cloner.deinitialize() }
29+
30+
let initVal = cloner.clone(globalInitVal)
31+
2832
uses.replaceAll(with: initVal, context)
2933
transitivelyErase(load: self, context)
3034
}
@@ -77,3 +81,36 @@ private func transitivelyErase(load: LoadInst, _ context: SimplifyContext) {
7781
inst = operandInst
7882
}
7983
}
84+
85+
private extension Value {
86+
func canBeCopied(into function: Function, _ context: SimplifyContext) -> Bool {
87+
if !function.isSerialized {
88+
return true
89+
}
90+
91+
// Can't use `ValueSet` because the this value is inside a global initializer and
92+
// not inside a function.
93+
var worklist = Stack<Value>(context)
94+
defer { worklist.deinitialize() }
95+
96+
var handled = Set<ObjectIdentifier>()
97+
98+
worklist.push(self)
99+
handled.insert(ObjectIdentifier(self))
100+
101+
while let value = worklist.pop() {
102+
if let fri = value as? FunctionRefInst {
103+
if !fri.referencedFunction.hasValidLinkageForFragileRef {
104+
return false
105+
}
106+
}
107+
for op in value.definingInstruction!.operands {
108+
if handled.insert(ObjectIdentifier(op.value)).inserted {
109+
worklist.push(op.value)
110+
}
111+
}
112+
}
113+
return true
114+
}
115+
}
116+

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -127,19 +127,6 @@ extension MutatingContext {
127127
}
128128
}
129129

130-
/// Copies all instructions of a static init value of a global to the insertion point of `builder`.
131-
func copyStaticInitializer(fromInitValue: Value, to builder: Builder) -> Value? {
132-
let range = _bridged.copyStaticInitializer(fromInitValue.bridged, builder.bridged)
133-
guard let result = range.clonedInitValue.value,
134-
let firstClonedInst = range.firstClonedInst.instruction else {
135-
return nil
136-
}
137-
let resultInst = result.definingInstruction!
138-
notifyNewInstructions(from: firstClonedInst, to: resultInst)
139-
notifyInstructionChanged(resultInst)
140-
return result
141-
}
142-
143130
private func notifyNewInstructions(from: Instruction, to: Instruction) {
144131
var inst = from
145132
while inst != to {
@@ -262,10 +249,16 @@ struct FunctionPassContext : MutatingContext {
262249
return false
263250
}
264251

265-
/// Copies `initValue` (including all operand instructions, transitively) to the
266-
/// static init value of `global`.
267-
func createStaticInitializer(for global: GlobalVariable, initValue: SingleValueInstruction) {
268-
_bridged.createStaticInitializer(global.bridged, initValue.bridged)
252+
func mangleOutlinedVariable(from function: Function) -> String {
253+
let stdString = _bridged.mangleOutlinedVariable(function.bridged)
254+
return String(_cxxString: stdString)
255+
}
256+
257+
func createGlobalVariable(name: String, type: Type, isPrivate: Bool) -> GlobalVariable {
258+
let gv = name._withStringRef {
259+
_bridged.createGlobalVariable($0, type.bridged, isPrivate)
260+
}
261+
return gv.globalVar
269262
}
270263
}
271264

@@ -328,6 +321,12 @@ extension Builder {
328321
self.init(insertAt: .before(firstInst), location: firstInst.location,
329322
context.notifyInstructionChanged, context._bridged.asNotificationHandler())
330323
}
324+
325+
init(staticInitializerOf global: GlobalVariable, _ context: some MutatingContext) {
326+
self.init(insertAt: .staticInitializer(global),
327+
location: Location.artificialUnreachableLocation,
328+
{ _ in }, context._bridged.asNotificationHandler())
329+
}
331330
}
332331

333332
//===----------------------------------------------------------------------===//

SwiftCompilerSources/Sources/Optimizer/Utilities/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ swift_compiler_sources(Optimizer
1111
OptUtils.swift
1212
WalkUtils.swift
1313
AccessUtils.swift
14+
StaticInitCloner.swift
1415
)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//===--- StaticInitCloner.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+
import OptimizerBridging
15+
16+
/// Clones the initializer value of a GlobalVariable.
17+
///
18+
/// Used to transitively clone "constant" instructions, including their operands,
19+
/// from or to the static initializer value of a GlobalVariable.
20+
///
21+
struct StaticInitCloner<Context: MutatingContext> {
22+
private var bridged: BridgedCloner
23+
private let context: Context
24+
private let cloningIntoFunction: Bool
25+
26+
init(cloneTo global: GlobalVariable, _ context: Context) {
27+
self.bridged = BridgedCloner(global.bridged, context._bridged)
28+
self.context = context
29+
self.cloningIntoFunction = false
30+
}
31+
32+
init(cloneBefore inst: Instruction, _ context: Context) {
33+
self.bridged = BridgedCloner(inst.bridged, context._bridged)
34+
self.context = context
35+
self.cloningIntoFunction = true
36+
}
37+
38+
mutating func deinitialize() {
39+
bridged.destroy(context._bridged)
40+
}
41+
42+
/// Transitively clones `value` including its defining instruction's operands.
43+
mutating func clone(_ value: Value) -> Value {
44+
45+
if isCloned(value: value) {
46+
return getClonedValue(of: value)
47+
}
48+
49+
let inst = value.definingInstruction!
50+
for op in inst.operands {
51+
_ = clone(op.value)
52+
}
53+
54+
bridged.clone(inst.bridged)
55+
let clonedValue = getClonedValue(of: value)
56+
if cloningIntoFunction {
57+
context.notifyInstructionChanged(clonedValue.definingInstruction!)
58+
}
59+
return clonedValue
60+
}
61+
62+
mutating func getClonedValue(of value: Value) -> Value {
63+
bridged.getClonedValue(value.bridged).value
64+
}
65+
66+
func isCloned(value: Value) -> Bool {
67+
bridged.isValueCloned(value.bridged)
68+
}
69+
}

SwiftCompilerSources/Sources/SIL/GlobalVariable.swift

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,100 @@ final public class GlobalVariable : CustomStringConvertible, HasShortDescription
6666

6767
extension Instruction {
6868
public var isValidInStaticInitializerOfGlobal: Bool {
69-
return BridgedGlobalVar.isValidStaticInitializer(bridged)
69+
// Rule out SILUndef and SILArgument.
70+
if operands.contains(where: { $0.value.definingInstruction == nil }) {
71+
return false
72+
}
73+
switch self {
74+
case let bi as BuiltinInst:
75+
switch bi.id {
76+
case .ZeroInitializer:
77+
let type = bi.type.isBuiltinVector ? bi.type.builtinVectorElementType : bi.type
78+
return type.isBuiltinInteger || type.isBuiltinFloat
79+
case .PtrToInt:
80+
return bi.operands[0].value is StringLiteralInst
81+
case .StringObjectOr:
82+
// The first operand can be a string literal (i.e. a pointer), but the
83+
// second operand must be a constant. This enables creating a
84+
// a pointer+offset relocation.
85+
// Note that StringObjectOr requires the or'd bits in the first
86+
// operand to be 0, so the operation is equivalent to an addition.
87+
return bi.operands[1].value is IntegerLiteralInst
88+
case .ZExtOrBitCast:
89+
return true;
90+
case .USubOver:
91+
// Handle StringObjectOr(tuple_extract(usub_with_overflow(x, offset)), bits)
92+
// This pattern appears in UTF8 String literal construction.
93+
if let tei = bi.uses.getSingleUser(ofType: TupleExtractInst.self),
94+
tei.isResultOfOffsetSubtract {
95+
return true
96+
}
97+
return false
98+
case .OnFastPath:
99+
return true
100+
default:
101+
return false
102+
}
103+
case let tei as TupleExtractInst:
104+
// Handle StringObjectOr(tuple_extract(usub_with_overflow(x, offset)), bits)
105+
// This pattern appears in UTF8 String literal construction.
106+
if tei.isResultOfOffsetSubtract,
107+
let bi = tei.uses.getSingleUser(ofType: BuiltinInst.self),
108+
bi.id == .StringObjectOr {
109+
return true
110+
}
111+
return false
112+
case let sli as StringLiteralInst:
113+
switch sli.encoding {
114+
case .Bytes, .UTF8:
115+
return true
116+
case .ObjCSelector:
117+
// Objective-C selector string literals cannot be used in static
118+
// initializers.
119+
return false
120+
}
121+
case let fri as FunctionRefInst:
122+
// TODO: support async function pointers in static globals.
123+
return !fri.referencedFunction.isAsync
124+
case is StructInst,
125+
is TupleInst,
126+
is IntegerLiteralInst,
127+
is FloatLiteralInst,
128+
is ObjectInst,
129+
is ValueToBridgeObjectInst,
130+
is ConvertFunctionInst,
131+
is ThinToThickFunctionInst:
132+
return true
133+
default:
134+
return false
135+
}
136+
}
137+
}
138+
139+
// Match the pattern:
140+
// tuple_extract(usub_with_overflow(x, integer_literal, integer_literal 0), 0)
141+
private extension TupleExtractInst {
142+
var isResultOfOffsetSubtract: Bool {
143+
if fieldIndex == 0,
144+
let bi = tuple as? BuiltinInst,
145+
bi.id == .USubOver,
146+
bi.operands[1].value is IntegerLiteralInst,
147+
let overFlowFlag = bi.operands[2].value as? IntegerLiteralInst,
148+
overFlowFlag.value.isNullValue() {
149+
return true
150+
}
151+
return false
70152
}
71153
}
72154

73155
// Bridging utilities
74156

75157
extension BridgedGlobalVar {
76-
var globalVar: GlobalVariable { obj.getAs(GlobalVariable.self) }
158+
public var globalVar: GlobalVariable { obj.getAs(GlobalVariable.self) }
159+
160+
public var optional: OptionalBridgedGlobalVar {
161+
OptionalBridgedGlobalVar(obj: self.obj)
162+
}
77163
}
78164

79165
extension OptionalBridgedGlobalVar {

include/swift/SIL/SILBridging.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -363,8 +363,6 @@ struct BridgedGlobalVar {
363363
inline OptionalBridgedInstruction getStaticInitializerValue() const;
364364

365365
bool canBeInitializedStatically() const;
366-
367-
static inline bool isValidStaticInitializer(BridgedInstruction inst);
368366
};
369367

370368
struct OptionalBridgedGlobalVar {
@@ -1285,11 +1283,6 @@ OptionalBridgedInstruction BridgedGlobalVar::getStaticInitializerValue() const {
12851283
return {nullptr};
12861284
}
12871285

1288-
bool BridgedGlobalVar::isValidStaticInitializer(BridgedInstruction inst) {
1289-
swift::SILInstruction *i = inst.getInst();
1290-
return swift::SILGlobalVariable::isValidStaticInitializerInst(i, i->getModule());
1291-
}
1292-
12931286
BridgedInstruction BridgedMultiValueResult::getParent() const {
12941287
return {getMVResult()->getParent()};
12951288
}

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,27 @@ struct BridgedNodeSet {
139139
}
140140
};
141141

142+
namespace swift {
143+
class ClonerWithFixedLocation;
144+
}
145+
146+
struct BridgedCloner {
147+
swift::ClonerWithFixedLocation * _Nonnull cloner;
148+
149+
BridgedCloner(BridgedGlobalVar var, BridgedPassContext context);
150+
151+
BridgedCloner(BridgedInstruction inst, BridgedPassContext context);
152+
153+
void destroy(BridgedPassContext context);
154+
155+
SWIFT_IMPORT_UNSAFE
156+
BridgedValue getClonedValue(BridgedValue v);
157+
158+
bool isValueCloned(BridgedValue v) const;
159+
160+
void clone(BridgedInstruction inst);
161+
};
162+
142163
struct BridgedPostDomTree {
143164
swift::PostDominanceInfo * _Nonnull pdi;
144165

@@ -220,15 +241,10 @@ struct BridgedPassContext {
220241

221242
bool specializeAppliesInFunction(BridgedFunction function, bool isMandatory) const;
222243

223-
void createStaticInitializer(BridgedGlobalVar global, BridgedInstruction initValue) const;
224-
225-
struct StaticInitCloneResult {
226-
OptionalBridgedInstruction firstClonedInst;
227-
OptionalBridgedValue clonedInitValue;
228-
};
244+
std::string mangleOutlinedVariable(BridgedFunction function) const;
229245

230246
SWIFT_IMPORT_UNSAFE
231-
StaticInitCloneResult copyStaticInitializer(BridgedValue initValue, BridgedBuilder b) const;
247+
BridgedGlobalVar createGlobalVariable(llvm::StringRef name, swift::SILType type, bool isPrivate) const;
232248

233249
void inlineFunction(BridgedInstruction apply, bool mandatoryInline) const;
234250

include/swift/SILOptimizer/PassManager/PassManager.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ class SwiftPassInvocation {
7878
bool aliveNodeSets[NodeSetCapacity];
7979
int numNodeSetsAllocated = 0;
8080

81+
int numClonersAllocated = 0;
82+
8183
bool needFixStackNesting = false;
8284

8385
void endPassRunChecks();
@@ -136,6 +138,9 @@ class SwiftPassInvocation {
136138

137139
void endTransformFunction();
138140

141+
void notifyNewCloner() { numClonersAllocated++; }
142+
void notifyClonerDestroyed() { numClonersAllocated--; }
143+
139144
void setNeedFixStackNesting(bool newValue) { needFixStackNesting = newValue; }
140145
bool getNeedFixStackNesting() const { return needFixStackNesting; }
141146
};

0 commit comments

Comments
 (0)