Skip to content

Commit afc0f61

Browse files
committed
Optimizer: support statically initialized globals which contain pointers to other globals
For example: ``` var p = Point(x: 10, y: 20) let o = UnsafePointer(&p) ``` Also support outlined arrays with pointers to other globals. For example: ``` var g1 = 1 var g2 = 2 func f() -> [UnsafePointer<Int>] { return [UnsafePointer(&g1), UnsafePointer(&g2)] } ```
1 parent 538d730 commit afc0f61

File tree

10 files changed

+165
-17
lines changed

10 files changed

+165
-17
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/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/GlobalVariable.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,9 @@ extension Instruction {
145145
is ObjectInst,
146146
is ValueToBridgeObjectInst,
147147
is ConvertFunctionInst,
148-
is ThinToThickFunctionInst:
148+
is ThinToThickFunctionInst,
149+
is AddressToPointerInst,
150+
is GlobalAddrInst:
149151
return true
150152
default:
151153
return false

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
}

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+

test/SILOptimizer/init_static_globals.sil

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,13 @@ sil_global [let] @g5 : $TwoFields
9191
sil_global [let] @g6 : $TwoFields
9292
sil_global [let] @g7 : $TwoFields
9393

94+
// CHECK-LABEL: sil_global [let] @g8 : $UnsafePointer<Int32> = {
95+
// CHECK-NEXT: %0 = global_addr @g1 : $*Int32
96+
// CHECK-NEXT: %1 = address_to_pointer %0 : $*Int32 to $Builtin.RawPointer
97+
// CHECK-NEXT: %initval = struct $UnsafePointer<Int32> (%1 : $Builtin.RawPointer)
98+
// CHECK-NEXT: }
99+
sil_global [let] @g8 : $UnsafePointer<Int32>
100+
94101
// CHECK-LABEL: sil [global_init_once_fn] [ossa] @globalinit_trivialglobal_func :
95102
// CHECK-NOT: alloc_global
96103
// CHECK-NOT: store
@@ -246,3 +253,21 @@ bb0:
246253
return %10 : $()
247254
}
248255

256+
// CHECK-LABEL: sil [global_init_once_fn] [ossa] @globalinit_pointer_to_other_global :
257+
// CHECK-NOT: alloc_global
258+
// CHECK-NOT: store
259+
// CHECK: } // end sil function 'globalinit_pointer_to_other_global'
260+
sil [global_init_once_fn] [ossa] @globalinit_pointer_to_other_global : $@convention(c) (Builtin.RawPointer) -> () {
261+
bb0(%0 : $Builtin.RawPointer):
262+
alloc_global @g8
263+
%2 = global_addr @g8 : $*UnsafePointer<Int32>
264+
%3 = global_addr @g1 : $*Int32
265+
%4 = begin_access [read] [dynamic] %3 : $*Int32
266+
%5 = address_to_pointer %4 : $*Int32 to $Builtin.RawPointer
267+
end_access %4 : $*Int32
268+
%7 = struct $UnsafePointer<Int32> (%5 : $Builtin.RawPointer)
269+
store %7 to [trivial] %2 : $*UnsafePointer<Int32>
270+
%8 = tuple ()
271+
return %8 : $()
272+
}
273+

test/SILOptimizer/objectoutliner.sil

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ class Obj : Base {
1313
@_hasStorage var value: Int64
1414
}
1515

16+
sil_global [let] @g1 : $Int32 = {
17+
%0 = integer_literal $Builtin.Int32, 1
18+
%initval = struct $Int32 (%0 : $Builtin.Int32)
19+
}
20+
1621
// CHECK-LABEL: sil_global private @outline_global_simpleTv_ : $Obj = {
1722
// CHECK-NEXT: %0 = integer_literal $Builtin.Int64, 1
1823
// CHECK-NEXT: %1 = struct $Int64 (%0 : $Builtin.Int64)
@@ -47,6 +52,15 @@ class Obj : Base {
4752
// CHECK-NEXT: %initval = object $Obj (%1 : $Int64, [tail_elems] %6 : $(Int64, Bool), %11 : $(Int64, Bool), %13 : $(Int64, Bool))
4853
// CHECK-NEXT: }
4954

55+
// CHECK-LABEL: sil_global private @outline_global_addressTv_ : $Obj = {
56+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int64, 1
57+
// CHECK-NEXT: %1 = struct $Int64 (%0 : $Builtin.Int64)
58+
// CHECK-NEXT: %2 = global_addr @g1 : $*Int32
59+
// CHECK-NEXT: %3 = address_to_pointer %2 : $*Int32 to $Builtin.RawPointer
60+
// CHECK-NEXT: %4 = struct $UnsafePointer<Int32> (%3 : $Builtin.RawPointer)
61+
// CHECK-NEXT: %initval = object $Obj (%1 : $Int64, [tail_elems] %4 : $UnsafePointer<Int32>)
62+
// CHECK-NEXT: }
63+
5064
// CHECK-LABEL: sil @outline_global_simple
5165
// CHECK: [[G:%[0-9]+]] = global_value @outline_global_simpleTv_ : $Obj
5266
// CHECK: strong_retain [[G]] : $Obj
@@ -343,3 +357,29 @@ bb0:
343357
return %33 : $Obj
344358
}
345359

