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 4 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
53 changes: 53 additions & 0 deletions stdlib/public/stubs/SwiftNativeNSXXXBaseARC.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//===--- 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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: this shouldn't be a doc comment.


SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
size_t swift_stdlib_NSStringHashValue(NSString *NS_RELEASES_ARGUMENT str,
bool isASCII) {
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;
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) {
return str.lowercaseString;
}

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

#endif
66 changes: 66 additions & 0 deletions test/stdlib/StringMemoryTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// 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

// The autorelease return optimizaion seems to be failing in the simulator.
// XFAIL: CPU=i386
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we just don't do it there. @rjmccall?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, seems to be the case. I tried with a pure objective-c app and we also leak there on an i386 simulator rdar://34863458


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 @@
// UNSUPPORTED: linux
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to make this REQUIRES: objc_interop. This won't work on Windows or Android either, after all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point

// 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