[5.0] [String] ASCII fast-path for UTF16View (#20900)

* Assorted bridging changes:
• Convert _AbstractStringStorage to a protocol, and the free functions used to deduplicate implementations to extensions on that protocol.
• Move 'start' into the abstract type and use that to simplify some code
• Move the ASCII fast path for length into UTF16View.
• Add a weirder but faster way to check which (if any) of our NSString subclasses a given object is, and adopt it

(cherry picked from commit 8b57921905ed89b921aeaf68f72b73d3dfb67054)

* [String] ASCII fast-path for UTF16View (#20848)

Add an isASCII fast-path for many UTF16View operations. These are
heavily utilized in random-access scenarios, allowing us to both be
more efficient and skip generating breadcrumbs for ASCII strings.
diff --git a/stdlib/public/SwiftShims/CoreFoundationShims.h b/stdlib/public/SwiftShims/CoreFoundationShims.h
index 57b6c53..03eabe5 100644
--- a/stdlib/public/SwiftShims/CoreFoundationShims.h
+++ b/stdlib/public/SwiftShims/CoreFoundationShims.h
@@ -137,6 +137,10 @@
                                          _swift_shims_UInt8 *buffer,
                                          _swift_shims_CFIndex maxLength,
                                          unsigned long encoding);
+
+SWIFT_RUNTIME_STDLIB_API
+__swift_uintptr_t
+_swift_stdlib_unsafeAddressOfClass(id _Nonnull obj);
   
 #endif // __OBJC2__
 
diff --git a/stdlib/public/core/StringBridge.swift b/stdlib/public/core/StringBridge.swift
index 776187b..4950f5f 100644
--- a/stdlib/public/core/StringBridge.swift
+++ b/stdlib/public/core/StringBridge.swift
@@ -107,6 +107,8 @@
 }
 
 
+
+
 @_effects(releasenone)
 internal func _cocoaGetCStringTrampoline(
                              _ string: _CocoaString,
@@ -131,6 +133,40 @@
   @inline(__always) get { return 0x8000100 }
 }
 
+@_effects(readonly)
+private func _unsafeAddressOfCocoaStringClass(_ str: _CocoaString) -> UInt {
+  return _swift_stdlib_unsafeAddressOfClass(str)
+}
+
+internal enum _KnownCocoaString {
+  case storage
+  case shared
+  case cocoa
+#if !(arch(i386) || arch(arm))
+  case tagged
+#endif
+  
+  @inline(__always)
+  init(_ str: _CocoaString) {
+    
+    #if !(arch(i386) || arch(arm))
+    if _isObjCTaggedPointer(str) {
+      self = .tagged
+      return
+    }
+    #endif
+    
+    switch _unsafeAddressOfCocoaStringClass(str) {
+    case unsafeBitCast(_StringStorage.self, to: UInt.self):
+      self = .storage
+    case unsafeBitCast(_SharedStringStorage.self, to: UInt.self):
+      self = .shared
+    default:
+      self = .cocoa
+    }
+  }
+}
+
 #if !(arch(i386) || arch(arm))
 // Resiliently write a tagged cocoa string's contents into a buffer
 @_effects(releasenone) // @opaque