360+
// CHECK-LABEL: sil @outline_global_address :
361+
// CHECK: [[G:%[0-9]+]] = global_value @outline_global_addressTv_ : $Obj
362+
// CHECK: strong_retain [[G]] : $Obj
363+
// CHECK-NOT: store
364+
// CHECK: } // end sil function 'outline_global_address'
365+
sil @outline_global_address : $@convention(thin) () -> () {
366+
bb0:
367+
%0 = integer_literal $Builtin.Word, 1
368+
%1 = integer_literal $Builtin.Int64, 1
369+
%4 = struct $Int64 (%1 : $Builtin.Int64)
370+
%7 = alloc_ref [tail_elems $Int64 * %0 : $Builtin.Word] $Obj
371+
%9 = ref_element_addr %7 : $Obj, #Obj.value
372+
store %4 to %9 : $*Int64
373+
%11 = ref_tail_addr %7 : $Obj, $UnsafePointer<Int32>
374+
%12 = global_addr @g1 : $*Int32
375+
%13 = begin_access [read] [dynamic] %12 : $*Int32
376+
%14 = address_to_pointer %13 : $*Int32 to $Builtin.RawPointer
377+
end_access %13 : $*Int32
378+
%16 = struct $UnsafePointer<Int32> (%14 : $Builtin.RawPointer)
379+
store %16 to %11 : $*UnsafePointer<Int32>
380+
%18 = end_cow_mutation %7 : $Obj
381+
strong_release %18 : $Obj
382+
%r = tuple ()
383+
return %r : $()
384+
}
385+

test/SILOptimizer/performance-annotations.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,11 @@ func testNestedGenericStruct() -> NestedGenericStruct<Int> {
377377
return nestedGeneric
378378
}
379379

380+
var x = 24
381+
let pointerToX = UnsafePointer(&x)
382+
383+
@_noLocks
384+
func testPointerToX() -> UnsafePointer<Int> {
385+
return pointerToX
386+
}
387+

test/SILOptimizer/static_arrays.swift

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,17 @@ func functionArray() -> [(Int) -> Int] {
202202
return [foo, bar, { $0 + 10 }]
203203
}
204204

205+
var g1 = 1
206+
var g2 = 2
207+
208+
// CHECK-LABEL: sil {{.*arrayOfGlobalPointers.*}} : $@convention(thin) () -> @owned Array<UnsafePointer<Int>> {
209+
// CHECK: global_value @{{.*arrayOfGlobalPointers.*}}
210+
// CHECK: } // end sil function '{{.*arrayOfGlobalPointers.*}}'
211+
@inline(never)
212+
public func arrayOfGlobalPointers() -> [UnsafePointer<Int>] {
213+
return [UnsafePointer(&g1), UnsafePointer(&g2)]
214+
}
215+
205216
public struct FStr {
206217
// Not an array, but also tested here.
207218
public static var globalFunc = foo
@@ -239,6 +250,9 @@ func testit() {
239250
let sdict = returnStringDictionary()
240251
// CHECK-OUTPUT-NEXT: sdict 3: 2, 4, 6
241252
print("sdict \(sdict.count): \(sdict["1"]!), \(sdict["3"]!), \(sdict["5"]!)")
253+
254+
// CHECK-OUTPUT-NEXT: globalpointers: [1, 2]
255+
print("globalpointers: \(arrayOfGlobalPointers().map { $0.pointee })")
242256
}
243257

244258
testit()
@@ -253,17 +267,15 @@ func takeUnsafePointer(ptr : UnsafePointer<SwiftClass>, len: Int) {
253267
// This should be a single basic block, and the array should end up being stack
254268
// allocated.
255269
//
256-
// CHECK-LABEL: sil @{{.*}}passArrayOfClasses
257-
// CHECK: bb0(%0 : $SwiftClass, %1 : $SwiftClass, %2 : $SwiftClass):
258-
// CHECK-NOT: bb1(
259-
// CHECK: alloc_ref{{(_dynamic)?}} {{.*}}[tail_elems $SwiftClass *
260-
// CHECK-NOT: bb1(
261-
// CHECK: return
270+
// CHECK-LABEL: sil [noinline] @{{.*passArrayOfClasses.*}} : $@convention(thin) (@guaranteed SwiftClass, @guaranteed SwiftClass, @guaranteed SwiftClass) -> () {
271+
// CHECK: bb0(%0 : $SwiftClass, %1 : $SwiftClass, %2 : $SwiftClass):
272+
// CHECK-NOT: bb1(
273+
// CHECK: alloc_ref{{(_dynamic)?}} {{.*}}[tail_elems $SwiftClass *
274+
// CHECK-NOT: bb1(
275+
// CHECK: } // end sil function '{{.*passArrayOfClasses.*}}'
276+
@inline(never)
262277
public func passArrayOfClasses(a: SwiftClass, b: SwiftClass, c: SwiftClass) {
263278
let arr = [a, b, c]
264279
takeUnsafePointer(ptr: arr, len: arr.count)
265280
}
266281

267-
268-
269-

0 commit comments

Comments
 (0)