Skip to content

Commit ec5f40f

Browse files
committed
runtime: Move String implementation stubs that want need the auto-released return value optimization to an ARC compiled file
String's hashValue function is implemented in terms of Foundation's hash function in a runtime function on darwin platforms. For non-ASCII strings we will call str.decomposedStringWithCanonicalMapping inside this runtime function which will allocate a new NSString and return the result in the current autorelease pool. We implemented this function in a file compiled without ARC. This meant that we would leak said NSString into the current active autorelease pool. This patch moves the implementation to a file compiled with ARC. ARC will insert objc_retainAutoreleasedReturnValue call and on platforms that require it an marker for the hand-off of the autoreleased return value optimization. SR-4889 rdar://32199117
1 parent 0a2cf99 commit ec5f40f

File tree

5 files changed

+186
-19
lines changed

5 files changed

+186
-19
lines changed

stdlib/public/stubs/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ set(swift_stubs_objc_sources
1111
FoundationHelpers.mm
1212
OptionalBridgingHelper.mm
1313
Reflection.mm
14+
SwiftNativeNSXXXBaseARC.m
1415
SwiftNativeNSXXXBase.mm.gyb)
1516
set(swift_stubs_unicode_normalization_sources
1617
UnicodeNormalization.cpp)
@@ -41,3 +42,5 @@ add_swift_library(swiftStdlibStubs OBJECT_LIBRARY TARGET_LIBRARY
4142
LINK_FLAGS ${SWIFT_RUNTIME_CORE_LINK_FLAGS}
4243
INSTALL_IN_COMPONENT stdlib)
4344

45+
set_property(SOURCE SwiftNativeNSXXXBaseARC.m APPEND_STRING PROPERTY COMPILE_FLAGS
46+
"-fobjc-arc")

stdlib/public/stubs/SwiftNativeNSXXXBase.mm.gyb

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -143,25 +143,6 @@ swift_stdlib_CFStringHashCString(const uint8_t *bytes, CFIndex len) {
143143
return Result;
144144
}
145145

