Skip to content

Commit b73539a

Browse files
committed
IRGen: fix and re-enable static read-only arrays
Static read-only arrays didn't work when passed to ObjectiveC as NSArray. The storage class of static read-only arrays doesn't carry information about the Element type. The new `__SwiftDeferredStaticNSArray` is generic over the element type and doesn't have to rely on the element type information of the array storage.
1 parent 4cf6f79 commit b73539a

File tree

6 files changed

+153
-23
lines changed

6 files changed

+153
-23
lines changed

lib/IRGen/GenConstant.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ llvm::Constant *irgen::emitConstantObject(IRGenModule &IGM, ObjectInst *OI,
294294
// deinit is a no-op and does not actually destroy any elements.
295295
auto *var = new llvm::GlobalVariable(IGM.Module, IGM.TypeMetadataStructTy,
296296
/*constant*/ true, llvm::GlobalValue::ExternalLinkage,
297-
/*initializer*/ nullptr, "$ss19__EmptyArrayStorageCN");
297+
/*initializer*/ nullptr, "$ss20__StaticArrayStorageCN");
298298
IGM.swiftStaticArrayMetadata = var;
299299
}
300300
elts[0] = llvm::ConstantStruct::get(ObjectHeaderTy, {

lib/IRGen/IRGenModule.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,13 +1875,6 @@ bool IRGenModule::shouldPrespecializeGenericMetadata() {
18751875
}
18761876

18771877
bool IRGenModule::canMakeStaticObjectsReadOnly() {
1878-
// Unconditionally disable this until we can fix the metadata.
1879-
// The trick of using the Empty array metadata for static arrays
1880-
// breaks Obj-C interop quite badly.
1881-
// rdar://101126543
1882-
return false;
1883-
1884-
#if 0
18851878
if (getOptions().DisableReadonlyStaticObjects)
18861879
return false;
18871880

@@ -1892,7 +1885,6 @@ bool IRGenModule::canMakeStaticObjectsReadOnly() {
18921885

18931886
return getAvailabilityContext().isContainedIn(
18941887
Context.getImmortalRefCountSymbolsAvailability());
1895-
#endif
18961888
}
18971889

18981890
void IRGenerator::addGenModule(SourceFile *SF, IRGenModule *IGM) {

stdlib/public/core/ContiguousArrayBuffer.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,48 @@ internal final class __EmptyArrayStorage
6262
}
6363
}
6464

65+
/// The storage for static read-only arrays.
66+
///
67+
/// In contrast to `_ContiguousArrayStorage` this class is _not_ generic over
68+
/// the element type, because the metatype for static read-only arrays cannot
69+
/// be instantiated at runtime.
70+
///
71+
/// Static read-only arrays can only contain non-verbatim bridged element types.
72+
@_fixed_layout
73+
@usableFromInline
74+
@_objc_non_lazy_realization
75+
internal final class __StaticArrayStorage
76+
: __ContiguousArrayStorageBase {
77+
78+
@inlinable
79+
@nonobjc
80+
internal init(_doNotCallMe: ()) {
81+
_internalInvariantFailure("creating instance of __StaticArrayStorage")
82+
}
83+
84+
#if _runtime(_ObjC)
85+
override internal func _withVerbatimBridgedUnsafeBuffer<R>(
86+
_ body: (UnsafeBufferPointer<AnyObject>) throws -> R
87+
) rethrows -> R? {
88+
return nil
89+
}
90+
91+
override internal func _getNonVerbatimBridgingBuffer() -> _BridgingBuffer {
92+
fatalError("__StaticArrayStorage._withVerbatimBridgedUnsafeBuffer must not be called")
93+
}
94+
#endif
95+
96+
@inlinable
97+
override internal func canStoreElements(ofDynamicType _: Any.Type) -> Bool {
98+
return false
99+
}
100+
101+
@inlinable
102+
override internal var staticElementType: Any.Type {
103+
fatalError("__StaticArrayStorage.staticElementType must not be called")
104+
}
105+
}
106+
65107
/// The empty array prototype. We use the same object for all empty
66108
/// `[Native]Array<Element>`s.
67109
@inlinable
@@ -827,6 +869,9 @@ internal struct _ContiguousArrayBuffer<Element>: _ArrayBufferProtocol {
827869
}
828870
return _storage
829871
}
872+
if _storage is __StaticArrayStorage {
873+
return __SwiftDeferredStaticNSArray<Element>(_nativeStorage: _storage)
874+
}
830875
return __SwiftDeferredNSArray(_nativeStorage: _storage)
831876
}
832877
#endif

