Skip to content

Commit cd64d52

Browse files
authored
Merge pull request #67829 from eeckstein/global-addr-in-initializer
Optimizer: support statically initialized globals which contain pointers to other globals
2 parents 7ca5998 + afc0f61 commit cd64d52

20 files changed

+382
-26
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/InitializeStaticGlobals.swift

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ let initializeStaticGlobalsPass = FunctionPass(name: "initialize-static-globals"
6565

6666
_ = cloner.clone(storeToGlobal.source)
6767

68+
// The initial value can contain a `begin_access` if it references another global variable by address, e.g.
69+
// var p = Point(x: 10, y: 20)
70+
// let o = UnsafePointer(&p)
71+
//
72+
allocInst.global.stripAccessInstructionFromInitializer(context)
73+
6874
context.erase(instruction: allocInst)
6975
context.erase(instruction: storeToGlobal)
7076
context.removeTriviallyDeadInstructionsIgnoringDebugUses(in: function)
@@ -94,21 +100,19 @@ private func getGlobalInitialization(of function: Function) -> (allocInst: Alloc
94100
switch inst {
95101
case is ReturnInst,
96102
is DebugValueInst,
97-
is DebugStepInst:
103+
is DebugStepInst,
104+
is BeginAccessInst,
105+
is EndAccessInst:
98106
break
99107
case let agi as AllocGlobalInst:
100108
if allocInst != nil {
101109
return nil
102110
}
103111
allocInst = agi
104112
case let ga as GlobalAddrInst:
105-
if globalAddr != nil {
106-
return nil
107-
}
108-
guard let agi = allocInst, agi.global == ga.global else {
109-
return nil
113+
if let agi = allocInst, agi.global == ga.global {
114+
globalAddr = ga
110115
}
111-
globalAddr = ga
112116
case let si as StoreInst:
113117
if store != nil {
114118
return nil

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ObjectOutliner.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,12 @@ private func constructObject(of allocRef: AllocRefInstBase,
302302
}
303303
}
304304
globalBuilder.createObject(type: allocRef.type, arguments: objectArgs, numBaseElements: storesToClassFields.count)
305+
306+
// The initial value can contain a `begin_access` if it references another global variable by address, e.g.
307+
// var p = Point(x: 10, y: 20)
308+
// let a = [UnsafePointer(&p)]
309+
//
310+
global.stripAccessInstructionFromInitializer(context)
305311
}
306312

307313
private func replace(object allocRef: AllocRefInstBase,
@@ -339,6 +345,9 @@ private extension Value {
339345
guard let svi = self as? SingleValueInstruction else {
340346
return false
341347
}
348+
if let beginAccess = svi as? BeginAccessInst {
349+
return beginAccess.address.isValidGlobalInitValue
350+
}
342351
if !svi.isValidInStaticInitializerOfGlobal {
343352
return false
344353
}

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ swift_compiler_sources(Optimizer
1919
SimplifyInitEnumDataAddr.swift
2020
SimplifyLoad.swift
2121
SimplifyPartialApply.swift
22+
SimplifyPointerToAddress.swift
2223
SimplifyRefCasts.swift
2324
SimplifyRetainReleaseValue.swift
2425
SimplifyStrongRetainRelease.swift
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===--- SimplifyPointerToAddress.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 PointerToAddressInst : OnoneSimplifyable {
16+
17+
/// For a redundant pair of pointer-address conversions, e.g.
18+
///
19+
/// %2 = address_to_pointer %1
20+
/// %3 = pointer_to_address %2 [strict]
21+
///
22+
/// replace all uses of %3 with %1.
23+
///
24+
func simplify(_ context: SimplifyContext) {
25+
if let atp = self.pointer as? AddressToPointerInst,
26+
atp.address.type == self.type,
27+
self.isStrict,
28+
29+
// If the pointer is within an ownership scope, the transformation can break ownership rules, e.g.
30+
// %2 = begin_borrow %1
31+
// %3 = ref_tail_addr %2
32+
// %4 = address_to_pointer %3
33+
// end_borrow %2
34+
// %5 = pointer_to_address %4 <- cannot replace %5 with %3!
35+
//
36+
!atp.address.accessBase.hasLocalOwnershipLifetime
37+
{
38+
self.uses.replaceAll(with: atp.address, context)
39+
}
40+
}
41+
}

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ private extension Value {
225225
var singleUseValue: any Value = self
226226
var path = SmallProjectionPath()
227227
while true {
228-
guard let use = singleUseValue.uses.singleNonDebugUse else {
228+
guard let use = singleUseValue.uses.singleRelevantUse else {
229229
return nil
230230
}
231231

@@ -247,6 +247,8 @@ private extension Value {
247247
default:
248248
return nil
249249
}
250+
case is PointerToAddressInst, is AddressToPointerInst, is BeginAccessInst:
251+
break
250252
default:
251253
return nil
252254
}
@@ -334,3 +336,27 @@ fileprivate struct FunctionWorklist {
334336
}
335337
}
336338
}
339+
340+
private extension UseList {
341+
var singleRelevantUse: Operand? {
342+
var singleUse: Operand?
343+
for use in self {
344+
switch use.instruction {
345+
case is DebugValueInst,
346+
// The initializer value of a global can contain access instructions if it references another
347+
// global variable by address, e.g.
348+
// var p = Point(x: 10, y: 20)
349+
// let o = UnsafePointer(&p)
350+
// Therefore ignore the `end_access` use of a `begin_access`.
351+
is EndAccessInst:
352+
continue
353+
default:
354+
if singleUse != nil {
355+
return nil
356+
}
357+
singleUse = use
358+
}
359+
}
360+
return singleUse
361+
}
362+
}

SwiftCompilerSources/Sources/Optimizer/Utilities/AccessUtils.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,25 @@ enum AccessBase : CustomStringConvertible, Hashable {
131131
return nil
132132
}
133133
}
134+
135+
/// True if this access base may be derived from a reference that is only valid within a locally
136+
/// scoped OSSA lifetime. For example:
137+
///
138+
/// %reference = begin_borrow %1
139+
/// %base = ref_tail_addr %reference <- %base must not be used outside the borrow scope
140+
/// end_borrow %reference
141+
///
142+
/// This is not true for scoped storage such as alloc_stack and @in arguments.
143+
///
144+
var hasLocalOwnershipLifetime: Bool {
145+
if let reference = reference {
146+
// Conservatively assume that everything which is a ref-counted object is within an ownership scope.
147+
// TODO: we could e.g. exclude guaranteed function arguments.
148+
return reference.ownership != .none
149+
}
150+
return false
151+
}
152+
134153
/// True, if the baseAddress is of an immutable property or global variable
135154
var isLet: Bool {
136155
switch self {

SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,3 +431,31 @@ extension FullApplySite {
431431
return false
432432
}
433433
}
434+
435+
extension GlobalVariable {
436+
/// Removes all `begin_access` and `end_access` instructions from the initializer.
437+
///
438+
/// Access instructions are not allowed in the initializer, because the initializer must not contain
439+
/// instructions with side effects (initializer instructions are not executed).
440+
/// Exclusivity checking does not make sense in the initializer.
441+
///
442+
/// The initializer functions of globals, which reference other globals by address, contain access
443+
/// instructions. After the initializing code is copied to the global's initializer, those access
444+
/// instructions must be stripped.
445+
func stripAccessInstructionFromInitializer(_ context: FunctionPassContext) {
446+
guard let initInsts = staticInitializerInstructions else {
447+
return
448+
}
449+
for initInst in initInsts {
450+
switch initInst {
451+
case let beginAccess as BeginAccessInst:
452+
beginAccess.uses.replaceAll(with: beginAccess.address, context)
453+
context.erase(instruction: beginAccess)
454+
case let endAccess as EndAccessInst:
455+
context.erase(instruction: endAccess)
456+
default:
457+
break
458+
}
459+
}
460+
}
461+
}

SwiftCompilerSources/Sources/SIL/BasicBlock.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public struct InstructionList : CollectionLikeSequence, IteratorProtocol {
9797

9898
public func reversed() -> ReverseInstructionList {
9999
if let inst = currentInstruction {
100-
let lastInst = inst.parentBlock.bridged.getLastInst().instruction
100+
let lastInst = inst.bridged.getLastInstOfParent().instruction
101101
return ReverseInstructionList(first: lastInst)
102102
}
103103
return ReverseInstructionList(first: nil)

SwiftCompilerSources/Sources/SIL/GlobalVariable.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,18 @@ final public class GlobalVariable : CustomStringConvertible, HasShortDescription
4343
return bridged.isAvailableExternally()
4444
}
4545

46+
public var staticInitializerInstructions: InstructionList? {
47+
if let firstStaticInitInst = bridged.getFirstStaticInitInst().instruction {
48+
return InstructionList(first: firstStaticInitInst)
49+
}
50+
return nil
51+
}
52+
4653
public var staticInitValue: SingleValueInstruction? {
47-
bridged.getStaticInitializerValue().instruction as? SingleValueInstruction
54+
if let staticInitInsts = staticInitializerInstructions {
55+
return staticInitInsts.reversed().first! as? SingleValueInstruction
56+
}
57+
return nil
4858
}
4959

5060
/// True if the global's linkage and resilience expansion allow the global
@@ -135,7 +145,9 @@ extension Instruction {
135145
is ObjectInst,
136146
is ValueToBridgeObjectInst,
137147
is ConvertFunctionInst,
138-
is ThinToThickFunctionInst:
148+
is ThinToThickFunctionInst,
149+
is AddressToPointerInst,
150+
is GlobalAddrInst:
139151
return true
140152
default:
141153
return false

SwiftCompilerSources/Sources/SIL/Instruction.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ class AddressToPointerInst : SingleValueInstruction, UnaryInstruction {
457457
final public
458458
class PointerToAddressInst : SingleValueInstruction, UnaryInstruction {
459459
public var pointer: Value { operand.value }
460+
public var isStrict: Bool { bridged.PointerToAddressInst_isStrict() }
460461
}
461462

462463
final public

0 commit comments

Comments
 (0)