@@ -185,47 +221,49 @@
 @usableFromInline
 @_effects(releasenone) // @opaque
 internal func _bridgeCocoaString(_ cocoaString: _CocoaString) -> _StringGuts {
-  if let abstract = cocoaString as? _AbstractStringStorage {
-    return abstract.asString._guts
-  }
+  switch _KnownCocoaString(cocoaString) {
+  case .storage:
+    return _unsafeUncheckedDowncast(cocoaString, to: _StringStorage.self).asString._guts
+  case .shared:
+    return _unsafeUncheckedDowncast(cocoaString, to: _SharedStringStorage.self).asString._guts
 #if !(arch(i386) || arch(arm))
-  if _isObjCTaggedPointer(cocoaString) {
-    return _StringGuts(_SmallString(taggedCocoa: cocoaString))
-  }
+  case .tagged:
+      return _StringGuts(_SmallString(taggedCocoa: cocoaString))
 #endif
-
-  // "copy" it into a value to be sure nobody will modify behind
-  // our backs.  In practice, when value is already immutable, this
-  // just does a retain.
-  //
-  // TODO: Only in certain circumstances should we emit this call:
-  //   1) If it's immutable, just retain it.
-  //   2) If it's mutable with no associated information, then a copy must
-  //      happen; might as well eagerly bridge it in.
-  //   3) If it's mutable with associated information, must make the call
-  //
-  let immutableCopy
-    = _stdlib_binary_CFStringCreateCopy(cocoaString) as AnyObject
-
-#if !(arch(i386) || arch(arm))
-  if _isObjCTaggedPointer(immutableCopy) {
-    return _StringGuts(_SmallString(taggedCocoa: immutableCopy))
-  }
-#endif
-
-  let (fastUTF8, isASCII): (Bool, Bool)
-  switch _getCocoaStringPointer(immutableCopy) {
+  case .cocoa:
+    // "copy" it into a value to be sure nobody will modify behind
+    // our backs.  In practice, when value is already immutable, this
+    // just does a retain.
+    //
+    // TODO: Only in certain circumstances should we emit this call:
+    //   1) If it's immutable, just retain it.
+    //   2) If it's mutable with no associated information, then a copy must
+    //      happen; might as well eagerly bridge it in.
+    //   3) If it's mutable with associated information, must make the call
+    //
+    let immutableCopy
+      = _stdlib_binary_CFStringCreateCopy(cocoaString) as AnyObject
+    
+    #if !(arch(i386) || arch(arm))
+    if _isObjCTaggedPointer(immutableCopy) {
+      return _StringGuts(_SmallString(taggedCocoa: immutableCopy))
+    }
+    #endif
+    
+    let (fastUTF8, isASCII): (Bool, Bool)
+    switch _getCocoaStringPointer(immutableCopy) {
     case .ascii(_): (fastUTF8, isASCII) = (true, true)
     case .utf8(_): (fastUTF8, isASCII) = (true, false)
     default:  (fastUTF8, isASCII) = (false, false)
+    }
+    let length = _stdlib_binary_CFStringGetLength(immutableCopy)
+    
+    return _StringGuts(
+      cocoa: immutableCopy,
+      providesFastUTF8: fastUTF8,
+      isASCII: isASCII,
+      length: length)
   }
-  let length = _stdlib_binary_CFStringGetLength(immutableCopy)
-
-  return _StringGuts(
-    cocoa: immutableCopy,
-    providesFastUTF8: fastUTF8,
-    isASCII: isASCII,
-    length: length)
 }
 
 extension String {
diff --git a/stdlib/public/core/StringStorage.swift b/stdlib/public/core/StringStorage.swift
index 768da04..babb40a 100644
--- a/stdlib/public/core/StringStorage.swift
+++ b/stdlib/public/core/StringStorage.swift
@@ -12,159 +12,160 @@
 
 import SwiftShims
 
+//Having @objc stuff in an extension creates an ObjC category, which we don't want
 #if _runtime(_ObjC)
 
-internal class _AbstractStringStorage: __SwiftNativeNSString, _NSCopying {
-  // Abstract interface
-  internal var asGuts: _StringGuts { @_effects(readonly) get { Builtin.unreachable() } }
-  final internal var asString: String { @_effects(readonly) get { return String(asGuts) } }
-  internal var count: Int { @_effects(readonly) get { Builtin.unreachable() } }
-  fileprivate var isASCII: Bool { @_effects(readonly) get { Builtin.unreachable() } }
-
-  //Having these in an extension creates an ObjC category, which we don't want
+internal protocol _AbstractStringStorage : _NSCopying {
+  var asString: String { get }
+  var count: Int { get }
+  var isASCII: Bool { get }
+  var start: UnsafePointer<UInt8> { get }
   //in UTF16 code units
-  @objc(length) internal var length: Int { @_effects(readonly) get { Builtin.unreachable() } }
-  
-  @objc(copyWithZone:)
-  final internal func copy(with zone: _SwiftNSZone?) -> AnyObject {
-    // While _StringStorage instances aren't immutable in general,
-    // mutations may only occur when instances are uniquely referenced.
-    // Therefore, it is safe to return self here; any outstanding Objective-C
-    // reference will make the instance non-unique.
-    return self
-  }
-}
-
-#else
-
-internal class _AbstractStringStorage: __SwiftNativeNSString {
-  // Abstract interface
-  internal var asGuts: _StringGuts { @_effects(readonly) get { Builtin.unreachable() } }
-  final internal var asString: String { @_effects(readonly) get { return String(asGuts) } }
-  internal var count: Int { @_effects(readonly) get { Builtin.unreachable() } }
-  fileprivate var isASCII: Bool { @_effects(readonly) get { Builtin.unreachable() } }
-}
-
-#endif
-
-// ObjC interfaces
-#if _runtime(_ObjC)
-
-@_effects(releasenone)
-private func _getCharacters<T:_AbstractStringStorage>(_ this:T,
-   _ buffer: UnsafeMutablePointer<UInt16>,
-   _ aRange: _SwiftNSRange) {
-  _precondition(aRange.location >= 0 && aRange.length >= 0,
-    "Range out of bounds")
-  _precondition(aRange.location + aRange.length <= Int(this.count),
-    "Range out of bounds")
-
-  let range = Range(
-    uncheckedBounds: (aRange.location, aRange.location+aRange.length))
-  let str = this.asString
-  str._copyUTF16CodeUnits(
-    into: UnsafeMutableBufferPointer(start: buffer, count: range.count),
-    range: range)
-}
-
-@_effects(releasenone)
-private func _getCString<T:_AbstractStringStorage>(
-  _ this: T,
-  _ utf8: String.UTF8View,
-  _ outputPtr: UnsafeMutablePointer<UInt8>,
-  _ maxLength: Int,
-  _ encoding: UInt)
-  -> Int8 {
-    switch (encoding, this.isASCII) {
-    case (_cocoaASCIIEncoding, true):
-      fallthrough
-    case (_cocoaUTF8Encoding, _):
-      guard maxLength >= this.count + 1 else { return 0 }
-      let buffer = UnsafeMutableBufferPointer(start: outputPtr, count: maxLength)
-      let (_, end) = utf8._copyContents(initializing: buffer)
-      buffer[end] = 0
-      return 1
-    default:
-      return  _cocoaGetCStringTrampoline(this,
-                                         outputPtr,
-                                         maxLength,
-                                         encoding)
-    }
-}
-
-@inline(never) //hide the shim call so we can use @_effects
-@_effects(readonly)
-private func _isNSString(_ str:AnyObject) -> UInt8 {
-  return _swift_stdlib_isNSString(str)
-}
-
-//This used to be on _AbstractStringStorage, which meant it went through the
-//dynamic version of asString.
-//Making it generic and calling it from the subclasses lets us avoid that.
-@_effects(readonly)
-private func _isEqual<T:_AbstractStringStorage>(_ this:T, _ other:AnyObject?)
-  -> Int8 {
-  guard let other = other else {
-    return 0
-  }
-
-  if this === other {
-    return 1
-  }
- 
-  let ourGuts = this.asGuts
-  defer { _fixLifetime(ourGuts) }
-
-  //Handle the case where both strings were bridged from Swift.
-  //We can't use String.== because it doesn't match NSString semantics.
-  if let otherGuts = (other as? _AbstractStringStorage)?.asGuts {
-    if otherGuts.count != ourGuts.count {
-      return 0
-    }
-    return ourGuts.withFastUTF8 { ourBytes in
-      return otherGuts.withFastUTF8 { otherBytes in
-        return (ourBytes.baseAddress == otherBytes.baseAddress ||
-          (memcmp(ourBytes.baseAddress!, otherBytes.baseAddress!, ourBytes.count) == 0)) ? 1 : 0
-      }
-    }
-  }
-  
-  //we're allowed to crash, but for compatibility reasons NSCFString allows non-strings here
-  if _isNSString(other) != 1 {
-    return 0
-  }
-  
-  //At this point we've proven that it is an NSString of some sort, but not one of ours
-  if this.length != _stdlib_binary_CFStringGetLength(other) {
-    return 0
-  }
-  
-  defer {
-    _fixLifetime(other)
-  }
-
-  //CFString will only give us ASCII bytes here, but that's fine
-  //We already handled non-ASCII UTF8 strings earlier since they're Swift
-  if let otherBytes = _cocoaUTF8Pointer(other) {
-    return ourGuts.withFastUTF8 { ourBytes in
-      return (ourBytes.baseAddress == otherBytes ||
-        (memcmp(ourBytes.baseAddress!, otherBytes, ourBytes.count) == 0)) ? 1 : 0
-    }
-  }
-  
-  /*
-  The abstract implementation of -isEqualToString: falls back to -compare:
-  immediately, so when we run out of fast options to try, do the same.
-  We can likely be more clever here if need be
-  */
-  return _cocoaStringCompare(this, other) == 0 ? 1 : 0
+  var length: Int { get }
 }
 
 internal let _cocoaASCIIEncoding:UInt = 1 /* NSASCIIStringEncoding */
 internal let _cocoaUTF8Encoding:UInt = 4 /* NSUTF8StringEncoding */
 
+@_effects(readonly)
+private func _isNSString(_ str:AnyObject) -> UInt8 {
+  return _swift_stdlib_isNSString(str)
+}
 
-#endif // _runtime(_ObjC)
+#else
+
+internal protocol _AbstractStringStorage {
+  var asString: String { get }
+  var count: Int { get }
+  var isASCII: Bool { get }
+  var start: UnsafePointer<UInt8> { get }
+}
+
+#endif
+
+extension _AbstractStringStorage {
+
+  // ObjC interfaces
+  #if _runtime(_ObjC)
+  
+  @inline(__always)
+  @_effects(releasenone)
+  internal func _getCharacters(
+    _ buffer: UnsafeMutablePointer<UInt16>,
+    _ aRange: _SwiftNSRange
+    ) {
+    _precondition(aRange.location >= 0 && aRange.length >= 0,
+                  "Range out of bounds")
+    _precondition(aRange.location + aRange.length <= Int(count),
+                  "Range out of bounds")
+    
+    let range = Range(
+      uncheckedBounds: (aRange.location, aRange.location+aRange.length))
+    let str = asString
+    str._copyUTF16CodeUnits(
+      into: UnsafeMutableBufferPointer(start: buffer, count: range.count),
+      range: range)
+  }
+
+  @inline(__always)
+  @_effects(releasenone)
+  internal func _getCString(
+    _ outputPtr: UnsafeMutablePointer<UInt8>,
+    _ maxLength: Int,
+    _ encoding: UInt)
+    -> Int8 {
+      switch (encoding, isASCII) {
+      case (_cocoaASCIIEncoding, true):
+        fallthrough
+      case (_cocoaUTF8Encoding, _):
+        guard maxLength >= count + 1 else { return 0 }
+        let buffer = UnsafeMutableBufferPointer(start: outputPtr, count: maxLength)
+        buffer.initialize(from: UnsafeBufferPointer(start: start, count: count))
+        buffer[count] = 0
+        return 1
+      default:
+        return  _cocoaGetCStringTrampoline(self,
+                                           outputPtr,
+                                           maxLength,
+                                           encoding)
+      }
+  }
+
+  @inline(__always)
+  @_effects(readonly)
+  internal func _cString(encoding: UInt) -> UnsafePointer<UInt8>? {
+    switch (encoding, isASCII) {
+    case (_cocoaASCIIEncoding, true):
+      fallthrough
+    case (_cocoaUTF8Encoding, _):
+      return start
+    default:
+      return _cocoaCStringUsingEncodingTrampoline(self, encoding)
+    }
+  }
+  
+  @_effects(readonly)
+  internal func _nativeIsEqual<T:_AbstractStringStorage>(_ nativeOther: T) -> Int8 {
+    if count != nativeOther.count {
+      return 0
+    }
+    return (start == nativeOther.start || (memcmp(start, nativeOther.start, count) == 0)) ? 1 : 0
+  }
+
+  @inline(__always)
+  @_effects(readonly)
+  internal func _isEqual(_ other:AnyObject?)
+    -> Int8 {
+      guard let other = other else {
+        return 0
+      }
+      
+      if self === other {
+        return 1
+      }
+      
+      // Handle the case where both strings were bridged from Swift.
+      // We can't use String.== because it doesn't match NSString semantics.
+      let knownOther = _KnownCocoaString(other)
+      switch knownOther {
+        case .storage:
+          return _nativeIsEqual(_unsafeUncheckedDowncast(other, to: _StringStorage.self))
+        case .shared:
+          return _nativeIsEqual(_unsafeUncheckedDowncast(other, to: _SharedStringStorage.self))
+#if !(arch(i386) || arch(arm))
+        case .tagged:
+          fallthrough
+#endif
+        case .cocoa:
+          //we're allowed to crash, but for compatibility reasons NSCFString allows non-strings here
+          if _isNSString(other) != 1 {
+            return 0
+          }
+          
+          //At this point we've proven that it is an NSString of some sort, but not one of ours
+          if length != _stdlib_binary_CFStringGetLength(other) {
+            return 0
+          }
+          
+          defer { _fixLifetime(other) }
+          
+          //CFString will only give us ASCII bytes here, but that's fine
+          //We already handled non-ASCII UTF8 strings earlier since they're Swift
+          if let otherStart = _cocoaUTF8Pointer(other) {
+            return (start == otherStart || (memcmp(start, otherStart, count) == 0)) ? 1 : 0
+          }
+          
+          /*
+           The abstract implementation of -isEqualToString: falls back to -compare:
+           immediately, so when we run out of fast options to try, do the same.
+           We can likely be more clever here if need be
+           */
+          return _cocoaStringCompare(self, other) == 0 ? 1 : 0
+      }
+  }
+  
+  #endif //_runtime(_ObjC)
+}
 
 #if arch(i386) || arch(arm)
 private typealias Flags = _StringObject.Flags
@@ -177,7 +178,7 @@
 // Optional<_StringBreadcrumbs>.
 //
 
-final internal class _StringStorage: _AbstractStringStorage {
+final internal class _StringStorage: __SwiftNativeNSString, _AbstractStringStorage {
 #if arch(i386) || arch(arm)
   // The total allocated storage capacity. Note that this includes the required
   // nul-terminator
@@ -189,7 +190,7 @@
 
   internal var _reserved: UInt16
 
-  override internal var count: Int {
+  internal var count: Int {
     @inline(__always) get { return _count }
     @inline(__always) set { _count = newValue }
   }
@@ -200,7 +201,7 @@
 
   internal var _countAndFlags: _StringObject.CountAndFlags
 
-  override internal var count: Int {
+  internal var count: Int {
     @inline(__always) get { return _countAndFlags.count }
     @inline(__always) set { _countAndFlags.count = newValue }
   }
@@ -215,29 +216,28 @@
   }
 #endif
 
-  fileprivate final override var isASCII: Bool {
-#if arch(i386) || arch(arm)
-    return _flags.isASCII
-#else
-    return _countAndFlags.isASCII
-#endif
+  internal final var isASCII: Bool {
+    @inline(__always) get {
+  #if arch(i386) || arch(arm)
+      return _flags.isASCII
+  #else
+      return _countAndFlags.isASCII
+  #endif
+    }
   }
 
-  override final internal var asGuts: _StringGuts {
-    @inline(__always) @_effects(readonly) get {
-      return _StringGuts(self)
+  final internal var asString: String {
+    @_effects(readonly) @inline(__always) get {
+      return String(_StringGuts(self))
     }
   }
 
 #if _runtime(_ObjC)
   
   @objc(length)
-  override final internal var length: Int {
+  final internal var length: Int {
     @_effects(readonly) @inline(__always) get {
-      if isASCII {
-        return count
-      }
-      return asString.utf16.count
+      return asString.utf16.count //UTF16View special-cases ASCII for us
     }
   }
 
@@ -262,7 +262,7 @@
   final internal func getCharacters(
    _ buffer: UnsafeMutablePointer<UInt16>,
    range aRange: _SwiftNSRange) {
-    _getCharacters(self, buffer, aRange)
+    _getCharacters(buffer, aRange)
   }
 
   @objc(_fastCStringContents:)
@@ -283,15 +283,8 @@
 
   @objc(cStringUsingEncoding:)
   @_effects(readonly)
-  final internal func _cString(encoding: UInt) -> UnsafePointer<UInt8>? {
-    switch (encoding, isASCII) {
-    case (_cocoaASCIIEncoding, true):
-      fallthrough
-    case (_cocoaUTF8Encoding, _):
-      return start
-    default:
-      return _cocoaCStringUsingEncodingTrampoline(self, encoding)
-    }
+  final internal func cString(encoding: UInt) -> UnsafePointer<UInt8>? {
+    return _cString(encoding: encoding)
   }
   
   @objc(getCString:maxLength:encoding:)
@@ -300,7 +293,7 @@
                                maxLength: Int,
                                encoding: UInt)
     -> Int8 {
-    return _getCString(self, asString.utf8, outputPtr, maxLength, encoding)
+    return _getCString(outputPtr, maxLength, encoding)
   }
 
   @objc
@@ -316,7 +309,16 @@
   @objc(isEqualToString:)
   @_effects(readonly)
   final internal func isEqual(to other:AnyObject?) -> Int8 {
-    return _isEqual(self, other)
+    return _isEqual(other)
+  }
+  
+  @objc(copyWithZone:)
+  final internal func copy(with zone: _SwiftNSZone?) -> AnyObject {
+    // While _StringStorage instances aren't immutable in general,
+    // mutations may only occur when instances are uniquely referenced.
+    // Therefore, it is safe to return self here; any outstanding Objective-C
+    // reference will make the instance non-unique.
+    return self
   }
 
 #endif // _runtime(_ObjC)
@@ -666,10 +668,10 @@
 }
 
 // For shared storage and bridging literals
-final internal class _SharedStringStorage: _AbstractStringStorage {
+final internal class _SharedStringStorage: __SwiftNativeNSString, _AbstractStringStorage {
   internal var _owner: AnyObject?
 
-  internal var _start: UnsafePointer<UInt8>
+  internal var start: UnsafePointer<UInt8>
 
 #if arch(i386) || arch(arm)
   internal var _count: Int
@@ -688,14 +690,14 @@
 
   internal var _breadcrumbs: _StringBreadcrumbs? = nil
 
-  internal var start: UnsafePointer<UInt8> { return _start }
-
-  override internal var count: Int {
-#if arch(i386) || arch(arm)
-    return _count
-#else
-    return _countAndFlags.count
-#endif
+  internal var count: Int {
+    @_effects(readonly) @inline(__always) get {
+      #if arch(i386) || arch(arm)
+          return _count
+      #else
+          return _countAndFlags.count
+      #endif
+    }
   }
 
   internal init(
@@ -703,7 +705,7 @@
     countAndFlags: _StringObject.CountAndFlags
   ) {
     self._owner = nil
-    self._start = ptr
+    self.start = ptr
 #if arch(i386) || arch(arm)
     self._count = countAndFlags.count
     self._flags = countAndFlags.flags
@@ -714,29 +716,28 @@
     self._invariantCheck()
   }
   
-  fileprivate final override var isASCII: Bool {
-    #if arch(i386) || arch(arm)
-    return _flags.isASCII
-    #else
-    return _countAndFlags.isASCII
-    #endif
+  internal final var isASCII: Bool {
+    @inline(__always) get {
+      #if arch(i386) || arch(arm)
+      return _flags.isASCII
+      #else
+      return _countAndFlags.isASCII
+      #endif
+    }
   }
 
-  override final internal var asGuts: _StringGuts {
-    @_effects(readonly) get {
-      return _StringGuts(self)
+  final internal var asString: String {
+    @_effects(readonly) @inline(__always) get {
+      return String(_StringGuts(self))
     }
   }
 
 #if _runtime(_ObjC)
   
   @objc(length)
-  override final internal var length: Int {
+  final internal var length: Int {
     @_effects(readonly) get {
-      if isASCII {
-        return count
-      }
-      return asString.utf16.count
+      return asString.utf16.count //UTF16View special-cases ASCII for us
     }
   }
   
@@ -761,7 +762,7 @@
   final internal func getCharacters(
     _ buffer: UnsafeMutablePointer<UInt16>,
     range aRange: _SwiftNSRange) {
-    _getCharacters(self, buffer, aRange)
+    _getCharacters(buffer, aRange)
   }
   
   @objc
@@ -792,15 +793,8 @@
 
   @objc(cStringUsingEncoding:)
   @_effects(readonly)
-  final internal func _cString(encoding: UInt) -> UnsafePointer<UInt8>? {
-    switch (encoding, isASCII) {
-    case (_cocoaASCIIEncoding, true):
-      fallthrough
-    case (_cocoaUTF8Encoding, _):
-      return start
-    default:
-      return _cocoaCStringUsingEncodingTrampoline(self, encoding)
-    }
+  final internal func cString(encoding: UInt) -> UnsafePointer<UInt8>? {
+    return _cString(encoding: encoding)
   }
   
   @objc(getCString:maxLength:encoding:)
@@ -809,13 +803,22 @@
                                maxLength: Int,
                                encoding: UInt)
     -> Int8 {
-    return _getCString(self, asString.utf8, outputPtr, maxLength, encoding)
+    return _getCString(outputPtr, maxLength, encoding)
   }
 
   @objc(isEqualToString:)
   @_effects(readonly)
   final internal func isEqual(to other:AnyObject?) -> Int8 {
-    return _isEqual(self, other)
+    return _isEqual(other)
+  }
+  
+  @objc(copyWithZone:)
+  final internal func copy(with zone: _SwiftNSZone?) -> AnyObject {
+    // While _StringStorage instances aren't immutable in general,
+    // mutations may only occur when instances are uniquely referenced.
+    // Therefore, it is safe to return self here; any outstanding Objective-C
+    // reference will make the instance non-unique.
+    return self
   }
 
 #endif // _runtime(_ObjC)
diff --git a/stdlib/public/core/StringUTF16View.swift b/stdlib/public/core/StringUTF16View.swift
index 2537294..0cbd168 100644
--- a/stdlib/public/core/StringUTF16View.swift
+++ b/stdlib/public/core/StringUTF16View.swift
@@ -117,7 +117,8 @@
   #else
   @usableFromInline @inline(never) @_effects(releasenone)
   internal func _invariantCheck() {
-    // TODO: Ensure start/end are not sub-scalr UTF-8 transcoded indices
+    _internalInvariant(
+      startIndex.transcodedOffset == 0 && endIndex.transcodedOffset == 0)
   }
   #endif // INTERNAL_CHECKS_ENABLED
 }
@@ -143,9 +144,8 @@
 
   @inlinable @inline(__always)
   public func index(after i: Index) -> Index {
-    // TODO(String performance) known-ASCII fast path
-
     if _slowPath(_guts.isForeign) { return _foreignIndex(after: i) }
+    if _guts.isASCII { return i.nextEncoded }
 
     // For a BMP scalar (1-3 UTF-8 code units), advance past it. For a non-BMP
     // scalar, use a transcoded offset first.
@@ -159,9 +159,8 @@
   @inlinable @inline(__always)
   public func index(before i: Index) -> Index {
     precondition(!i.isZeroPosition)
-    // TODO(String performance) known-ASCII fast path
-
     if _slowPath(_guts.isForeign) { return _foreignIndex(before: i) }
+    if _guts.isASCII { return i.priorEncoded }
 
     if i.transcodedOffset != 0 {
       _internalInvariant(i.transcodedOffset == 1)
@@ -181,7 +180,6 @@
   }
 
   public func index(_ i: Index, offsetBy n: Int) -> Index {
-    // TODO(String performance) known-ASCII fast path
     if _slowPath(_guts.isForeign) {
       return _foreignIndex(i, offsetBy: n)
     }
@@ -194,7 +192,6 @@
   public func index(
     _ i: Index, offsetBy n: Int, limitedBy limit: Index
   ) -> Index? {
-    // TODO(String performance) known-ASCII fast path
     if _slowPath(_guts.isForeign) {
       return _foreignIndex(i, offsetBy: n, limitedBy: limit)
     }
@@ -216,7 +213,6 @@
   }
 
   public func distance(from start: Index, to end: Index) -> Int {
-    // TODO(String performance) known-ASCII fast path
     if _slowPath(_guts.isForeign) {
       return _foreignDistance(from: start, to: end)
     }
@@ -249,7 +245,6 @@
   @inlinable
   public subscript(i: Index) -> UTF16.CodeUnit {
     @inline(__always) get {
-      // TODO(String performance) known-ASCII fast path
       String(_guts)._boundsCheck(i)
 
       if _fastPath(_guts.isFastUTF8) {
@@ -319,16 +314,16 @@
 
 
 extension String.UTF16View: CustomStringConvertible {
- @inlinable
- public var description: String {
-   @inline(__always) get { return String(_guts) }
- }
+  @inlinable
+  public var description: String {
+    @inline(__always) get { return String(_guts) }
+  }
 }
 
 extension String.UTF16View: CustomDebugStringConvertible {
- public var debugDescription: String {
-   return "StringUTF16(\(self.description.debugDescription))"
- }
+  public var debugDescription: String {
+    return "StringUTF16(\(self.description.debugDescription))"
+  }
 }
 
 extension String {
@@ -514,10 +509,15 @@
     // Trivial and common: start
     if idx == startIndex { return 0 }
 
+    if _guts.isASCII {
+      _internalInvariant(idx.transcodedOffset == 0)
+      return idx.encodedOffset
+    }
+
     if idx.encodedOffset < _shortHeuristic || !_guts.hasBreadcrumbs {
       return _distance(from: startIndex, to: idx)
     }
-
+    
     // Simple and common: endIndex aka `length`.
     let breadcrumbsPtr = _guts.getBreadcrumbsPtr()
     if idx == endIndex { return breadcrumbsPtr.pointee.utf16Length }
@@ -534,6 +534,8 @@
     // Trivial and common: start
     if offset == 0 { return startIndex }
 
+    if _guts.isASCII { return Index(encodedOffset: offset) }
+
     if offset < _shortHeuristic || !_guts.hasBreadcrumbs {
       return _index(startIndex, offsetBy: offset)
     }
@@ -592,12 +594,26 @@
 
     if _slowPath(range.isEmpty) { return }
 
+    let isASCII = _guts.isASCII
     return _guts.withFastUTF8 { utf8 in
       var writeIdx = 0
       let writeEnd = buffer.count
       var readIdx = range.lowerBound.encodedOffset
       let readEnd = range.upperBound.encodedOffset
 
+      if isASCII {
+        _internalInvariant(range.lowerBound.transcodedOffset == 0)
+        _internalInvariant(range.upperBound.transcodedOffset == 0)
+        while readIdx < readEnd {
+          _internalInvariant(utf8[readIdx] < 0x80)
+          buffer[_unchecked: writeIdx] = UInt16(
+            truncatingIfNeeded: utf8[_unchecked: readIdx])
+          readIdx &+= 1
+          writeIdx &+= 1
+        }
+        return
+      }
+
       // Handle mid-transcoded-scalar initial index
       if _slowPath(range.lowerBound.transcodedOffset != 0) {
         _internalInvariant(range.lowerBound.transcodedOffset == 1)
diff --git a/stdlib/public/stubs/FoundationHelpers.mm b/stdlib/public/stubs/FoundationHelpers.mm
index 32668d7..e2cb9c8 100644
--- a/stdlib/public/stubs/FoundationHelpers.mm
+++ b/stdlib/public/stubs/FoundationHelpers.mm
@@ -175,6 +175,11 @@
 
 }
 
+__swift_uintptr_t
+swift::_swift_stdlib_unsafeAddressOfClass(id _Nonnull obj) {
+  return (__swift_uintptr_t)object_getClass(obj); //TODO: do direct isa access when in the OS
+}
+
 
 #endif
 
diff --git a/test/api-digester/Outputs/stability-stdlib-abi.swift.expected b/test/api-digester/Outputs/stability-stdlib-abi.swift.expected
index 9fad767..152c968 100644
--- a/test/api-digester/Outputs/stability-stdlib-abi.swift.expected
+++ b/test/api-digester/Outputs/stability-stdlib-abi.swift.expected
@@ -230,10 +230,8 @@
 Var Hasher._Core._buffer is added to a non-resilient type
 Var Hasher._Core._state in a non-resilient type changes position from 0 to 1
 
-Class _AbstractStringStorage has been removed
 Constructor _StringGuts.init(_:) has been removed
 Constructor _StringObject.init(_:) has been removed
-Constructor _StringStorage.init() has been removed
 Var _StringObject.nativeStorage has been removed
 Var _StringStorage._countAndFlags has been removed
 Var _StringStorage._realCapacityAndFlags has been removed
@@ -520,3 +518,15 @@
 Var LazyCollectionProtocol.lazy has declared type change from LazyCollection<τ_0_0.Elements> to LazySequence<τ_0_0.Elements>
 
 Func Sequence.reduce(into:_:) has parameter 0 changing from Default to Owned
+
+Class _AbstractStringStorage has been changed to a Protocol
+Class _AbstractStringStorage has generic signature change from to <τ_0_0 : _NSCopying>
+Class _AbstractStringStorage has removed conformance to _NSCopying
+Class _AbstractStringStorage has removed conformance to _NSStringCore
+Class _AbstractStringStorage has removed conformance to _ShadowProtocol
+Class _AbstractStringStorage is now without @_fixed_layout
+Class _SharedStringStorage has changed its super class from _AbstractStringStorage to __SwiftNativeNSString
+Class _StringStorage has changed its super class from _AbstractStringStorage to __SwiftNativeNSString
+Constructor _AbstractStringStorage.init() has been removed
+Func _AbstractStringStorage.copy(with:) has been removed
+Func _AbstractStringStorage.getOrComputeBreadcrumbs() has been removed