stdlib/public/core/SwiftNativeNSArray.swift

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -329,29 +329,29 @@ extension __SwiftNativeNSArrayWithContiguousStorage {
329329
/// buffers used for Array storage.
330330
@_fixed_layout // FIXME(sil-serialize-all)
331331
@usableFromInline
332-
@objc internal final class __SwiftDeferredNSArray
332+
@objc internal class __SwiftDeferredNSArray
333333
: __SwiftNativeNSArrayWithContiguousStorage {
334334

335335
// This stored property should be stored at offset zero. We perform atomic
336336
// operations on it.
337337
//
338338
// Do not access this property directly.
339339
@nonobjc
340-
internal var _heapBufferBridged_DoNotUse: AnyObject?
340+
internal final var _heapBufferBridged_DoNotUse: AnyObject?
341341

342342
// When this class is allocated inline, this property can become a
343343
// computed one.
344344
@usableFromInline
345345
@nonobjc
346-
internal let _nativeStorage: __ContiguousArrayStorageBase
346+
internal final let _nativeStorage: __ContiguousArrayStorageBase
347347

348348
@nonobjc
349-
internal var _heapBufferBridgedPtr: UnsafeMutablePointer<AnyObject?> {
349+
internal final var _heapBufferBridgedPtr: UnsafeMutablePointer<AnyObject?> {
350350
return _getUnsafePointerToStoredProperties(self).assumingMemoryBound(
351351
to: Optional<AnyObject>.self)
352352
}
353353

354-
internal var _heapBufferBridged: __BridgingBufferStorage? {
354+
internal final var _heapBufferBridged: __BridgingBufferStorage? {
355355
if let ref =
356356
_stdlib_atomicLoadARCRef(object: _heapBufferBridgedPtr) {
357357
return unsafeBitCast(ref, to: __BridgingBufferStorage.self)
@@ -365,7 +365,7 @@ extension __SwiftNativeNSArrayWithContiguousStorage {
365365
self._nativeStorage = _nativeStorage
366366
}
367367

368-
internal func _destroyBridgedStorage(_ hb: __BridgingBufferStorage?) {
368+
internal final func _destroyBridgedStorage(_ hb: __BridgingBufferStorage?) {
369369
if let bridgedStorage = hb {
370370
let buffer = _BridgingBuffer(bridgedStorage)
371371
let count = buffer.count
@@ -422,10 +422,70 @@ extension __SwiftNativeNSArrayWithContiguousStorage {
422422
/// This override allows the count to be read without triggering
423423
/// bridging of array elements.
424424
@objc
425-
internal override var count: Int {
425+
internal override final var count: Int {
426426
return _nativeStorage.countAndCapacity.count
427427
}
428428
}
429+
430+
/// A `__SwiftDeferredNSArray` which is used for static read-only Swift Arrays.
431+
///
432+
/// In contrast to it's base class, `__SwiftDeferredStaticNSArray` is generic
433+
/// over the element type. This is needed because the storage class of a static
434+
/// read-only array does _not_ provide the element type.
435+
internal final class __SwiftDeferredStaticNSArray<Element>
436+
: __SwiftDeferredNSArray {
437+
438+
internal override func withUnsafeBufferOfObjects<R>(
439+
_ body: (UnsafeBufferPointer<AnyObject>) throws -> R
440+
) rethrows -> R {
441+
while true {
442+
var buffer: UnsafeBufferPointer<AnyObject>
443+
444+
// If we've already got a buffer of bridged objects, just use it
445+
if let bridgedStorage = _heapBufferBridged {
446+
let bridgingBuffer = _BridgingBuffer(bridgedStorage)
447+
buffer = UnsafeBufferPointer(
448+
start: bridgingBuffer.baseAddress, count: bridgingBuffer.count)
449+
}
450+
else {
451+
// Static read-only arrays can only contain non-verbatim bridged
452+
// element types.
453+
454+
// Create buffer of bridged objects.
455+
let objects = getNonVerbatimBridgingBuffer()
456+
457+
// Atomically store a reference to that buffer in self.
458+
if !_stdlib_atomicInitializeARCRef(
459+
object: _heapBufferBridgedPtr, desired: objects.storage!) {
460+
461+
// Another thread won the race. Throw out our buffer.
462+
_destroyBridgedStorage(
463+
unsafeDowncast(objects.storage!, to: __BridgingBufferStorage.self))
464+
}
465+
continue // Try again
466+
}
467+
468+
defer { _fixLifetime(self) }
469+
return try body(buffer)
470+
}
471+
}
472+
473+
internal func getNonVerbatimBridgingBuffer() -> _BridgingBuffer {
474+
_internalInvariant(
475+
!_isBridgedVerbatimToObjectiveC(Element.self),
476+
"Verbatim bridging should be handled separately")
477+
let count = _nativeStorage.countAndCapacity.count
478+
let result = _BridgingBuffer(count)
479+
let resultPtr = result.baseAddress
480+
let p = UnsafeMutablePointer<Element>(Builtin.projectTailElems(_nativeStorage, Element.self))
481+
for i in 0..<count {
482+
(resultPtr + i).initialize(to: _bridgeAnythingToObjectiveC(p[i]))
483+
}
484+
_fixLifetime(self)
485+
return result
486+
}
487+
}
488+
429489
#else
430490
// Empty shim version for non-objc platforms.
431491
@usableFromInline

test/SILOptimizer/readonly_arrays.swift

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,19 @@
99

1010
// Check if the optimizer is able to convert array literals to constant statically initialized arrays.
1111

12-
// CHECK: @"$s4test11arrayLookupyS2iFTv_r" = {{.*}} constant {{.*}} @"$ss19__EmptyArrayStorageCN", {{.*}} @_swiftImmortalRefCount
13-
// CHECK: @"$s4test11returnArraySaySiGyFTv_r" = {{.*}} constant {{.*}} @"$ss19__EmptyArrayStorageCN", {{.*}} @_swiftImmortalRefCount
14-
// CHECK: @"$s4test9passArrayyyFTv_r" = {{.*}} constant {{.*}} @"$ss19__EmptyArrayStorageCN", {{.*}} @_swiftImmortalRefCount
15-
// CHECK: @"$s4test9passArrayyyFTv0_r" = {{.*}} constant {{.*}} @"$ss19__EmptyArrayStorageCN", {{.*}} @_swiftImmortalRefCount
16-
// CHECK: @"$s4test10storeArrayyyFTv_r" = {{.*}} constant {{.*}} @"$ss19__EmptyArrayStorageCN", {{.*}} @_swiftImmortalRefCount
17-
// CHECK: @"$s4test3StrV14staticVariable_WZTv_r" = {{.*}} constant {{.*}} @"$ss19__EmptyArrayStorageCN", {{.*}} @_swiftImmortalRefCount
12+
// CHECK: @"$s4test11arrayLookupyS2iFTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} @_swiftImmortalRefCount
13+
// CHECK: @"$s4test11returnArraySaySiGyFTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} @_swiftImmortalRefCount
14+
// CHECK: @"$s4test9passArrayyyFTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} @_swiftImmortalRefCount
15+
// CHECK: @"$s4test9passArrayyyFTv0_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} @_swiftImmortalRefCount
16+
// CHECK: @"$s4test10storeArrayyyFTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} @_swiftImmortalRefCount
17+
// CHECK: @"$s4test3StrV14staticVariable_WZTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} @_swiftImmortalRefCount
1818
// CHECK-NOT: swift_initStaticObject
1919

2020
// UNSUPPORTED: use_os_stdlib
2121

2222
// Currently, constant static arrays only work on Darwin platforms.
2323
// REQUIRES: VENDOR=apple
2424

25-
// REQUIRES: rdar101126543
2625

2726
public struct Str {
2827
public static let staticVariable = [ 200, 201, 202 ]
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -target %target-future-triple -O %s -o %t/a.out
3+
// RUN: %target-run %t/a.out | %FileCheck %s
4+
5+
// REQUIRES: executable_test,swift_stdlib_no_asserts,optimized_stdlib
6+
// REQUIRES: objc_interop
7+
8+
// UNSUPPORTED: use_os_stdlib
9+
10+
import Foundation
11+
12+
@inline(never)
13+
func createArray() -> [Int] {
14+
return [1, 2, 3]
15+
}
16+
17+
@inline(never)
18+
func printNSArray(_ a: NSArray) {
19+
// CHECK: 1
20+
print(a[0])
21+
// CHECK: 2
22+
print(a[1])
23+
// CHECK: 3
24+
print(a[2])
25+
}
26+
27+
28+
@inline(never)
29+
func testit() {
30+
let a = createArray()
31+
printNSArray(a as NSArray)
32+
}
33+
34+
testit()

0 commit comments

Comments
 (0)