From 0f41feffbcab5ce6d93ce92cf9261a150fd0b791 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Tue, 9 Jan 2018 22:24:44 +0000 Subject: [PATCH] Support for 32-bit platforms (with `AnyObject?` for now) (#15) * Restore support for 32-bit platforms On 32-bit platforms, add an extra 32-bit word to both _StringObject and _StringGuts for padding and metadata bit storage. Padding _StringObject rather than adding two extra words to _StringGuts makes for unattractively discontiguous inline storage. However, _StringObject needs 2 more metadata bits than are available in a 32-bit BridgeObject; shoving an extra word in there was easier than a full code reorganization. To make use of all this luxurious extra space, on 32-bit platforms, _StringGuts._otherBits always contains the count for contiguous strings, while the new _StringGuts._extraBits stored property contains the start address. This makes extracting an unmanaged view a trivial operation. * [runtime] SwiftObject, SwiftValue: Simplify -description implementation Previously, these classes implemented -description and -debugDescription by 1) calling _getSummary in the stdlib to get the Swift String description corresponding to the value, and then 2) calling _convertStringToNSString in the Foundation overlay to convert the resulting String into an NSString. To hold the Swift String value between the two calls, SwiftObject defined a dummy C++ struct whose layout is supposed match that of Swift Strings. Instead of updating this struct to the new String representation, this commit collapses the two stages above into a single call into a new generic function, _getDescription (defined in the Foundation overlay), which gets rid of this maintenance chore. * [runtime] Reflection: Update String representation to follow stdlib changes * _StringObject: Replace _BridgeObject with AnyObject? on 32-bit platforms Also, don't store any tagged value in _StringObject on 32-bit -- with AnyObject taking up a full word now, we only have 28 bits available (30 tops), which is not enough to store either a count nor a start address. * LibcShims: Adjust _swift_stdlib_print_hex for 32-bit platforms. * [test] Update 32-bit reflection tests Previous (speculative) update missed a couple of offset/size changes. * [test] IRGen/lazy_multi_file.swift: Allow for the possibility of an sret param before self. String is 4 words long on 32 bit platforms, which gets returned via sret on i386. * [test] DebugInfo/self.swift: Don't expect self to be alloca'd first On i386, space for a String gets allocated before the self variable. * _StringGuts: Add _RawBitPattern typealias * _StringGuts: Fix an overlapping access warning * _StringVariant: Remove IndexDistance condition * [runtime] Update mangled symbol --- stdlib/public/SDK/Foundation/NSString.swift | 4 +- stdlib/public/core/ReflectionLegacy.swift | 10 - stdlib/public/core/StringComparable.swift | 39 +-- stdlib/public/core/StringGuts.swift | 207 +++++++++--- stdlib/public/core/StringObject.swift | 301 +++++++++++++++--- stdlib/public/core/StringVariant.swift | 1 - stdlib/public/runtime/Reflection.mm | 5 +- stdlib/public/runtime/SwiftObject.h | 9 +- stdlib/public/runtime/SwiftObject.mm | 31 +- stdlib/public/runtime/SwiftValue.mm | 6 +- stdlib/public/stubs/LibcShims.cpp | 10 +- test/DebugInfo/self.swift | 2 +- .../Reflection/reflect_String.swift | 4 +- .../Reflection/reflect_multiple_types.swift | 8 +- 14 files changed, 470 insertions(+), 167 deletions(-) diff --git a/stdlib/public/SDK/Foundation/NSString.swift b/stdlib/public/SDK/Foundation/NSString.swift index fbbe27e029eef..4a98a707e9656 100644 --- a/stdlib/public/SDK/Foundation/NSString.swift +++ b/stdlib/public/SDK/Foundation/NSString.swift @@ -23,8 +23,8 @@ public class NSSimpleCString {} public class NSConstantString {} // Called by the SwiftObject implementation. -public func _convertStringToNSString(_ string: String) -> NSString { - return string._bridgeToObjectiveC() +public func _getDescription(_ x: T) -> NSString { + return String(reflecting: x)._bridgeToObjectiveC() } extension NSString : ExpressibleByStringLiteral { diff --git a/stdlib/public/core/ReflectionLegacy.swift b/stdlib/public/core/ReflectionLegacy.swift index 5cf967f074a9c..ce562ce52f342 100644 --- a/stdlib/public/core/ReflectionLegacy.swift +++ b/stdlib/public/core/ReflectionLegacy.swift @@ -66,16 +66,6 @@ public protocol _Mirror { var disposition: _MirrorDisposition { get } } -/// An entry point that can be called from C++ code to get the summary string -/// for an arbitrary object. The memory pointed to by "out" is initialized with -/// the summary string. -@_inlineable // FIXME(sil-serialize-all) -@_silgen_name("swift_getSummary") -public // COMPILER_INTRINSIC -func _getSummary(_ out: UnsafeMutablePointer, x: T) { - out.initialize(to: String(reflecting: x)) -} - /// Produce a mirror for any value. The runtime produces a mirror that /// structurally reflects values of any type. @_inlineable // FIXME(sil-serialize-all) diff --git a/stdlib/public/core/StringComparable.swift b/stdlib/public/core/StringComparable.swift index bbad98fa6e762..326d573268327 100644 --- a/stdlib/public/core/StringComparable.swift +++ b/stdlib/public/core/StringComparable.swift @@ -80,15 +80,11 @@ extension _StringGuts { @effects(readonly) public static func _compareDeterministicUnicodeCollation( - _leftUnsafeStringGutsBitPattern leftBits: (UInt, UInt), - _rightUnsafeStringGutsBitPattern rightBits: (UInt, UInt) + _leftUnsafeStringGutsBitPattern leftBits: _RawBitPattern, + _rightUnsafeStringGutsBitPattern rightBits: _RawBitPattern ) -> Int { - let left = _StringGuts( - object: _StringObject(rawBits: leftBits.0), - otherBits: leftBits.1) - let right = _StringGuts( - object: _StringObject(rawBits: rightBits.0), - otherBits: rightBits.1) + let left = _StringGuts(rawBits: leftBits) + let right = _StringGuts(rawBits: rightBits) return _compareDeterministicUnicodeCollation( left, 0.., - _rightUnsafeStringGutsBitPattern rightBits: (UInt, UInt), + _rightUnsafeStringGutsBitPattern rightBits: _RawBitPattern, _ rightRange: Range ) -> Int { - let left = _StringGuts( - object: _StringObject(rawBits: leftBits.0), - otherBits: leftBits.1) - let right = _StringGuts( - object: _StringObject(rawBits: rightBits.0), - otherBits: rightBits.1) + let left = _StringGuts(rawBits: leftBits) + let right = _StringGuts(rawBits: rightBits) return _compareDeterministicUnicodeCollation( left, leftRange, to: right, rightRange) } @@ -165,8 +157,7 @@ extension _StringGuts { @inline(__always) @_inlineable public func _bitwiseEqualTo(_ other: _StringGuts) -> Bool { - return self._object.rawBits == other._object.rawBits - && self._otherBits == other._otherBits + return self.rawBits == other.rawBits } @_inlineable @@ -232,11 +223,9 @@ extension _StringGuts { return result } #endif - let leftBits = (left._object.rawBits, left._otherBits) - let rightBits = (right._object.rawBits, right._otherBits) return _compareDeterministicUnicodeCollation( - _leftUnsafeStringGutsBitPattern: leftBits, leftRange, - _rightUnsafeStringGutsBitPattern: rightBits, rightRange) + _leftUnsafeStringGutsBitPattern: left.rawBits, leftRange, + _rightUnsafeStringGutsBitPattern: right.rawBits, rightRange) } @_inlineable @@ -259,11 +248,9 @@ extension _StringGuts { return result } #endif - let leftBits = (left._object.rawBits, left._otherBits) - let rightBits = (right._object.rawBits, right._otherBits) return _compareDeterministicUnicodeCollation( - _leftUnsafeStringGutsBitPattern: leftBits, - _rightUnsafeStringGutsBitPattern: rightBits) + _leftUnsafeStringGutsBitPattern: left.rawBits, + _rightUnsafeStringGutsBitPattern: right.rawBits) } } diff --git a/stdlib/public/core/StringGuts.swift b/stdlib/public/core/StringGuts.swift index 2d6ef526ff1c4..52b55ad43c283 100644 --- a/stdlib/public/core/StringGuts.swift +++ b/stdlib/public/core/StringGuts.swift @@ -23,31 +23,67 @@ import SwiftShims @_fixed_layout public // FIXME struct _StringGuts { - // TODO 32-bit: Insert padding between fields public // FIXME for testing only - var _storage: (_StringObject, UInt) + var _object: _StringObject + public // FIXME for testing only + var _otherBits: UInt // (Mostly) count or inline storage + +#if arch(i386) || arch(arm) + public // FIXME for testing only + var _extraBits: UInt // Start address or inline storage +#endif + +#if arch(i386) || arch(arm) @_inlineable + @inline(__always) public - var _object: _StringObject { - @inline(__always) get { return _storage.0 } - @inline(__always) set { _storage.0 = newValue } + init(object: _StringObject, otherBits: UInt, extraBits: UInt) { + self._object = object + self._otherBits = otherBits + self._extraBits = extraBits + _invariantCheck() } - @_inlineable - public - var _otherBits: UInt { - @inline(__always) get { return _storage.1 } - @inline(__always) set { _storage.1 = newValue } - } - +#else @_inlineable @inline(__always) public init(object: _StringObject, otherBits: UInt) { - self._storage.0 = object - self._storage.1 = otherBits + self._object = object + self._otherBits = otherBits _invariantCheck() } +#endif + + public typealias _RawBitPattern = (UInt64, UInt64) + + @_versioned + @_inlineable + internal var rawBits: _RawBitPattern { + @inline(__always) + get { +#if arch(i386) || arch(arm) + return (_object.rawBits, + UInt64(truncatingIfNeeded: _extraBits) &<< 32 | + UInt64(truncatingIfNeeded: _otherBits)) +#else + return (_object.rawBits, UInt64(truncatingIfNeeded: _otherBits)) +#endif + } + } + + init(rawBits: _RawBitPattern) { +#if arch(i386) || arch(arm) + self.init( + object: _StringObject(rawBits: rawBits.0), + otherBits: UInt(truncatingIfNeeded: rawBits.1), + extraBits: UInt(truncatingIfNeeded: rawBits.1 &>> 32)) +#else + self.init( + object: _StringObject(rawBits: rawBits.0), + otherBits: UInt(rawBits.1)) +#endif + } } extension _StringGuts { @@ -55,6 +91,26 @@ extension _StringGuts { @_versioned // FIXME(sil-serialize-all) internal func _invariantCheck() { #if INTERNAL_CHECKS_ENABLED + _object._invariantCheck() +#if arch(i386) || arch(arm) + if _object.isContiguous { + _sanityCheck(_extraBits != 0) // TODO: in ABI's address space + } else { + _sanityCheck(_extraBits == 0) + } + if _object.isNative { + _sanityCheck(UInt(_object.nativeRawStorage.count) == self._otherBits) + _sanityCheck( + UInt(bitPattern: _object.nativeRawStorage.rawStart) == self._extraBits) + } else if _object.isUnmanaged { + } else if _object.isCocoa { + _sanityCheck(_otherBits != 0) + } else if _object.isSmall { + fatalError("Small strings aren't supported yet") + } else { + fatalError("Unimplemented string form") + } +#else // 64-bit if _object.isNative { _sanityCheck(UInt(_object.nativeRawStorage.count) == self._otherBits) } else if _object.isUnmanaged { @@ -68,7 +124,8 @@ extension _StringGuts { } else { fatalError("Unimplemented string form") } -#endif +#endif // arch(i386) || arch(arm) +#endif // INTERNAL_CHECKS_ENABLED } @_inlineable @@ -87,7 +144,7 @@ extension _StringGuts { // // FIXME: Super hacky. Is there a better way? defer { _fixLifetime(self) } - var bitPattern = _object.payloadBits + var bitPattern = _object.referenceBits return _isUnique_native(&bitPattern) } @@ -104,35 +161,30 @@ extension _StringGuts { @_inlineable public // @testable var _isNative: Bool { - // FIXME: Currently used to sometimes mean contiguous ASCII return _object.isNative } @_inlineable public // @testable var _isCocoa: Bool { - // FIXME: Currently used to sometimes mean contiguous ASCII return _object.isCocoa } @_inlineable public // @testable var _isUnmanaged: Bool { - // FIXME: Currently used to sometimes mean contiguous ASCII return _object.isUnmanaged } @_inlineable public // @testable var _isSmall: Bool { - // FIXME: Currently used to sometimes mean contiguous ASCII return _object.isSmall } @_inlineable public // @testable var _owner: AnyObject? { - // FIXME: Currently used to sometimes mean contiguous ASCII return _object.owner } @@ -147,14 +199,16 @@ extension _StringGuts { @_inlineable internal var _isEmptyLiteral: Bool { - // FIXME: Currently used to sometimes mean contiguous ASCII +#if arch(i386) || arch(arm) + return _extraBits == UInt(bitPattern: _emptyStringBase) +#else return _object.isEmptyLiteral +#endif } @_inlineable public // @testable var byteWidth: Int { - // FIXME: Currently used to sometimes mean contiguous ASCII return _object.byteWidth } @@ -179,9 +233,16 @@ extension _StringGuts { init(_ storage: _SwiftStringStorage) where CodeUnit : FixedWidthInteger & UnsignedInteger { _sanityCheck(storage.count >= 0) +#if arch(i386) || arch(arm) + self.init( + object: _StringObject(storage), + otherBits: UInt(bitPattern: storage.count), + extraBits: UInt(bitPattern: storage.rawStart)) +#else self.init( object: _StringObject(storage), otherBits: UInt(bitPattern: storage.count)) +#endif } // @@ -201,7 +262,8 @@ extension _StringGuts { get { _sanityCheck(_object.isCocoa) defer { _fixLifetime(self) } - return _StringGuts.getCocoaLength(_unsafeBitPattern: _object.payloadBits) + return _StringGuts.getCocoaLength( + _unsafeBitPattern: _object.referenceBits) // _stdlib_binary_CFStringGetLength(_object.asCocoaObject) } } @@ -235,7 +297,15 @@ extension _StringGuts { @inline(__always) public // @testable init() { +#if arch(i386) || arch(arm) + self.init( + object: _StringObject(), + otherBits: 0, + extraBits: UInt(bitPattern: _emptyStringBase)) +#else self.init(object: _StringObject(), otherBits: 0) +#endif + _invariantCheck() } @inline(never) @@ -252,10 +322,22 @@ extension _StringGuts { self.init() return } +#if arch(i386) || arch(arm) self.init( object: _StringObject( - cocoaObject: s, isSingleByte: isSingleByte, isContiguous: start != nil), + cocoaObject: s, + isSingleByte: isSingleByte, + isContiguous: start != nil), + otherBits: UInt(bitPattern: count), + extraBits: UInt(bitPattern: start)) +#else + self.init( + object: _StringObject( + cocoaObject: s, + isSingleByte: isSingleByte, + isContiguous: start != nil), otherBits: UInt(bitPattern: start)) +#endif if start == nil { _sanityCheck(_object.isOpaque) } else { @@ -263,6 +345,19 @@ extension _StringGuts { } } + @_versioned + @_inlineable + internal var _unmanagedRawStart: UnsafeRawPointer { + @inline(__always) get { + _sanityCheck(_object.isUnmanaged) +#if arch(i386) || arch(arm) + return Builtin.reinterpretCast(_extraBits) +#else + return _object.asUnmanagedRawStart +#endif + } + } + @_versioned @_inlineable internal var _unmanagedCount: Int { @@ -282,7 +377,7 @@ extension _StringGuts { where CodeUnit : FixedWidthInteger & UnsignedInteger { _sanityCheck(_object.isUnmanaged) _sanityCheck(CodeUnit.bitWidth == _object.bitWidth) - let start = _object.asUnmanagedRawStart.assumingMemoryBound(to: CodeUnit.self) + let start = _unmanagedRawStart.assumingMemoryBound(to: CodeUnit.self) let count = _unmanagedCount _sanityCheck(count >= 0) return _UnmanagedString(start: start, count: count) @@ -293,12 +388,20 @@ extension _StringGuts { init(_ s: _UnmanagedString) where CodeUnit : FixedWidthInteger & UnsignedInteger { _sanityCheck(s.count >= 0) +#if arch(i386) || arch(arm) + self.init( + object: _StringObject(unmanagedWithBitWidth: CodeUnit.bitWidth), + otherBits: UInt(bitPattern: s.count), + extraBits: UInt(bitPattern: s.rawStart)) +#else self.init( object: _StringObject(unmanaged: s.start), otherBits: UInt(bitPattern: s.count)) +#endif _sanityCheck(_object.isUnmanaged) - _sanityCheck(_object.asUnmanagedRawStart == s.rawStart) + _sanityCheck(_unmanagedRawStart == s.rawStart) _sanityCheck(_unmanagedCount == s.count) + _invariantCheck() } // @@ -308,7 +411,7 @@ extension _StringGuts { @_inlineable internal var _taggedCocoaCount: Int { _sanityCheck(_object.isSmall) - return Int(bitPattern: _object.payloadBits) + return Int(truncatingIfNeeded: _object.payloadBits) } @_versioned @@ -323,6 +426,9 @@ extension _StringGuts { @_versioned @inline(never) // Hide CF dependency internal init(_taggedCocoaObject object: _CocoaString) { +#if arch(i386) || arch(arm) + _sanityCheckFailure("32-bit platforms don't have tagged Cocoa objects") +#else _sanityCheck(_isObjCTaggedPointer(object)) let count = _stdlib_binary_CFStringGetLength(object) self.init( @@ -330,6 +436,7 @@ extension _StringGuts { smallStringPayload: UInt(count), isSingleByte: false), otherBits: Builtin.reinterpretCast(object)) _sanityCheck(_object.isSmall) +#endif } } @@ -341,6 +448,14 @@ extension _StringGuts { @effects(readonly) get { _sanityCheck(_object.isContiguousASCII) +#if arch(i386) || arch(arm) + _sanityCheck(self._extraBits != 0) + let start = UnsafePointer(bitPattern: _extraBits) + let count = Int(bitPattern: _otherBits) + return _UnmanagedASCIIString( + start: start._unsafelyUnwrappedUnchecked, + count: count) +#else if _object.isUnmanaged { return _asUnmanaged() } else if _object.isNative { @@ -349,6 +464,7 @@ extension _StringGuts { _sanityCheck(_object.isContiguousCocoa) return _asContiguousCocoa(of: UInt8.self) } +#endif } } @@ -359,6 +475,14 @@ extension _StringGuts { @effects(readonly) get { _sanityCheck(_object.isContiguousUTF16) +#if arch(i386) || arch(arm) + _sanityCheck(_extraBits != 0) + let start = UnsafePointer(bitPattern: _extraBits) + let count = Int(bitPattern: _otherBits) + return _UnmanagedUTF16String( + start: start._unsafelyUnwrappedUnchecked, + count: count) +#else if _object.isUnmanaged { return _asUnmanaged() } else if _object.isNative { @@ -367,6 +491,7 @@ extension _StringGuts { _sanityCheck(_object.isContiguousCocoa) return _asContiguousCocoa(of: UTF16.CodeUnit.self) } +#endif } } } @@ -436,10 +561,10 @@ extension _StringGuts { var _underlyingCocoaString: _CocoaString? { if _object.isNative { return _object.nativeRawStorage - } + } if _object.isCocoa { return _object.asCocoaObject - } + } if _object.isSmall { return _taggedCocoaObject } @@ -478,6 +603,15 @@ internal func internalDumpHex(_ x: UInt, newline: Bool = true) { internalDumpHexImpl(x, newline: newline) } @_versioned +internal func internalDumpHex(_ x: UInt64, newline: Bool = true) { +#if arch(i386) || arch(arm) + internalDumpHexImpl(UInt(truncatingIfNeeded: x >> 32), newline: false) + internalDumpHexImpl(UInt(truncatingIfNeeded: x), newline: newline) +#else + internalDumpHexImpl(UInt(x), newline: newline) +#endif +} +@_versioned internal func internalDumpHex(_ x: AnyObject, newline: Bool = true) { internalDumpHexImpl(Builtin.reinterpretCast(x), newline: newline) } @@ -493,9 +627,9 @@ internal func internalDumpHex(_ x: Bool, newline: Bool = true) { extension _StringGuts { public func _dump() { print("_StringGuts(", terminator: "") - internalDumpHex(_object.rawBits, newline: false) + internalDumpHex(rawBits.0, newline: false) print(" ", terminator: "") - internalDumpHex(_otherBits, newline: false) + internalDumpHex(rawBits.1, newline: false) print(": ", terminator: "") if _object.isNative { let storage = _object.nativeRawStorage @@ -520,7 +654,7 @@ extension _StringGuts { print(_cocoaCount, terminator: "") } else if _object.isUnmanaged { print("unmanaged ", terminator: "") - internalDumpHex(_object.asUnmanagedRawStart, newline: false) + internalDumpHex(_unmanagedRawStart, newline: false) print(" count: ", terminator: "") print(_unmanagedCount, terminator: "") } else if _object.isSmall { @@ -865,7 +999,7 @@ extension _StringGuts { @_inlineable public // TODO(StringGuts): for testing only mutating func append(_ other: _StringGuts) { - if _object.isEmptyLiteral { + if _isEmptyLiteral { self = other return } @@ -887,7 +1021,7 @@ extension _StringGuts { mutating func append(_ other: _StringGuts, range: Range) { _sanityCheck(range.lowerBound >= 0 && range.upperBound <= other.count) guard range.count > 0 else { return } - if _object.isEmptyLiteral && range.count == other.count { + if _isEmptyLiteral && range.count == other.count { self = other return } @@ -1061,9 +1195,10 @@ extension _StringGuts : Sequence { _sanityCheck(_buffer.count == 0) _buffer.count = Swift.min(_buffer.capacity, _endOffset - _nextOffset) _sanityCheck(_buffer.count > 0) + let guts = _guts // Make a copy to prevent overlapping access to self _buffer.withUnsafeMutableBufferPointer { buffer in let range: Range = _nextOffset ..< _nextOffset + buffer.count - _guts._copy(range: range, into: buffer) + guts._copy(range: range, into: buffer) } _nextOffset += _buffer.count } diff --git a/stdlib/public/core/StringObject.swift b/stdlib/public/core/StringObject.swift index df2ee9b89fba0..ce393c89659e0 100644 --- a/stdlib/public/core/StringObject.swift +++ b/stdlib/public/core/StringObject.swift @@ -21,10 +21,41 @@ struct _StringObject { // TODO: Proper built-in string object support. For now, we use BridgeObject // which might be very slightly suboptimal and different than our bit // patterns, but provides us the runtime functionality we want. +#if arch(i386) || arch(arm) + // BridgeObject lacks support for tagged pointers on 32-bit platforms, and + // there are no free bits available to implement it. We store tagged payloads + // in an extra word instead, which also includes the + // value/subvariant/width/opacity indicators. (Thus, we currently have only 28 + // bits available for a payload on 32-bit systems, even though we pad + // _StringObject to 8 bytes on all platforms.) + // + // Since we don't implement small strings, we don't currently use these 30 + // bits at all; the base address and count of unmanaged strings live outside + // _StringObject. + @_versioned + internal + var _object: AnyObject? + + @_versioned + internal + var _highBits: UInt +#else @_versioned internal var _object: _BuiltinBridgeObject +#endif +#if arch(i386) || arch(arm) + @_versioned + @_inlineable + @inline(__always) + internal + init(_ object: AnyObject?, _ high: UInt) { + self._object = object + self._highBits = high + _invariantCheck() + } +#else @_versioned @_inlineable @inline(__always) @@ -33,26 +64,46 @@ struct _StringObject { self._object = object _invariantCheck() } +#endif @_versioned @_inlineable internal - var rawBits: UInt { + var _objectBits: UInt { @inline(__always) - get { return _bitPattern(_object) } + get { return Builtin.reinterpretCast(_object) } + } + + @_versioned + @_inlineable + internal + var rawBits: UInt64 { + @inline(__always) + get { +#if arch(i386) || arch(arm) + return UInt64(_highBits) &<< 32 | UInt64(_objectBits) +#else + return UInt64(truncatingIfNeeded: _objectBits) +#endif + } } } +// ## _StringObject bit layout // -// Constant bits and masks -// -// -// Bit layout (x86-64 and arm64): -// -// _StringObject: +// x86-64 and arm64: (one 64-bit word) // +---+---+---|---+------+----------------------------------------------------+ -// + t | v | o | w | uuuu | payload (54 bits) | +// + t | v | o | w | uuuu | payload (56 bits) | // +---+---+---|---+------+----------------------------------------------------+ +// msb lsb +// +// i386 and arm: (two 32-bit words) +// _highBits _object +// +------------------------------------+ +------------------------------------+ +// | t | v | o | w | unused (28 bits) | + optional obj reference | +// +------------------------------------+ +------------------------------------+ +// msb lsb msb lsb +// // where t: is-a-value, i.e. a tag bit that says not to perform ARC // v: sub-variant bit, i.e. set for isCocoa or isSmall // o: is-opaque, i.e. opaque vs contiguously stored strings @@ -65,17 +116,21 @@ struct _StringObject { // isUnmanaged: the pointer to code units // isSmall: opaque bits used for inline storage // TODO: use them! // -// TODO: 32-bit platforms! -// extension _StringObject { - // NOTE: deviating from ObjC tagged pointer bits, as we just want to avoid - // swift runtime management, and top bit suffices for that. @_versioned @_inlineable internal static var _isValueBit: UInt { @inline(__always) - get { return 0x80_00_0000_0000_0000 } + get { +#if arch(i386) || arch(arm) + return 0x8000_0000 +#else + // NOTE: deviating from ObjC tagged pointer bits, as we just want to avoid + // swift runtime management, and top bit suffices for that. + return 0x80_00_0000_0000_0000 +#endif + } } // After deciding isValue, which of the two variants (on both sides) are we. @@ -85,7 +140,13 @@ extension _StringObject { internal static var _subVariantBit: UInt { @inline(__always) - get { return 0x40_00_0000_0000_0000 } + get { +#if arch(i386) || arch(arm) + return 0x4000_0000 +#else + return 0x40_00_0000_0000_0000 +#endif + } } @_versioned @@ -93,7 +154,13 @@ extension _StringObject { internal static var _isOpaqueBit: UInt { @inline(__always) - get { return 0x20_00_0000_0000_0000 } + get { +#if arch(i386) || arch(arm) + return 0x2000_0000 +#else + return 0x20_00_0000_0000_0000 +#endif + } } @_versioned @@ -101,10 +168,16 @@ extension _StringObject { internal static var _twoByteBit: UInt { @inline(__always) - get { return 0x10_00_0000_0000_0000 } + get { +#if arch(i386) || arch(arm) + return 0x1000_0000 +#else + return 0x10_00_0000_0000_0000 +#endif + } } - // Which of the 4 sub-variants are we depends on the top two bits + // There are 4 sub-variants depending on the isValue and subVariant bits @_versioned @_inlineable internal @@ -118,9 +191,21 @@ extension _StringObject { internal static var _payloadMask: UInt { @inline(__always) - get { return 0x00FF_FFFF_FFFF_FFFF } + get { +#if arch(i386) || arch(arm) + return 0x0FFF_FFFF +#else + return 0x00FF_FFFF_FFFF_FFFF +#endif + } } +} +extension _StringObject { +#if arch(i386) || arch(arm) + // TODO: On 32-bit platforms, _StringGuts identifies empty strings by their + // start address, stored outside of _StringObject. +#else @_versioned @_inlineable internal @@ -128,6 +213,15 @@ extension _StringObject { @inline(__always) get { return _isValueBit | UInt(bitPattern: _emptyStringBase) } } + + @_versioned + @_inlineable + internal + var isEmptyLiteral: Bool { + @inline(__always) + get { return _objectBits == _StringObject._emptyLiteralBitPattern } + } +#endif } // @@ -145,9 +239,9 @@ extension _StringObject { _sanityCheck(isNative) _sanityCheck( _usesNativeSwiftReferenceCounting( - type(of: Builtin.reinterpretCast(payloadBits) as AnyObject))) + type(of: Builtin.reinterpretCast(referenceBits) as AnyObject))) - return Builtin.reinterpretCast(payloadBits) + return Builtin.reinterpretCast(referenceBits) } } @@ -160,11 +254,15 @@ extension _StringObject { _sanityCheck(isCocoa) _sanityCheck( !_usesNativeSwiftReferenceCounting( - type(of: Builtin.reinterpretCast(payloadBits) as AnyObject))) - return Builtin.reinterpretCast(payloadBits) + type(of: Builtin.reinterpretCast(referenceBits) as AnyObject))) + return Builtin.reinterpretCast(referenceBits) } } +#if arch(i386) || arch(arm) + // TODO: On 32-bit platforms, we don't have enough payload bits to store a + // start pointer yet, so we store it in _StringGuts instead. +#else @_versioned @_inlineable internal @@ -172,33 +270,70 @@ extension _StringObject { @inline(__always) get { _sanityCheck(isUnmanaged) + _sanityCheck(payloadBits <= UInt.max) return UnsafeRawPointer( - bitPattern: payloadBits + bitPattern: UInt(truncatingIfNeeded: payloadBits) )._unsafelyUnwrappedUnchecked } } +#endif } // // Queries on a StringObject // extension _StringObject { + @_versioned + @_inlineable + internal + var referenceBits: UInt { + @inline(__always) + get { +#if arch(i386) || arch(arm) + return Builtin.reinterpretCast(_object) +#else + return _bitPattern(_object) & UInt(_StringObject._payloadMask) +#endif + } + } + @_versioned @_inlineable internal var payloadBits: UInt { @inline(__always) - get { return rawBits & _StringObject._payloadMask } + get { +#if arch(i386) || arch(arm) + // TODO: This is currently always zero. + return _highBits & _StringObject._payloadMask +#else + return UInt(truncatingIfNeeded: rawBits) & _StringObject._payloadMask +#endif + } } public // @testable var owner: AnyObject? { // For testing only if _fastPath(isNative || isCocoa) { - return Builtin.reinterpretCast(payloadBits) + return Builtin.reinterpretCast(referenceBits) } return nil } + @_versioned + @_inlineable + internal + var _variantBits: UInt { + @inline(__always) + get { +#if arch(i386) || arch(arm) + return _highBits & _StringObject._variantMask +#else + return _objectBits & _StringObject._variantMask +#endif + } + } + // // Determine which of the 4 major variants we are // @@ -207,7 +342,7 @@ extension _StringObject { internal var isNative: Bool { @inline(__always) - get { return rawBits & _StringObject._variantMask == 0 } + get { return _variantBits == 0 } } @_versioned @@ -215,7 +350,7 @@ extension _StringObject { internal var isCocoa: Bool { @inline(__always) - get { return rawBits & _StringObject._variantMask == _StringObject._subVariantBit } + get { return _variantBits == _StringObject._subVariantBit } } @_versioned @@ -223,7 +358,7 @@ extension _StringObject { internal var isUnmanaged: Bool { @inline(__always) - get { return rawBits & _StringObject._variantMask == _StringObject._isValueBit } + get { return _variantBits == _StringObject._isValueBit } } @_versioned @@ -231,7 +366,7 @@ extension _StringObject { internal var isSmall: Bool { @inline(__always) - get { return rawBits & _StringObject._variantMask == _StringObject._variantMask } + get { return _variantBits == _StringObject._variantMask } } // @@ -242,7 +377,13 @@ extension _StringObject { internal var isContiguous: Bool { @inline(__always) - get { return rawBits & _StringObject._isOpaqueBit == 0 } + get { +#if arch(i386) || arch(arm) + return _highBits & _StringObject._isOpaqueBit == 0 +#else + return _objectBits & _StringObject._isOpaqueBit == 0 +#endif + } } @_versioned @@ -273,7 +414,13 @@ extension _StringObject { public // @testable var isSingleByte: Bool { @inline(__always) - get { return rawBits & _StringObject._twoByteBit == 0 } + get { +#if arch(i386) || arch(arm) + return _highBits & _StringObject._twoByteBit == 0 +#else + return _objectBits & _StringObject._twoByteBit == 0 +#endif + } } @_inlineable @@ -304,14 +451,6 @@ extension _StringObject { get { return isContiguous && !isSingleByte } } - @_versioned - @_inlineable - internal - var isEmptyLiteral: Bool { - @inline(__always) - get { return rawBits == _StringObject._emptyLiteralBitPattern } - } - @_versioned @_inlineable @inline(__always) @@ -355,7 +494,9 @@ extension _StringObject { } } else if isUnmanaged { _sanityCheck(isContiguous) +#if !arch(i386) && !arch(arm) _sanityCheck(payloadBits > 0) // TODO: inside address space +#endif } else if isCocoa { } else if isSmall { _sanityCheck(isOpaque) @@ -375,8 +516,14 @@ extension _StringObject { @inline(__always) // TODO: private internal - init(rawBits: UInt) { + init(rawBits: UInt64) { +#if arch(i386) || arch(arm) + self.init( + Builtin.reinterpretCast(UInt(truncatingIfNeeded: rawBits)), + UInt(truncatingIfNeeded: rawBits &>> 32)) +#else self.init(Builtin.reinterpretCast(rawBits)) +#endif } @_versioned @@ -386,11 +533,38 @@ extension _StringObject { init( _payloadBits: UInt, isValue: Bool, - isSmallOrObjC: Bool, // TODO: better name here? + isSmallOrObjC: Bool, isOpaque: Bool, isTwoByte: Bool ) { - var rawBits = _payloadBits +#if arch(i386) || arch(arm) + var highBits: UInt + var objectBits: UInt + if isValue { + // 28-bit payload is stored in _highBits + _sanityCheck(_payloadBits & ~_StringObject._payloadMask == 0) + highBits = _payloadBits & _StringObject._payloadMask + highBits |= _StringObject._isValueBit + _sanityCheck(!isSmallOrObjC) // Can't do that yet + objectBits = Builtin.reinterpretCast(_BuiltinBridgeObject?.none) + } else { + // Payload is bit pattern of reference stored in _object + highBits = 0 + objectBits = _payloadBits + if isSmallOrObjC { + highBits |= _StringObject._subVariantBit + } + } + if isOpaque { + highBits |= _StringObject._isOpaqueBit + } + if isTwoByte { + highBits |= _StringObject._twoByteBit + } + self.init(Builtin.reinterpretCast(objectBits), highBits) +#else + _sanityCheck(_payloadBits & ~_StringObject._payloadMask == 0) + var rawBits = _payloadBits & _StringObject._payloadMask if isValue { rawBits |= _StringObject._isValueBit } @@ -403,7 +577,8 @@ extension _StringObject { if isTwoByte { rawBits |= _StringObject._twoByteBit } - self.init(rawBits: rawBits) + self.init(Builtin.reinterpretCast(rawBits)) +#endif _sanityCheck(isSmall == (isValue && isSmallOrObjC)) _sanityCheck(isUnmanaged == (isValue && !isSmallOrObjC)) _sanityCheck(isCocoa == (!isValue && isSmallOrObjC)) @@ -434,7 +609,13 @@ extension _StringObject { @inline(__always) internal init() { - self.init(rawBits: _StringObject._emptyLiteralBitPattern) +#if arch(i386) || arch(arm) + // TODO: On 32-bit platforms, _StringGuts identifies empty strings by their + // start address, stored outside of _StringObject. + self.init(nil, _StringObject._isValueBit) +#else + self.init(rawBits: UInt64(_StringObject._emptyLiteralBitPattern)) +#endif } @_versioned @@ -462,6 +643,9 @@ extension _StringObject { isSingleByte: isSingleByte) } +#if arch(i386) || arch(arm) + // FIXME Small strings aren't implemented on 32-bit platforms yet +#else @_versioned @_inlineable @inline(__always) @@ -474,7 +658,23 @@ extension _StringObject { isOpaque: true, isTwoByte: !isSingleByte) } +#endif +#if arch(i386) || arch(arm) + @_versioned + @_inlineable + @inline(__always) + internal + init(unmanagedWithBitWidth bitWidth: Int) { + self.init( + _payloadBits: 0, + isValue: true, + isSmallOrObjC: false, + isOpaque: false, + isTwoByte: bitWidth == 16) + _sanityCheck(isSingleByte == (bitWidth == 8)) + } +#else @_versioned @_inlineable @inline(__always) @@ -483,13 +683,14 @@ extension _StringObject { unmanaged: UnsafePointer ) where CodeUnit : FixedWidthInteger & UnsignedInteger { self.init( - _payloadBits: Builtin.reinterpretCast(unmanaged), - isValue: true, - isSmallOrObjC: false, - isOpaque: false, - isTwoByte: CodeUnit.bitWidth == 16) + _payloadBits: UInt(bitPattern: unmanaged), + isValue: true, + isSmallOrObjC: false, + isOpaque: false, + isTwoByte: CodeUnit.bitWidth == 16) _sanityCheck(isSingleByte == (CodeUnit.bitWidth == 8)) } +#endif @_versioned @_inlineable diff --git a/stdlib/public/core/StringVariant.swift b/stdlib/public/core/StringVariant.swift index 385c118f7d44f..44578a6e56447 100644 --- a/stdlib/public/core/StringVariant.swift +++ b/stdlib/public/core/StringVariant.swift @@ -14,7 +14,6 @@ internal protocol _StringVariant : RandomAccessCollection where Element == Unicode.UTF16.CodeUnit, - IndexDistance == Int, SubSequence == Self { // FIXME associatedtype Encoding : _UnicodeEncoding associatedtype CodeUnit : FixedWidthInteger & UnsignedInteger diff --git a/stdlib/public/runtime/Reflection.mm b/stdlib/public/runtime/Reflection.mm index ecab1edce6c27..9665438b2f2a1 100644 --- a/stdlib/public/runtime/Reflection.mm +++ b/stdlib/public/runtime/Reflection.mm @@ -78,7 +78,7 @@ - (id)debugQuickLookObject; struct String { // Keep the details of String's implementation opaque to the runtime. - const void *x, *y, *z; + const uint64_t x, y; /// Keep String trivial on the C++ side so we can control its instantiation. String() = default; @@ -90,7 +90,8 @@ explicit String(const char (&s)[N]) { } /// Copy an ASCII string into a swift String on the heap. - explicit String(const char *ptr, size_t size) { + explicit String(const char *ptr, size_t size) + : x(0), y(0) { swift_stringFromUTF8InRawMemory(this, ptr, size); } diff --git a/stdlib/public/runtime/SwiftObject.h b/stdlib/public/runtime/SwiftObject.h index fe81e605203c1..1688f02c1af21 100644 --- a/stdlib/public/runtime/SwiftObject.h +++ b/stdlib/public/runtime/SwiftObject.h @@ -78,14 +78,7 @@ SWIFT_RUNTIME_EXPORT @interface SwiftObject { namespace swift { -struct String { void *x, *y, *z; }; - -/// Helper from the standard library for stringizing an arbitrary object. -extern "C" SWIFT_CC(swift) -void swift_getSummary(String *out, OpaqueValue *value, const Metadata *T); - -// Convert a Swift String to an NSString. -NSString *convertStringToNSString(String *swiftString); +NSString *getDescription(OpaqueValue *value, const Metadata *type); } diff --git a/stdlib/public/runtime/SwiftObject.mm b/stdlib/public/runtime/SwiftObject.mm index 88341f2d90046..366f77ffb156f 100644 --- a/stdlib/public/runtime/SwiftObject.mm +++ b/stdlib/public/runtime/SwiftObject.mm @@ -147,29 +147,26 @@ static Class _swift_getObjCClassOfAllocated(const void *object) { class_getInstanceSize(cls), mask)); } -NSString *swift::convertStringToNSString(String *swiftString) { - // public func _convertStringToNSString(_ string: String) -> NSString - typedef SWIFT_CC(swift) NSString *ConversionFn(void *sx, void *sy, void *sz); - auto convertStringToNSString = SWIFT_LAZY_CONSTANT( - reinterpret_cast(dlsym(RTLD_DEFAULT, - MANGLE_AS_STRING(MANGLE_SYM(10Foundation24_convertStringToNSStringySo0E0CSSF))))); - +NSString *swift::getDescription(OpaqueValue *value, const Metadata *type) { + typedef SWIFT_CC(swift) NSString *GetDescriptionFn(OpaqueValue*, const Metadata*); + auto getDescription = SWIFT_LAZY_CONSTANT( + reinterpret_cast(dlsym(RTLD_DEFAULT, + MANGLE_AS_STRING(MANGLE_SYM(10Foundation15_getDescriptionySo8NSStringCxlF))))); + // If Foundation hasn't loaded yet, fall back to returning the static string // "Swift._SwiftObject". The likelihood of someone invoking -description without // ObjC interop is low. - if (!convertStringToNSString) + if (!getDescription) { return @"Swift._SwiftObject"; + } - return convertStringToNSString(swiftString->x, - swiftString->y, - swiftString->z); + return [getDescription(value, type) autorelease]; } -static NSString *_getDescription(SwiftObject *obj) { - String tmp; +static NSString *_getObjectDescription(SwiftObject *obj) { swift_retain((HeapObject*)obj); - swift_getSummary(&tmp, (OpaqueValue*)&obj, _swift_getClassOfAllocated(obj)); - return [convertStringToNSString(&tmp) autorelease]; + return getDescription((OpaqueValue*)&obj, + _swift_getClassOfAllocated(obj)); } static NSString *_getClassDescription(Class cls) { @@ -388,10 +385,10 @@ - (id)performSelector:(SEL)aSelector withObject:(id)object1 } - (NSString *)description { - return _getDescription(self); + return _getObjectDescription(self); } - (NSString *)debugDescription { - return _getDescription(self); + return _getObjectDescription(self); } + (NSString *)description { diff --git a/stdlib/public/runtime/SwiftValue.mm b/stdlib/public/runtime/SwiftValue.mm index 3c25d7300da9c..9299c87222f01 100644 --- a/stdlib/public/runtime/SwiftValue.mm +++ b/stdlib/public/runtime/SwiftValue.mm @@ -337,7 +337,6 @@ - (NSUInteger)hash { } static NSString *getValueDescription(_SwiftValue *self) { - String tmp; const Metadata *type; const OpaqueValue *value; std::tie(type, value) = getValueFromSwiftValue(self); @@ -347,10 +346,9 @@ - (NSUInteger)hash { auto copy = type->allocateBufferIn(©Buf); type->vw_initializeWithCopy(copy, const_cast(value)); - swift_getSummary(&tmp, copy, type); - + NSString *string = getDescription(copy, type); type->deallocateBufferIn(©Buf); - return convertStringToNSString(&tmp); + return string; } - (NSString *)description { diff --git a/stdlib/public/stubs/LibcShims.cpp b/stdlib/public/stubs/LibcShims.cpp index 687c7023f762d..07d4b42ce15b3 100644 --- a/stdlib/public/stubs/LibcShims.cpp +++ b/stdlib/public/stubs/LibcShims.cpp @@ -333,11 +333,13 @@ swift::_stdlib_cxx11_mt19937_uniform(__swift_uint32_t upper_bound) { // FIXME remove SWIFT_RUNTIME_STDLIB_INTERFACE void swift::_swift_stdlib_print_hex(__swift_uintptr_t value, int newline) { - if (newline != 0) { - printf("%016tX\n", value); - } - else { + if (sizeof(value) == 4) { + printf("%08tX", value); + } else { printf("%016tX", value); } + if (newline) { + printf("\n"); + } } diff --git a/test/DebugInfo/self.swift b/test/DebugInfo/self.swift index f7544bb66eadd..221274fffc843 100644 --- a/test/DebugInfo/self.swift +++ b/test/DebugInfo/self.swift @@ -15,7 +15,7 @@ public func f() { // // CHECK: define {{.*}} @"$S4self11stuffStructVACycfC"( // CHECK-NEXT: entry: -// CHECK-NEXT: %[[ALLOCA:.*]] = alloca %T4self11stuffStructV, align {{(4|8)}} +// CHECK: %[[ALLOCA:.*]] = alloca %T4self11stuffStructV, align {{(4|8)}} // CHECK: call void @llvm.dbg.declare(metadata %T4self11stuffStructV* %[[ALLOCA]], // CHECK-SAME: metadata ![[SELF:.*]], metadata !DIExpression()), !dbg // CHECK: ![[STUFFSTRUCT:.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "stuffStruct",{{.*}}identifier diff --git a/validation-test/Reflection/reflect_String.swift b/validation-test/Reflection/reflect_String.swift index c0ee7876ee540..1ca4461150bf9 100644 --- a/validation-test/Reflection/reflect_String.swift +++ b/validation-test/Reflection/reflect_String.swift @@ -34,9 +34,9 @@ reflect(object: obj) // CHECK-32: (class reflect_String.TestClass) // CHECK-32: Type info: -// CHECK-32-NEXT: (class_instance size=32 alignment=4 stride=32 +// CHECK-32-NEXT: (class_instance size=24 alignment=4 stride=24 // CHECK-32-NEXT: (field name=t offset=8 -// CHECK-32-NEXT: (struct size=16 alignment=4 stride=16 num_extra_inhabitants=1 +// CHECK-32-NEXT: (struct size=16 alignment=4 stride=16 num_extra_inhabitants=4095 // (unstable implementation details omitted) doneReflecting() diff --git a/validation-test/Reflection/reflect_multiple_types.swift b/validation-test/Reflection/reflect_multiple_types.swift index 289320a65ac2a..2247c9f73b227 100644 --- a/validation-test/Reflection/reflect_multiple_types.swift +++ b/validation-test/Reflection/reflect_multiple_types.swift @@ -200,7 +200,7 @@ reflect(object: obj) // CHECK-32: (class reflect_multiple_types.TestClass) // CHECK-32: Type info: -// CHECK-32-NEXT: (class_instance size=121 alignment=8 stride=128 num_extra_inhabitants=0 +// CHECK-32-NEXT: (class_instance size=129 alignment=8 stride=136 num_extra_inhabitants=0 // CHECK-32-NEXT: (field name=t00 offset=8 // CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=1 // (unstable implementation details omitted) @@ -259,7 +259,7 @@ reflect(object: obj) // CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 // (unstable implementation details omitted) // CHECK-32: (field name=t16 offset=88 -// CHECK-32-NEXT: (struct size=16 alignment=4 stride=16 num_extra_inhabitants=1 +// CHECK-32-NEXT: (struct size=16 alignment=4 stride=16 num_extra_inhabitants=4095 // (unstable implementation details omitted) // CHECK-32: (field name=t17 offset=104 // CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 @@ -273,11 +273,11 @@ reflect(object: obj) // CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 // CHECK-32-NEXT: (field name=_value offset=0 // CHECK-32-NEXT: (builtin size=4 alignment=4 stride=4 num_extra_inhabitants=0)))) -// CHECK-32-NEXT: (field name=t20 offset=116 +// CHECK-32-NEXT: (field name=t20 offset=120 // CHECK-32-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 // CHECK-32-NEXT: (field name=_value offset=0 // CHECK-32-NEXT: (builtin size=8 alignment=8 stride=8 num_extra_inhabitants=0)))) -// CHECK-32-NEXT: (field name=t21 offset=124 +// CHECK-32-NEXT: (field name=t21 offset=128 // CHECK-32-NEXT: (struct size=1 alignment=1 stride=1 num_extra_inhabitants=0 // CHECK-32-NEXT: (field name=_value offset=0 // CHECK-32-NEXT: (builtin size=1 alignment=1 stride=1 num_extra_inhabitants=0)))))