Skip to content

Commit 47383e2

Browse files
committed
stdlib: let the construction of a small string literal compile down to 2 constant loads.
The main part of this is to rewrite the small string literal-constructor to work with values (= shifting bytes) instead of setting bytes in memory. This allows the compiler to fold away everything and end up with the optimal code for small string literals.
1 parent 50f7ea5 commit 47383e2

File tree

3 files changed

+62
-10
lines changed

3 files changed

+62
-10
lines changed

stdlib/public/core/SmallString.swift

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -127,25 +127,30 @@ extension _SmallUTF8String {
127127
#endif
128128
}
129129

130+
@inline(__always)
130131
@inlinable
132+
@effects(readonly)
131133
public // @testable
132134
init?(_ codeUnits: UnsafeBufferPointer<UInt8>) {
133135
#if arch(i386) || arch(arm)
134136
return nil // Never form small strings on 32-bit
135137
#else
136138
let count = codeUnits.count
137139
guard count <= _SmallUTF8String.capacity else { return nil }
138-
self.init()
139-
self._withAllUnsafeMutableBytes { rawBufPtr in
140-
let rawDst = rawBufPtr.baseAddress._unsafelyUnwrappedUnchecked
141-
memcpy_(
142-
dst: rawDst.assumingMemoryBound(to: UInt8.self),
143-
src: codeUnits.baseAddress._unsafelyUnwrappedUnchecked,
144-
count: count
145-
)
140+
141+
let addr = codeUnits.baseAddress._unsafelyUnwrappedUnchecked
142+
var high: UInt
143+
let lowCount: Int
144+
if count > 8 {
145+
lowCount = 8
146+
high = _bytesToUInt(addr + 8, count &- 8)
147+
} else {
148+
lowCount = count
149+
high = 0
146150
}
147-
_sanityCheck(self.count == 0, "overwrote count early?")
148-
self.count = count
151+
high |= (UInt(count) &<< (8*15))
152+
let low = _bytesToUInt(addr, lowCount)
153+
_storage = (low, high)
149154

150155
// FIXME: support transcoding
151156
if !self.isASCII { return nil }
@@ -169,6 +174,18 @@ extension _SmallUTF8String {
169174
}
170175
}
171176

177+
@inline(__always)
178+
@inlinable
179+
func _bytesToUInt(_ input: UnsafePointer<UInt8>, _ c: Int) -> UInt {
180+
var r: UInt = 0
181+
var shift: Int = 0
182+
for idx in 0..<c {
183+
r = r | (UInt(input[idx]) &<< shift)
184+
shift = shift &+ 8
185+
}
186+
return r
187+
}
188+
172189
//
173190
// Small string read interface
174191
//

stdlib/public/core/String.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,7 @@ extension String : _ExpressibleByBuiltinUTF16StringLiteral {
734734
}
735735

736736
extension String : _ExpressibleByBuiltinStringLiteral {
737+
@inline(__always)
737738
@inlinable
738739
@effects(readonly)
739740
@_semantics("string.makeUTF8")
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// RUN: %target-swift-frontend -parse-as-library -O -emit-ir %s | %FileCheck %s
2+
// RUN: %target-swift-frontend -parse-as-library -Osize -emit-ir %s | %FileCheck %s
3+
// REQUIRES: swift_stdlib_no_asserts,optimized_stdlib
4+
5+
// This is an end-to-end test to ensure that the optimizer generates
6+
// optimal code for string literals
7+
8+
// CHECK-LABEL: define {{.*}}test_create_verysmallstring
9+
// CHECK: entry:
10+
// CHECK-NEXT: ret {{.*}} inttoptr
11+
public func test_create_verysmallstring() -> String {
12+
return "a"
13+
}
14+
15+
// CHECK-LABEL: define {{.*}}test_create_smallstring
16+
// CHECK: entry:
17+
// CHECK-NEXT: ret {{.*}} inttoptr
18+
public func test_create_smallstring() -> String {
19+
return "abcdefghijkl012"
20+
}
21+
22+
// CHECK-LABEL: define {{.*}}test_create_largestring
23+
// CHECK: entry:
24+
// CHECK-NEXT: ret {{.*}} inttoptr
25+
public func test_create_largestring() -> String {
26+
return "abcdefghijkl012qwerqwer"
27+
}
28+
29+
// CHECK-LABEL: define {{.*}}test_create_unicode
30+
// CHECK: entry:
31+
// CHECK-NEXT: ret {{.*}} inttoptr
32+
public func test_create_unicode() -> String {
33+
return "❄️gastroperiodyni"
34+
}

0 commit comments

Comments
 (0)