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

include/swift/SIL/SILBridging.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ struct BridgedGlobalVar {
367367
}
368368

369369
SWIFT_IMPORT_UNSAFE
370-
inline OptionalBridgedInstruction getStaticInitializerValue() const;
370+
inline OptionalBridgedInstruction getFirstStaticInitInst() const;
371371

372372
bool canBeInitializedStatically() const;
373373

@@ -467,6 +467,9 @@ struct BridgedInstruction {
467467
SWIFT_IMPORT_UNSAFE
468468
inline BridgedBasicBlock getParent() const;
469469

470+
SWIFT_IMPORT_UNSAFE
471+
inline BridgedInstruction getLastInstOfParent() const;
472+
470473
bool isDeleted() const {
471474
return getInst()->isDeleted();
472475
}
@@ -546,6 +549,9 @@ struct BridgedInstruction {
546549
return getAs<swift::BuiltinInst>()->getSubstitutions();
547550
}
548551

552+
bool PointerToAddressInst_isStrict() const {
553+
return getAs<swift::PointerToAddressInst>()->isStrict();
554+
}
549555

550556
bool AddressToPointerInst_needsStackProtection() const {
551557
return getAs<swift::AddressToPointerInst>()->needsStackProtection();
@@ -1413,11 +1419,12 @@ OptionalBridgedBasicBlock BridgedFunction::getLastBlock() const {
14131419
return {getFunction()->empty() ? nullptr : &*getFunction()->rbegin()};
14141420
}
14151421

1416-
OptionalBridgedInstruction BridgedGlobalVar::getStaticInitializerValue() const {
1417-
if (swift::SILInstruction *inst = getGlobal()->getStaticInitializerValue()) {
1418-
return {inst->asSILNode()};
1422+
OptionalBridgedInstruction BridgedGlobalVar::getFirstStaticInitInst() const {
1423+
if (getGlobal()->begin() == getGlobal()->end()) {
1424+
return {nullptr};
14191425
}
1420-
return {nullptr};
1426+
swift::SILInstruction *firstInst = &*getGlobal()->begin();
1427+
return {firstInst->asSILNode()};
14211428
}
14221429

14231430
BridgedInstruction BridgedMultiValueResult::getParent() const {
@@ -1430,6 +1437,10 @@ BridgedBasicBlock BridgedInstruction::getParent() const {
14301437
return {getInst()->getParent()};
14311438
}
14321439

1440+
inline BridgedInstruction BridgedInstruction::getLastInstOfParent() const {
1441+
return {getInst()->getParent()->back().asSILNode()};
1442+
}
1443+
14331444
BridgedSuccessorArray BridgedInstruction::TermInst_getSuccessors() const {
14341445
auto successors = getAs<swift::TermInst>()->getSuccessors();
14351446
return {{successors.data()}, (SwiftInt)successors.size()};

include/swift/SIL/SILGlobalVariable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class SILGlobalVariable
4242
static SwiftMetatype registeredMetatype;
4343

4444
public:
45+
using iterator = SILBasicBlock::iterator;
4546
using const_iterator = SILBasicBlock::const_iterator;
4647

4748
private:
@@ -182,6 +183,8 @@ class SILGlobalVariable
182183

183184
const_iterator begin() const { return StaticInitializerBlock.begin(); }
184185
const_iterator end() const { return StaticInitializerBlock.end(); }
186+
iterator begin() { return StaticInitializerBlock.begin(); }
187+
iterator end() { return StaticInitializerBlock.end(); }
185188

186189
void dropAllReferences() {
187190
StaticInitializerBlock.dropAllReferences();

lib/IRGen/GenConstant.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,20 @@ Explosion irgen::emitConstantValue(IRGenModule &IGM, SILValue operand,
379379
llvm::Type *ty = IGM.getTypeInfo(FRI->getType()).getStorageType();
380380
fnPtr = llvm::ConstantExpr::getBitCast(fnPtr, ty);
381381
return fnPtr;
382+
} else if (auto *gAddr = dyn_cast<GlobalAddrInst>(operand)) {
383+
SILGlobalVariable *var = gAddr->getReferencedGlobal();
384+
auto &ti = IGM.getTypeInfo(var->getLoweredType());
385+
auto expansion = IGM.getResilienceExpansionForLayout(var);
386+
assert(ti.isFixedSize(expansion));
387+
if (ti.isKnownEmpty(expansion)) {
388+
return llvm::ConstantPointerNull::get(IGM.OpaquePtrTy);
389+
}
390+
391+
Address addr = IGM.getAddrOfSILGlobalVariable(var, ti, NotForDefinition);
392+
return addr.getAddress();
393+
} else if (auto *atp = dyn_cast<AddressToPointerInst>(operand)) {
394+
auto *val = emitConstantValue(IGM, atp->getOperand()).claimNextConstant();
395+
return val;
382396
} else {
383397
llvm_unreachable("Unsupported SILInstruction in static initializer!");
384398
}

lib/SILOptimizer/SILCombiner/SILCombine.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,10 @@ void SwiftPassInvocation::eraseInstruction(SILInstruction *inst) {
617617
if (silCombiner) {
618618
silCombiner->eraseInstFromFunction(*inst);
619619
} else {
620-
inst->eraseFromParent();
620+
if (inst->isStaticInitializerInst()) {
621+
inst->getParent()->erase(inst, *getPassManager()->getModule());
622+
} else {
623+
inst->eraseFromParent();
624+
}
621625
}
622626
}

test/SILOptimizer/global_init_with_empty.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ struct Mystruct {
1919
static var structglobal = Mystruct(a: 3, b: Empty(), c: 4)
2020
// CHECK: @{{[^ ]*tupleglobal[^ ]*}} = hidden global <{ %TSi, %TSi }> <{ %TSi <{ i{{[0-9]+}} 5 }>, %TSi <{ i{{[0-9]+}} 6 }> }>
2121
static var tupleglobal = (a: 5, b: Empty(), c: 6)
22+
23+
static var empty: () = ()
24+
static var ptrToEmpty = UnsafePointer(&empty)
2225
}
2326

2427
// CHECK-OUTPUT: 3
@@ -33,3 +36,6 @@ print(Mystruct.tupleglobal.a)
3336
print(Mystruct.tupleglobal.b)
3437
// CHECK-OUTPUT-NEXT: 6
3538
print(Mystruct.tupleglobal.c)
39+
// CHECK-OUTPUT-NEXT: {{0x0+$}}
40+
print(Mystruct.ptrToEmpty)
41+

0 commit comments

Comments
 (0)