146-
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
147-
size_t
148-
swift_stdlib_NSStringHashValue(NSString *NS_RELEASES_ARGUMENT str,
149-
bool isASCII) {
150-
size_t Result =
151-
isASCII ? str.hash : str.decomposedStringWithCanonicalMapping.hash;
152-
153-
swift_unknownRelease(str);
154-
return Result;
155-
}
156-
157-
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
158-
size_t
159-
swift_stdlib_NSStringHashValuePointer(void *opaque, bool isASCII) {
160-
NSString *str = (NSString *)opaque;
161-
return isASCII ? str.hash : str.decomposedStringWithCanonicalMapping.hash;
162-
}
163-
164-
165146
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
166147
bool swift_stdlib_NSStringHasPrefixNFD(NSString *theString,
167148
NSString *prefix) {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===--- SwiftNativeNSXXXBaseARC.mm - Runtime stubs that require ARC ------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
#include "swift/Runtime/Config.h"
13+
14+
#if SWIFT_OBJC_INTEROP
15+
16+
#import <Foundation/Foundation.h>
17+
#import <CoreFoundation/CoreFoundation.h>
18+
#include <objc/NSObject.h>
19+
#include <objc/runtime.h>
20+
#include <objc/objc.h>
21+
22+
/// The following two routines need to be implemented in ARC because
23+
/// decomposedStringWithCanonicalMapping returns its result autoreleased. And we
24+
/// want ARC to insert 'objc_retainAutoreleasedReturnValue' and the necessary
25+
/// markers for the hand-off.
26+
27+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
28+
size_t swift_stdlib_NSStringHashValue(NSString *NS_RELEASES_ARGUMENT str,
29+
bool isASCII) {
30+
return isASCII ? str.hash : str.decomposedStringWithCanonicalMapping.hash;
31+
}
32+
33+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
34+
size_t
35+
swift_stdlib_NSStringHashValuePointer(void *opaque, bool isASCII) {
36+
NSString __unsafe_unretained *str =
37+
(__bridge NSString __unsafe_unretained *)opaque;
38+
return isASCII ? str.hash : str.decomposedStringWithCanonicalMapping.hash;
39+
}
40+
#endif

test/stdlib/StringMemoryTest.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -O %s -o %t/StringMemoryTest
3+
// RUN: %target-run %t/StringMemoryTest | %FileCheck %s
4+
5+
// REQUIRES: optimized_stdlib
6+
// REQUIRES: executable_test
7+
// REQUIRES: objc_interop
8+
9+
import Foundation
10+
11+
@inline(never)
12+
func lookup(_ str: String, _ dict: [String: Int]) -> Bool {
13+
if let _ = dict[str] {
14+
return true
15+
}
16+
return false
17+
}
18+
19+
/// Make sure the hash function does not leak.
20+
21+
let dict = [ "foo" : 1]
22+
for _ in 0 ..< 10_000_000 {
23+
if lookup("\u{1F1E7}\u{1F1E7}", dict) {
24+
print("Found?!")
25+
}
26+
}
27+
28+
// CHECK: Not found
29+
print("Not found")
30+
31+
var usage = rusage()
32+
getrusage(RUSAGE_SELF, &usage)
33+
34+
// CHECK: success
35+
// CHECK-NOT: failure
36+
37+
// We should not need 50MB for this.
38+
if usage.ru_maxrss > 50 * 1024 * 1024 {
39+
print("failure - should not need 50MB!")
40+
} else {
41+
print("success")
42+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// UNSUPPORTED: linux
2+
// RUN: otool -tvV %platform-module-dir/libswiftCore.dylib | %FileCheck %s --check-prefix=CHECK-%target-cpu
3+
4+
// Verify the autorelease return optimization sequence.
5+
6+
/// Test x86-64:
7+
8+
// CHECK-x86_64-LABEL: _swift_stdlib_NSStringHashValue:
9+
// CHECK-x86_64-NOT: ret
10+
// CHECK-x86_64: movq {{.*}}(%rip), %rsi ## Objc selector ref: decomposedStringWithCanonicalMapping
11+
// CHECK-x86_64: movq {{.*}}(%rip), [[MSG:%.*]] ## Objc message: -[%rdi decomposedStringWithCanonicalMapping]
12+
// CHECK-x86_64: callq *[[MSG]]
13+
// CHECK-x86_64: movq %rax, %rdi
14+
// CHECK-x86_64: callq {{.*}} ## symbol stub for: _objc_retainAutoreleasedReturnValue
15+
// CHECK-x86_64: ret
16+
17+
18+
// CHECK-x86_64-LABEL: _swift_stdlib_NSStringHashValuePointer:
19+
// CHECK-x86_64-NOT: ret
20+
// CHECK-x86_64: movq {{.*}}(%rip), %rsi ## Objc selector ref: decomposedStringWithCanonicalMapping
21+
// CHECK-x86_64: movq {{.*}}(%rip), [[MSG:%.*]] ## Objc message: -[%rdi decomposedStringWithCanonicalMapping]
22+
// CHECK-x86_64: callq *[[MSG]]
23+
// CHECK-x86_64: movq %rax, %rdi
24+
// CHECK-x86_64: callq {{.*}} ## symbol stub for: _objc_retainAutoreleasedReturnValue
25+
// CHECK-x86_64: ret
26+
27+
/// Test i386:
28+
29+
// CHECK-i386-LABEL: _swift_stdlib_NSStringHashValue:
30+
// CHECK-i386-NOT: ret
31+
// CHECK-i386: calll {{.*}} ## symbol stub for: _objc_msgSend
32+
// CHECK-i386: movl %ebp, %ebp
33+
// CHECK-i386: calll {{.*}} ## symbol stub for: _objc_retainAutoreleasedReturnValue
34+
// CHECK-i386: ret
35+
// CHECK-i386-LABEL: _swift_stdlib_NSStringHashValuePointer:
36+
// CHECK-i386-NOT: ret
37+
// CHECK-i386: calll {{.*}} ## symbol stub for: _objc_msgSend
38+
// CHECK-i386: movl %ebp, %ebp
39+
// CHECK-i386: calll {{.*}} ## symbol stub for: _objc_retainAutoreleasedReturnValue
40+
// CHECK-i386: ret
41+
42+
/// Test armv7:
43+
44+
// CHECK-armv7-LABEL: _swift_stdlib_NSStringHashValue:
45+
// CHECK-armv7-NOT: pop {{.*}}pc{{.*}}
46+
// CHECK-armv7: blx {{.*}} @ symbol stub for: _objc_msgSend
47+
// CHECK-armv7: mov r7, r7
48+
// CHECK-armv7: blx {{.*}} @ symbol stub for: _objc_retainAutoreleasedReturnValue
49+
// CHECK-armv7: pop {{.*}}pc{{.*}}
50+
// CHECK-armv7-LABEL: _swift_stdlib_NSStringHashValuePointer:
51+
// CHECK-armv7-NOT: pop {{.*}}pc{{.*}}
52+
// CHECK-armv7: blx {{.*}} @ symbol stub for: _objc_msgSend
53+
// CHECK-armv7: mov r7, r7
54+
// CHECK-armv7: blx {{.*}} @ symbol stub for: _objc_retainAutoreleasedReturnValue
55+
// CHECK-armv7: pop {{.*}}pc{{.*}}
56+
57+
/// Test armv7s:
58+
59+
// CHECK-armv7s-LABEL: _swift_stdlib_NSStringHashValue:
60+
// CHECK-armv7s-NOT: pop {{.*}}pc{{.*}}
61+
// CHECK-armv7s: blx {{.*}} @ symbol stub for: _objc_msgSend
62+
// CHECK-armv7s: mov r7, r7
63+
// CHECK-armv7s: blx {{.*}} @ symbol stub for: _objc_retainAutoreleasedReturnValue
64+
// CHECK-armv7s: pop {{.*}}pc{{.*}}
65+
// CHECK-armv7s-LABEL: _swift_stdlib_NSStringHashValuePointer:
66+
// CHECK-armv7s-NOT: pop {{.*}}pc{{.*}}
67+
// CHECK-armv7s: blx {{.*}} @ symbol stub for: _objc_msgSend
68+
// CHECK-armv7s: mov r7, r7
69+
// CHECK-armv7s: blx {{.*}} @ symbol stub for: _objc_retainAutoreleasedReturnValue
70+
// CHECK-armv7s: pop {{.*}}pc{{.*}}
71+
72+
73+
/// Test armv7k:
74+
75+
// CHECK-armv7k-LABEL: _swift_stdlib_NSStringHashValue:
76+
// CHECK-armv7k-NOT: pop {{.*}}pc{{.*}}
77+
// CHECK-armv7k: blx {{.*}} @ symbol stub for: _objc_msgSend
78+
// CHECK-armv7k: mov r7, r7
79+
// CHECK-armv7k: blx {{.*}} @ symbol stub for: _objc_retainAutoreleasedReturnValue
80+
// CHECK-armv7k: pop {{.*}}pc{{.*}}
81+
// CHECK-armv7k-LABEL: _swift_stdlib_NSStringHashValuePointer:
82+
// CHECK-armv7k-NOT: pop {{.*}}pc{{.*}}
83+
// CHECK-armv7k: blx {{.*}} @ symbol stub for: _objc_msgSend
84+
// CHECK-armv7k: mov r7, r7
85+
// CHECK-armv7k: blx {{.*}} @ symbol stub for: _objc_retainAutoreleasedReturnValue
86+
// CHECK-armv7k: pop {{.*}}pc{{.*}}
87+
88+
/// Test arm64:
89+
90+
// CHECK-arm64-LABEL: _swift_stdlib_NSStringHashValue:
91+
// CHECK-arm64-NOT: ret
92+
// CHECK-arm64: bl {{.*}} ; Objc message: -[x0 decomposedStringWithCanonicalMapping]
93+
// CHECK-arm64: mov x29, x29
94+
// CHECK-arm64: bl {{.*}} ; symbol stub for: _objc_retainAutoreleasedReturnValue
95+
// CHECK-arm64: ret
96+
// CHECK-arm64-LABEL: _swift_stdlib_NSStringHashValuePointer:
97+
// CHECK-arm64-NOT: ret
98+
// CHECK-arm64: bl {{.*}} ; Objc message: -[x0 decomposedStringWithCanonicalMapping]
99+
// CHECK-arm64: mov x29, x29
100+
// CHECK-arm64: bl {{.*}} ; symbol stub for: _objc_retainAutoreleasedReturnValue
101+
// CHECK-arm64: ret

0 commit comments

Comments
 (0)