Skip to content

IRGen: fix and re-enable static read-only arrays #62226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -949,9 +949,9 @@ class ASTContext final {
/// for extended existential types.
AvailabilityContext getParameterizedExistentialRuntimeAvailability();

/// Get the runtime availability of immortal ref-count symbols, which are
/// needed to place array buffers into constant data sections.
AvailabilityContext getImmortalRefCountSymbolsAvailability();
/// Get the runtime availability of array buffers placed in constant data
/// sections.
AvailabilityContext getStaticReadOnlyArraysAvailability();

/// Get the runtime availability of runtime functions for
/// variadic generic types.
Expand Down
6 changes: 2 additions & 4 deletions lib/AST/Availability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -567,10 +567,8 @@ ASTContext::getParameterizedExistentialRuntimeAvailability() {
}

AvailabilityContext
ASTContext::getImmortalRefCountSymbolsAvailability() {
// TODO: replace this with a concrete swift version once we have it.
// rdar://94185998
return getSwiftFutureAvailability();
ASTContext::getStaticReadOnlyArraysAvailability() {
return getSwift511Availability();
}

AvailabilityContext
Expand Down
7 changes: 2 additions & 5 deletions lib/IRGen/GenConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,13 +438,10 @@ llvm::Constant *irgen::emitConstantObject(IRGenModule &IGM, ObjectInst *OI,
IGM.swiftImmortalRefCount = var;
}
if (!IGM.swiftStaticArrayMetadata) {

// Static arrays can only contain trivial elements. Therefore we can reuse
// the metadata of the empty array buffer. The important thing is that its
// deinit is a no-op and does not actually destroy any elements.
// type metadata for class __StaticArrayStorage
auto *var = new llvm::GlobalVariable(IGM.Module, IGM.TypeMetadataStructTy,
/*constant*/ true, llvm::GlobalValue::ExternalLinkage,
/*initializer*/ nullptr, "$ss19__EmptyArrayStorageCN");
/*initializer*/ nullptr, "$ss20__StaticArrayStorageCN");
IGM.swiftStaticArrayMetadata = var;
}
elements[0].add(llvm::ConstantStruct::get(ObjectHeaderTy, {
Expand Down
10 changes: 1 addition & 9 deletions lib/IRGen/IRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2038,13 +2038,6 @@ bool IRGenModule::canUseObjCSymbolicReferences() {
}

bool IRGenModule::canMakeStaticObjectsReadOnly() {
// Unconditionally disable this until we can fix the metadata.
// The trick of using the Empty array metadata for static arrays
// breaks Obj-C interop quite badly.
// rdar://101126543
return false;

#if 0
if (getOptions().DisableReadonlyStaticObjects)
return false;

Expand All @@ -2054,8 +2047,7 @@ bool IRGenModule::canMakeStaticObjectsReadOnly() {
return false;

return getAvailabilityContext().isContainedIn(
Context.getImmortalRefCountSymbolsAvailability());
#endif
Context.getStaticReadOnlyArraysAvailability());
}

void IRGenerator::addGenModule(SourceFile *SF, IRGenModule *IGM) {
Expand Down
46 changes: 46 additions & 0 deletions stdlib/public/core/ContiguousArrayBuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,49 @@ public var _swiftEmptyArrayStorage: (Int, Int, Int, Int) =
(/*isa*/0, /*refcount*/-1, /*count*/0, /*flags*/1)
#endif

/// The storage for static read-only arrays.
///
/// In contrast to `_ContiguousArrayStorage` this class is _not_ generic over
/// the element type, because the metatype for static read-only arrays cannot
/// be instantiated at runtime.
///
/// Static read-only arrays can only contain non-verbatim bridged element types.
@_fixed_layout
@usableFromInline
@_objc_non_lazy_realization
internal final class __StaticArrayStorage
: __ContiguousArrayStorageBase {

@inlinable
@nonobjc
internal init(_doNotCallMe: ()) {
_internalInvariantFailure("creating instance of __StaticArrayStorage")
}

#if _runtime(_ObjC)
override internal func _withVerbatimBridgedUnsafeBuffer<R>(
_ body: (UnsafeBufferPointer<AnyObject>) throws -> R
) rethrows -> R? {
return nil
}

override internal func _getNonVerbatimBridgingBuffer() -> _BridgingBuffer {
fatalError("__StaticArrayStorage._withVerbatimBridgedUnsafeBuffer must not be called")
}
#endif

@inlinable
override internal func canStoreElements(ofDynamicType _: Any.Type) -> Bool {
return false
}

@inlinable
@_unavailableInEmbedded
override internal var staticElementType: Any.Type {
fatalError("__StaticArrayStorage.staticElementType must not be called")
}
}

/// The empty array prototype. We use the same object for all empty
/// `[Native]Array<Element>`s.
@inlinable
Expand Down Expand Up @@ -849,6 +892,9 @@ internal struct _ContiguousArrayBuffer<Element>: _ArrayBufferProtocol {
}
return _storage
}
if _storage is __StaticArrayStorage {
return __SwiftDeferredStaticNSArray<Element>(_nativeStorage: _storage)
}
return __SwiftDeferredNSArray(_nativeStorage: _storage)
}
#endif
Expand Down
75 changes: 68 additions & 7 deletions stdlib/public/core/SwiftNativeNSArray.swift
Original file line number Diff line number Diff line change
Expand Up @@ -329,29 +329,29 @@ extension __SwiftNativeNSArrayWithContiguousStorage {
/// buffers used for Array storage.
@_fixed_layout // FIXME(sil-serialize-all)
@usableFromInline
@objc internal final class __SwiftDeferredNSArray
@objc internal class __SwiftDeferredNSArray
: __SwiftNativeNSArrayWithContiguousStorage {

// This stored property should be stored at offset zero. We perform atomic
// operations on it.
//
// Do not access this property directly.
@nonobjc
internal var _heapBufferBridged_DoNotUse: AnyObject?
internal final var _heapBufferBridged_DoNotUse: AnyObject?

// When this class is allocated inline, this property can become a
// computed one.
@usableFromInline
@nonobjc
internal let _nativeStorage: __ContiguousArrayStorageBase
internal final let _nativeStorage: __ContiguousArrayStorageBase

@nonobjc
internal var _heapBufferBridgedPtr: UnsafeMutablePointer<AnyObject?> {
internal final var _heapBufferBridgedPtr: UnsafeMutablePointer<AnyObject?> {
return _getUnsafePointerToStoredProperties(self).assumingMemoryBound(
to: Optional<AnyObject>.self)
}

internal var _heapBufferBridged: __BridgingBufferStorage? {
internal final var _heapBufferBridged: __BridgingBufferStorage? {
if let ref =
_stdlib_atomicLoadARCRef(object: _heapBufferBridgedPtr) {
return unsafeBitCast(ref, to: __BridgingBufferStorage.self)
Expand All @@ -365,7 +365,7 @@ extension __SwiftNativeNSArrayWithContiguousStorage {
self._nativeStorage = _nativeStorage
}

internal func _destroyBridgedStorage(_ hb: __BridgingBufferStorage?) {
internal final func _destroyBridgedStorage(_ hb: __BridgingBufferStorage?) {
if let bridgedStorage = hb {
withExtendedLifetime(bridgedStorage) {
let buffer = _BridgingBuffer(bridgedStorage)
Expand Down Expand Up @@ -424,10 +424,71 @@ extension __SwiftNativeNSArrayWithContiguousStorage {
/// This override allows the count to be read without triggering
/// bridging of array elements.
@objc
internal override var count: Int {
internal override final var count: Int {
return _nativeStorage.countAndCapacity.count
}
}

/// A `__SwiftDeferredNSArray` which is used for static read-only Swift Arrays.
///
/// In contrast to its base class, `__SwiftDeferredStaticNSArray` is generic
/// over the element type. This is needed because the storage class of a static
/// read-only array (`__StaticArrayStorage`) does _not_ provide the element
/// type.
internal final class __SwiftDeferredStaticNSArray<Element>
: __SwiftDeferredNSArray {

internal override func withUnsafeBufferOfObjects<R>(
_ body: (UnsafeBufferPointer<AnyObject>) throws -> R
) rethrows -> R {
while true {
var buffer: UnsafeBufferPointer<AnyObject>

// If we've already got a buffer of bridged objects, just use it
if let bridgedStorage = _heapBufferBridged {
let bridgingBuffer = _BridgingBuffer(bridgedStorage)
buffer = UnsafeBufferPointer(
start: bridgingBuffer.baseAddress, count: bridgingBuffer.count)
}
else {
// Static read-only arrays can only contain non-verbatim bridged
// element types.

// Create buffer of bridged objects.
let objects = getNonVerbatimBridgingBuffer()

// Atomically store a reference to that buffer in self.
if !_stdlib_atomicInitializeARCRef(
object: _heapBufferBridgedPtr, desired: objects.storage!) {

// Another thread won the race. Throw out our buffer.
_destroyBridgedStorage(
unsafeDowncast(objects.storage!, to: __BridgingBufferStorage.self))
}
continue // Try again
}

defer { _fixLifetime(self) }
return try body(buffer)
}
}

internal func getNonVerbatimBridgingBuffer() -> _BridgingBuffer {
_internalInvariant(
!_isBridgedVerbatimToObjectiveC(Element.self),
"Verbatim bridging should be handled separately")
let count = _nativeStorage.countAndCapacity.count
let result = _BridgingBuffer(count)
let resultPtr = result.baseAddress
let p = UnsafeMutablePointer<Element>(Builtin.projectTailElems(_nativeStorage, Element.self))
for i in 0..<count {
(resultPtr + i).initialize(to: _bridgeAnythingToObjectiveC(p[i]))
}
_fixLifetime(self)
return result
}
}

#else
// Empty shim version for non-objc platforms.
@usableFromInline
Expand Down
6 changes: 2 additions & 4 deletions stdlib/public/stubs/GlobalObjects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ swift::_SwiftEmptyArrayStorage swift::_swiftEmptyArrayStorage = {
}
};

// Define required symbols to be used by constant static arrays.
// * `__swiftImmortalRefCount`: The bit pattern for the ref-count field of
// the array buffer.
// * `__swiftStaticArrayMetadata`: The isa-pointer for the array buffer.
// Define `__swiftImmortalRefCount` which is used by constant static arrays.
// It is the bit pattern for the ref-count field of the array buffer.
//
// TODO: Support constant static arrays on other platforms, too.
// This needs a bit more work because the tricks with absolute symbols and
Expand Down
13 changes: 6 additions & 7 deletions test/SILOptimizer/readonly_arrays.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,19 @@

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

// CHECK: @"$s4test11arrayLookupyS2iFTv_r" = {{.*}} constant {{.*}} @"$ss19__EmptyArrayStorageCN", {{.*}} @_swiftImmortalRefCount
// CHECK: @"$s4test11returnArraySaySiGyFTv_r" = {{.*}} constant {{.*}} @"$ss19__EmptyArrayStorageCN", {{.*}} @_swiftImmortalRefCount
// CHECK: @"$s4test9passArrayyyFTv_r" = {{.*}} constant {{.*}} @"$ss19__EmptyArrayStorageCN", {{.*}} @_swiftImmortalRefCount
// CHECK: @"$s4test9passArrayyyFTv0_r" = {{.*}} constant {{.*}} @"$ss19__EmptyArrayStorageCN", {{.*}} @_swiftImmortalRefCount
// CHECK: @"$s4test10storeArrayyyFTv_r" = {{.*}} constant {{.*}} @"$ss19__EmptyArrayStorageCN", {{.*}} @_swiftImmortalRefCount
// CHECK: @"$s4test3StrV14staticVariable_WZTv_r" = {{.*}} constant {{.*}} @"$ss19__EmptyArrayStorageCN", {{.*}} @_swiftImmortalRefCount
// CHECK-DAG: @"$s4test11arrayLookupyS2iFTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} @_swiftImmortalRefCount
// CHECK-DAG: @"$s4test11returnArraySaySiGyFTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} @_swiftImmortalRefCount
// CHECK-DAG: @"$s4test9passArrayyyFTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} @_swiftImmortalRefCount
// CHECK-DAG: @"$s4test9passArrayyyFTv0_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} @_swiftImmortalRefCount
// CHECK-DAG: @"$s4test10storeArrayyyFTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} @_swiftImmortalRefCount
// CHECK-DAG: @"$s4test3StrV14staticVariable_WZTv_r" = {{.*}} constant {{.*}} @"$ss20__StaticArrayStorageCN", {{.*}} @_swiftImmortalRefCount
// CHECK-NOT: swift_initStaticObject

// UNSUPPORTED: use_os_stdlib

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

// REQUIRES: rdar101126543

public struct Str {
public static let staticVariable = [ 200, 201, 202 ]
Expand Down
34 changes: 34 additions & 0 deletions test/SILOptimizer/readonly_arrays_objc.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -target %target-future-triple -O %s -o %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s

// REQUIRES: executable_test,swift_stdlib_no_asserts,optimized_stdlib
// REQUIRES: objc_interop

// UNSUPPORTED: use_os_stdlib

import Foundation

@inline(never)
func createArray() -> [Int] {
return [1, 2, 3]
}

@inline(never)
func printNSArray(_ a: NSArray) {
// CHECK: 1
print(a[0])
// CHECK: 2
print(a[1])
// CHECK: 3
print(a[2])
}


@inline(never)
func testit() {
let a = createArray()
printNSArray(a as NSArray)
}

testit()
19 changes: 19 additions & 0 deletions test/abi/macOS/arm64/stdlib-asserts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,23 @@

// Standard Library Symbols

// class __StaticArrayStorage
Added: _$ss20__StaticArrayStorageC12_doNotCallMeAByt_tcfC
Added: _$ss20__StaticArrayStorageC12_doNotCallMeAByt_tcfCTj
Added: _$ss20__StaticArrayStorageC12_doNotCallMeAByt_tcfCTq
Added: _$ss20__StaticArrayStorageC12_doNotCallMeAByt_tcfc
Added: _$ss20__StaticArrayStorageC16_doNotCallMeBaseAByt_tcfC
Added: _$ss20__StaticArrayStorageC16_doNotCallMeBaseAByt_tcfc
Added: _$ss20__StaticArrayStorageC16canStoreElements13ofDynamicTypeSbypXp_tF
Added: _$ss20__StaticArrayStorageC17staticElementTypeypXpvg
Added: _$ss20__StaticArrayStorageCMa
Added: _$ss20__StaticArrayStorageCMn
Added: _$ss20__StaticArrayStorageCMo
Added: _$ss20__StaticArrayStorageCMu
Added: _$ss20__StaticArrayStorageCN
Added: _$ss20__StaticArrayStorageCfD
Added: _$ss20__StaticArrayStorageCfd
Added: _OBJC_CLASS_$__TtCs20__StaticArrayStorage
Added: _OBJC_METACLASS_$__TtCs20__StaticArrayStorage

// Runtime Symbols
19 changes: 19 additions & 0 deletions test/abi/macOS/arm64/stdlib.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,23 @@ Added: _$ss19_getWeakRetainCountySuyXlF
// Swift._getUnownedRetainCount(Swift.AnyObject) -> Swift.UInt
Added: _$ss22_getUnownedRetainCountySuyXlF

// class __StaticArrayStorage
Added: _$ss20__StaticArrayStorageC12_doNotCallMeAByt_tcfC
Added: _$ss20__StaticArrayStorageC12_doNotCallMeAByt_tcfCTj
Added: _$ss20__StaticArrayStorageC12_doNotCallMeAByt_tcfCTq
Added: _$ss20__StaticArrayStorageC12_doNotCallMeAByt_tcfc
Added: _$ss20__StaticArrayStorageC16_doNotCallMeBaseAByt_tcfC
Added: _$ss20__StaticArrayStorageC16_doNotCallMeBaseAByt_tcfc
Added: _$ss20__StaticArrayStorageC16canStoreElements13ofDynamicTypeSbypXp_tF
Added: _$ss20__StaticArrayStorageC17staticElementTypeypXpvg
Added: _$ss20__StaticArrayStorageCMa
Added: _$ss20__StaticArrayStorageCMn
Added: _$ss20__StaticArrayStorageCMo
Added: _$ss20__StaticArrayStorageCMu
Added: _$ss20__StaticArrayStorageCN
Added: _$ss20__StaticArrayStorageCfD
Added: _$ss20__StaticArrayStorageCfd
Added: _OBJC_CLASS_$__TtCs20__StaticArrayStorage
Added: _OBJC_METACLASS_$__TtCs20__StaticArrayStorage

// Runtime Symbols
19 changes: 19 additions & 0 deletions test/abi/macOS/x86_64/stdlib-asserts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,23 @@

// Standard Library Symbols

// class __StaticArrayStorage
Added: _$ss20__StaticArrayStorageC12_doNotCallMeAByt_tcfC
Added: _$ss20__StaticArrayStorageC12_doNotCallMeAByt_tcfCTj
Added: _$ss20__StaticArrayStorageC12_doNotCallMeAByt_tcfCTq
Added: _$ss20__StaticArrayStorageC12_doNotCallMeAByt_tcfc
Added: _$ss20__StaticArrayStorageC16_doNotCallMeBaseAByt_tcfC
Added: _$ss20__StaticArrayStorageC16_doNotCallMeBaseAByt_tcfc
Added: _$ss20__StaticArrayStorageC16canStoreElements13ofDynamicTypeSbypXp_tF
Added: _$ss20__StaticArrayStorageC17staticElementTypeypXpvg
Added: _$ss20__StaticArrayStorageCMa
Added: _$ss20__StaticArrayStorageCMn
Added: _$ss20__StaticArrayStorageCMo
Added: _$ss20__StaticArrayStorageCMu
Added: _$ss20__StaticArrayStorageCN
Added: _$ss20__StaticArrayStorageCfD
Added: _$ss20__StaticArrayStorageCfd
Added: _OBJC_CLASS_$__TtCs20__StaticArrayStorage
Added: _OBJC_METACLASS_$__TtCs20__StaticArrayStorage

// Runtime Symbols
Loading