Skip to content

runtime: Move String implementation stubs that want need the auto-rel… #12312

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 9 commits into from
Oct 8, 2017
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
5 changes: 5 additions & 0 deletions stdlib/public/stubs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ set(swift_stubs_objc_sources
FoundationHelpers.mm
OptionalBridgingHelper.mm
Reflection.mm
SwiftNativeNSXXXBaseARC.m
SwiftNativeNSXXXBase.mm.gyb)
set(swift_stubs_unicode_normalization_sources
UnicodeNormalization.cpp)
Expand Down Expand Up @@ -41,3 +42,7 @@ add_swift_library(swiftStdlibStubs OBJECT_LIBRARY TARGET_LIBRARY
LINK_FLAGS ${SWIFT_RUNTIME_CORE_LINK_FLAGS}
INSTALL_IN_COMPONENT stdlib)

if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set_property(SOURCE SwiftNativeNSXXXBaseARC.m APPEND_STRING PROPERTY COMPILE_FLAGS
"-fobjc-arc")
endif()
35 changes: 0 additions & 35 deletions stdlib/public/stubs/SwiftNativeNSXXXBase.mm.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -143,25 +143,6 @@ swift_stdlib_CFStringHashCString(const uint8_t *bytes, CFIndex len) {
return Result;
}

SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
size_t
swift_stdlib_NSStringHashValue(NSString *NS_RELEASES_ARGUMENT str,
bool isASCII) {
size_t Result =
isASCII ? str.hash : str.decomposedStringWithCanonicalMapping.hash;

swift_unknownRelease(str);
return Result;
}

SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
size_t
swift_stdlib_NSStringHashValuePointer(void *opaque, bool isASCII) {
NSString *str = (NSString *)opaque;
return isASCII ? str.hash : str.decomposedStringWithCanonicalMapping.hash;
}


SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
bool swift_stdlib_NSStringHasPrefixNFD(NSString *theString,
NSString *prefix) {
Expand Down Expand Up @@ -211,22 +192,6 @@ bool swift_stdlib_NSStringHasSuffixNFDPointer(void *theString,
return Result;
}

SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
NS_RETURNS_RETAINED NSString *
swift_stdlib_NSStringLowercaseString(NSString *NS_RELEASES_ARGUMENT str) {
NSString *Result = objc_retain(str.lowercaseString);
swift_unknownRelease(str);
return Result;
}

SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
NS_RETURNS_RETAINED NSString *
swift_stdlib_NSStringUppercaseString(NSString *NS_RELEASES_ARGUMENT str) {
NSString *Result = objc_retain(str.uppercaseString);
swift_unknownRelease(str);
return Result;
}

SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
void swift_stdlib_CFSetGetValues(NSSet *NS_RELEASES_ARGUMENT set,
const void **values) {
Expand Down
75 changes: 75 additions & 0 deletions stdlib/public/stubs/SwiftNativeNSXXXBaseARC.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===--- SwiftNativeNSXXXBaseARC.mm - Runtime stubs that require ARC ------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/Runtime/Config.h"

#if SWIFT_OBJC_INTEROP

#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#include <objc/NSObject.h>
#include <objc/runtime.h>
#include <objc/objc.h>

// The following two routines need to be implemented in ARC because
// decomposedStringWithCanonicalMapping returns its result autoreleased. And we
// want ARC to insert 'objc_retainAutoreleasedReturnValue' and the necessary
// markers for the hand-off to facilitate the remove from autorelease pool
// optimization such that the object is not handed into the current autorelease
// pool which might be scoped such that repeatedly placing objects into it
// results in unbounded memory growth.

// On i386 the remove from autorelease pool optimization is foiled by the
// decomposedStringWithCanonicalMapping implementation. Instead, we use a local
// autorelease pool to prevent leaking of the temporary object into the callers
// autorelease pool.
#if defined(__i386__)
#define AUTORELEASEPOOL @autoreleasepool
#else
// On other platforms we rely on the remove from autorelease pool optimization.
#define AUTORELEASEPOOL
#endif

SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
size_t swift_stdlib_NSStringHashValue(NSString *NS_RELEASES_ARGUMENT str,
bool isASCII) {
AUTORELEASEPOOL {
return isASCII ? str.hash : str.decomposedStringWithCanonicalMapping.hash;
}
}

SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
size_t
swift_stdlib_NSStringHashValuePointer(void *opaque, bool isASCII) {
NSString __unsafe_unretained *str =
(__bridge NSString __unsafe_unretained *)opaque;
AUTORELEASEPOOL {
return isASCII ? str.hash : str.decomposedStringWithCanonicalMapping.hash;
}
}

SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
NS_RETURNS_RETAINED NSString *
swift_stdlib_NSStringLowercaseString(NSString *NS_RELEASES_ARGUMENT str) {
AUTORELEASEPOOL {
return str.lowercaseString;
}
}

SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
NS_RETURNS_RETAINED NSString *
swift_stdlib_NSStringUppercaseString(NSString *NS_RELEASES_ARGUMENT str) {
AUTORELEASEPOOL {
return str.uppercaseString;
}
}

#endif
63 changes: 63 additions & 0 deletions test/stdlib/StringMemoryTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -O %s -o %t/StringMemoryTest
// RUN: %target-run %t/StringMemoryTest | %FileCheck %s

// REQUIRES: optimized_stdlib
// REQUIRES: executable_test
// REQUIRES: objc_interop

import Foundation

let str = "abcdefg\u{A758}hijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\u{A759}"
let str2 = "abcdefg\u{A759}hijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\u{A758}"

@inline(never)
func lookup(_ str: String, _ dict: [String: Int]) -> Bool {
if let _ = dict[str] {
return true
}
return false
}

@inline(never)
func uppercase(_ str: String) -> String {
return str.uppercased()
}

@inline(never)
func lowercase(_ str: String) -> String {
return str.lowercased()
}

/// Make sure the hash function does not leak.

let dict = [ "foo" : 1]
for _ in 0 ..< 10_000_000 {
if lookup("\u{1F1E7}\u{1F1E7}", dict) {
print("Found?!")
}
if uppercase(str) == "A" {
print("Found?!")
}
if lowercase(str2) == "A" {
print("Found?!")
}
}

// CHECK-NOT: Found?!
// CHECK: Not found

print("Not found")

var usage = rusage()
getrusage(RUSAGE_SELF, &usage)

// CHECK: success
// CHECK-NOT: failure

// We should not need 50MB for this.
if usage.ru_maxrss > 50 * 1024 * 1024 {
print("failure - should not need 50MB!")
} else {
print("success")
}
101 changes: 101 additions & 0 deletions test/stdlib/runtime_autorelease_optimization.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// REQUIRES: objc_interop
// RUN: otool -tvV %platform-module-dir/libswiftCore.dylib | %FileCheck %s --check-prefix=CHECK-%target-cpu

// Verify the autorelease return optimization sequence.

/// Test x86-64:

// CHECK-x86_64-LABEL: _swift_stdlib_NSStringHashValue:
// CHECK-x86_64-NOT: ret
// CHECK-x86_64: movq {{.*}}(%rip), %rsi ## Objc selector ref: decomposedStringWithCanonicalMapping
// CHECK-x86_64: movq {{.*}}(%rip), [[MSG:%.*]] ## Objc message: -[%rdi decomposedStringWithCanonicalMapping]
// CHECK-x86_64: callq *[[MSG]]
// CHECK-x86_64: movq %rax, %rdi
// CHECK-x86_64: callq {{.*}} ## symbol stub for: _objc_retainAutoreleasedReturnValue
// CHECK-x86_64: ret


// CHECK-x86_64-LABEL: _swift_stdlib_NSStringHashValuePointer:
// CHECK-x86_64-NOT: ret
// CHECK-x86_64: movq {{.*}}(%rip), %rsi ## Objc selector ref: decomposedStringWithCanonicalMapping
// CHECK-x86_64: movq {{.*}}(%rip), [[MSG:%.*]] ## Objc message: -[%rdi decomposedStringWithCanonicalMapping]
// CHECK-x86_64: callq *[[MSG]]
// CHECK-x86_64: movq %rax, %rdi
// CHECK-x86_64: callq {{.*}} ## symbol stub for: _objc_retainAutoreleasedReturnValue
// CHECK-x86_64: ret

/// Test i386:

// CHECK-i386-LABEL: _swift_stdlib_NSStringHashValue:
// CHECK-i386-NOT: ret
// CHECK-i386: calll {{.*}} ## symbol stub for: _objc_msgSend
// CHECK-i386: movl %ebp, %ebp
// CHECK-i386: calll {{.*}} ## symbol stub for: _objc_retainAutoreleasedReturnValue
// CHECK-i386: ret
// CHECK-i386-LABEL: _swift_stdlib_NSStringHashValuePointer:
// CHECK-i386-NOT: ret
// CHECK-i386: calll {{.*}} ## symbol stub for: _objc_msgSend
// CHECK-i386: movl %ebp, %ebp
// CHECK-i386: calll {{.*}} ## symbol stub for: _objc_retainAutoreleasedReturnValue
// CHECK-i386: ret

/// Test armv7:

// CHECK-armv7-LABEL: _swift_stdlib_NSStringHashValue:
// CHECK-armv7-NOT: pop {{.*}}pc{{.*}}
// CHECK-armv7: blx {{.*}} @ symbol stub for: _objc_msgSend
// CHECK-armv7: mov r7, r7
// CHECK-armv7: blx {{.*}} @ symbol stub for: _objc_retainAutoreleasedReturnValue
// CHECK-armv7: pop {{.*}}pc{{.*}}
// CHECK-armv7-LABEL: _swift_stdlib_NSStringHashValuePointer:
// CHECK-armv7-NOT: pop {{.*}}pc{{.*}}
// CHECK-armv7: blx {{.*}} @ symbol stub for: _objc_msgSend
// CHECK-armv7: mov r7, r7
// CHECK-armv7: blx {{.*}} @ symbol stub for: _objc_retainAutoreleasedReturnValue
// CHECK-armv7: pop {{.*}}pc{{.*}}

/// Test armv7s:

// CHECK-armv7s-LABEL: _swift_stdlib_NSStringHashValue:
// CHECK-armv7s-NOT: pop {{.*}}pc{{.*}}
// CHECK-armv7s: blx {{.*}} @ symbol stub for: _objc_msgSend
// CHECK-armv7s: mov r7, r7
// CHECK-armv7s: blx {{.*}} @ symbol stub for: _objc_retainAutoreleasedReturnValue
// CHECK-armv7s: pop {{.*}}pc{{.*}}
// CHECK-armv7s-LABEL: _swift_stdlib_NSStringHashValuePointer:
// CHECK-armv7s-NOT: pop {{.*}}pc{{.*}}
// CHECK-armv7s: blx {{.*}} @ symbol stub for: _objc_msgSend
// CHECK-armv7s: mov r7, r7
// CHECK-armv7s: blx {{.*}} @ symbol stub for: _objc_retainAutoreleasedReturnValue
// CHECK-armv7s: pop {{.*}}pc{{.*}}


/// Test armv7k:

// CHECK-armv7k-LABEL: _swift_stdlib_NSStringHashValue:
// CHECK-armv7k-NOT: pop {{.*}}pc{{.*}}
// CHECK-armv7k: blx {{.*}} @ symbol stub for: _objc_msgSend
// CHECK-armv7k: mov r7, r7
// CHECK-armv7k: blx {{.*}} @ symbol stub for: _objc_retainAutoreleasedReturnValue
// CHECK-armv7k: pop {{.*}}pc{{.*}}
// CHECK-armv7k-LABEL: _swift_stdlib_NSStringHashValuePointer:
// CHECK-armv7k-NOT: pop {{.*}}pc{{.*}}
// CHECK-armv7k: blx {{.*}} @ symbol stub for: _objc_msgSend
// CHECK-armv7k: mov r7, r7
// CHECK-armv7k: blx {{.*}} @ symbol stub for: _objc_retainAutoreleasedReturnValue
// CHECK-armv7k: pop {{.*}}pc{{.*}}

/// Test arm64:

// CHECK-arm64-LABEL: _swift_stdlib_NSStringHashValue:
// CHECK-arm64-NOT: ret
// CHECK-arm64: bl {{.*}} ; Objc message: -[x0 decomposedStringWithCanonicalMapping]
// CHECK-arm64: mov x29, x29
// CHECK-arm64: bl {{.*}} ; symbol stub for: _objc_retainAutoreleasedReturnValue
// CHECK-arm64: ret
// CHECK-arm64-LABEL: _swift_stdlib_NSStringHashValuePointer:
// CHECK-arm64-NOT: ret
// CHECK-arm64: bl {{.*}} ; Objc message: -[x0 decomposedStringWithCanonicalMapping]
// CHECK-arm64: mov x29, x29
// CHECK-arm64: bl {{.*}} ; symbol stub for: _objc_retainAutoreleasedReturnValue
// CHECK-arm64: ret