diff --git a/CMakeLists.txt b/CMakeLists.txt index 31808174c5804..bfc677f49479f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -235,8 +235,8 @@ option(SWIFT_RUNTIME_CRASH_REPORTER_CLIENT FALSE) option(SWIFT_DARWIN_ENABLE_STABLE_ABI_BIT - "Enable the Swift stable ABI's class marker bit" - FALSE) + "Enable the Swift stable ABI's class marker bit for new deployment targets" + TRUE) set(SWIFT_DARWIN_XCRUN_TOOLCHAIN "XcodeDefault" CACHE STRING "The name of the toolchain to pass to 'xcrun'") diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index a453603dacd0e..a24735720b8fd 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -1192,6 +1192,31 @@ struct TargetClassMetadata : public TargetAnyClassMetadata { return bounds; } + /// Given a statically-emitted metadata template, this sets the correct + /// "is Swift" bit for the current runtime. Depending on the deployment + /// target a binary was compiled for, statically emitted metadata templates + /// may have a different bit set from the one that this runtime canonically + /// considers the "is Swift" bit. + void setAsTypeMetadata() { + // If the wrong "is Swift" bit is set, set the correct one. + // + // Note that the only time we should see the "new" bit set while + // expecting the "old" one is when running a binary built for a + // new OS on an old OS, which is not supported, however we do + // have tests that exercise this scenario. + auto otherSwiftBit = (3ULL - SWIFT_CLASS_IS_SWIFT_MASK); + assert(otherSwiftBit == 1ULL || otherSwiftBit == 2ULL); + + if ((this->Data & 3) == otherSwiftBit) { + this->Data ^= 3; + } + + // Otherwise there should be nothing to do, since only the old "is + // Swift" bit is used for backward-deployed runtimes. + + assert(isTypeMetadata()); + } + static bool classof(const TargetMetadata *metadata) { return metadata->getKind() == MetadataKind::Class; } diff --git a/include/swift/Runtime/BackDeployment.h b/include/swift/Runtime/BackDeployment.h new file mode 100644 index 0000000000000..1e6737f182d39 --- /dev/null +++ b/include/swift/Runtime/BackDeployment.h @@ -0,0 +1,45 @@ +//===--- BackDeployment.h - Support for running on older OS versions. -----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 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 +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_STDLIB_BACKDEPLOYMENT_H +#define SWIFT_STDLIB_BACKDEPLOYMENT_H + +#if defined(__APPLE__) && defined(__MACH__) + +#include "swift/Runtime/Config.h" +#include "../../../stdlib/public/SwiftShims/Visibility.h" + +#ifdef __cplusplus +namespace swift { extern "C" { +#endif + +#if SWIFT_CLASS_IS_SWIFT_MASK_GLOBAL_VARIABLE +# ifndef __cplusplus +// This file gets included from some C/ObjC files and +// SWIFT_RUNTIME_STDLIB_SPI doesn't imply extern in C. +extern +# endif +SWIFT_RUNTIME_STDLIB_SPI unsigned long long _swift_classIsSwiftMask; +#endif + +/// Returns true if the current OS version, at runtime, is a back-deployment +/// version. +SWIFT_RUNTIME_STDLIB_INTERNAL +int _swift_isBackDeploying(); + +#ifdef __cplusplus +}} // extern "C", namespace swift +#endif + +#endif // defined(__APPLE__) && defined(__MACH__) + +#endif // SWIFT_STDLIB_BACKDEPLOYMENT_H diff --git a/include/swift/Runtime/CMakeConfig.h.in b/include/swift/Runtime/CMakeConfig.h.in index 5a19acb3adaa7..5bf783b453e05 100644 --- a/include/swift/Runtime/CMakeConfig.h.in +++ b/include/swift/Runtime/CMakeConfig.h.in @@ -5,5 +5,7 @@ #define SWIFT_RUNTIME_CMAKECONFIG_H #cmakedefine01 SWIFT_DARWIN_ENABLE_STABLE_ABI_BIT +#cmakedefine01 SWIFT_BNI_OS_BUILD +#cmakedefine01 SWIFT_BNI_XCODE_BUILD #endif diff --git a/include/swift/Runtime/CMakeLists.txt b/include/swift/Runtime/CMakeLists.txt index 4b0687145867b..7dea0d2c2a39f 100644 --- a/include/swift/Runtime/CMakeLists.txt +++ b/include/swift/Runtime/CMakeLists.txt @@ -1,2 +1,13 @@ +# Detect B&I builds. +set(SWIFT_BNI_OS_BUILD FALSE) +set(SWIFT_BNI_XCODE_BUILD FALSE) +if(DEFINED ENV{RC_XBS}) + if(NOT DEFINED ENV{RC_XCODE} OR NOT "$ENV{RC_XCODE}") + set(SWIFT_BNI_OS_BUILD TRUE) + else() + set(SWIFT_BNI_XCODE_BUILD TRUE) + endif() +endif() + configure_file(CMakeConfig.h.in ${CMAKE_CURRENT_BINARY_DIR}/CMakeConfig.h ESCAPE_QUOTES @ONLY) diff --git a/include/swift/Runtime/Config.h b/include/swift/Runtime/Config.h index 94eace943b2c7..7377828924de6 100644 --- a/include/swift/Runtime/Config.h +++ b/include/swift/Runtime/Config.h @@ -141,10 +141,27 @@ /// Which bits in the class metadata are used to distinguish Swift classes /// from ObjC classes? #ifndef SWIFT_CLASS_IS_SWIFT_MASK -# if defined(__APPLE__) && SWIFT_OBJC_INTEROP && SWIFT_DARWIN_ENABLE_STABLE_ABI_BIT + +// Non-Apple platforms always use 1. +# if !defined(__APPLE__) +# define SWIFT_CLASS_IS_SWIFT_MASK 1ULL + +// Builds for Swift-in-the-OS always use 2. +# elif SWIFT_BNI_OS_BUILD # define SWIFT_CLASS_IS_SWIFT_MASK 2ULL -# else + +// Builds for Xcode always use 1. +# elif SWIFT_BNI_XCODE_BUILD # define SWIFT_CLASS_IS_SWIFT_MASK 1ULL + +// Other builds (such as local builds on developers' computers) +// dynamically choose the bit at runtime based on the current OS +// version. +# else +# define SWIFT_CLASS_IS_SWIFT_MASK _swift_classIsSwiftMask +# define SWIFT_CLASS_IS_SWIFT_MASK_GLOBAL_VARIABLE 1 +# include "BackDeployment.h" + # endif #endif diff --git a/include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h b/include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h index 16d3661471144..01c12cabf0317 100644 --- a/include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h +++ b/include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h @@ -37,6 +37,8 @@ extern "C" { #endif +extern unsigned long long swift_reflection_classIsSwiftMask; + /// Get the metadata version supported by the Remote Mirror library. SWIFT_REMOTE_MIRROR_LINKAGE uint16_t swift_reflection_getSupportedMetadataVersion(void); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 48486e5079e49..35e2041ca3166 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -429,7 +429,11 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.EnableSILOpaqueValues |= Args.hasArg(OPT_enable_sil_opaque_values); #if SWIFT_DARWIN_ENABLE_STABLE_ABI_BIT - Opts.UseDarwinPreStableABIBit = false; + Opts.UseDarwinPreStableABIBit = + (Target.isMacOSX() && Target.isMacOSXVersionLT(10, 14, 4)) || + (Target.isiOS() && Target.isOSVersionLT(12, 2)) || + (Target.isTvOS() && Target.isOSVersionLT(12, 2)) || + (Target.isWatchOS() && Target.isOSVersionLT(5, 2)); #else Opts.UseDarwinPreStableABIBit = true; #endif diff --git a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp index 2491668b7a1d9..b6536d0baff49 100644 --- a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp +++ b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp @@ -10,6 +10,9 @@ // //===----------------------------------------------------------------------===// +#define SWIFT_CLASS_IS_SWIFT_MASK swift_reflection_classIsSwiftMask +extern "C" unsigned long long swift_reflection_classIsSwiftMask = 2; + #include "swift/Reflection/ReflectionContext.h" #include "swift/Reflection/TypeLowering.h" #include "swift/Remote/CMemoryReader.h" diff --git a/stdlib/public/runtime/BackDeployment.cpp b/stdlib/public/runtime/BackDeployment.cpp new file mode 100644 index 0000000000000..c92c2db104e75 --- /dev/null +++ b/stdlib/public/runtime/BackDeployment.cpp @@ -0,0 +1,64 @@ +//===--- BackDeployment.cpp - Support for running on older OS versions. ---===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 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/BackDeployment.h" +#include "swift/Runtime/Config.h" +#include "../SwiftShims/FoundationShims.h" +#include + +#if defined(__APPLE__) && defined(__MACH__) + +#if SWIFT_CLASS_IS_SWIFT_MASK_GLOBAL_VARIABLE +static unsigned long long computeIsSwiftMask() { + if (swift::_swift_isBackDeploying()) + return 1ULL; + return 2ULL; +} + +SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_BEGIN +extern "C" unsigned long long +_swift_classIsSwiftMask = computeIsSwiftMask(); +SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_END +#endif // SWIFT_CLASS_IS_SWIFT_MASK_GLOBAL_VARIABLE + +static swift::_SwiftNSOperatingSystemVersion swiftInOSVersion = { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + 10, 14, 4 +// WatchOS also pretends to be iOS, so check it first. +#elif __WATCH_OS_VERSION_MIN_REQUIRED + 5, 2, 0 +#elif __IPHONE_OS_VERSION_MIN_REQUIRED || __TV_OS_VERSION_MIN_REQUIRED + 12, 2, 0 +#else + 9999, 0, 0 +#endif +}; + +static bool versionLessThan(swift::_SwiftNSOperatingSystemVersion lhs, + swift::_SwiftNSOperatingSystemVersion rhs) { + if (lhs.majorVersion < rhs.majorVersion) return true; + if (lhs.majorVersion > rhs.majorVersion) return false; + + if (lhs.minorVersion < rhs.minorVersion) return true; + if (lhs.minorVersion > rhs.minorVersion) return false; + + if (lhs.patchVersion < rhs.patchVersion) return true; + + return false; +} + +SWIFT_RUNTIME_STDLIB_INTERNAL +int _swift_isBackDeploying() { + auto version = swift::_swift_stdlib_operatingSystemVersion(); + return versionLessThan(version, swiftInOSVersion); +} +#endif diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index db0f67de5e4e3..8f5d79fb6be49 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -31,6 +31,7 @@ set(swift_runtime_objc_sources set(swift_runtime_sources AnyHashableSupport.cpp Array.cpp + BackDeployment.cpp Casting.cpp CompatibilityOverride.cpp CygwinPort.cpp diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 7d06adf85c8f3..d08fb47ba14df 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -96,7 +96,15 @@ Metadata *TargetSingletonMetadataInitialization::allocate( // If this is a class, we have to initialize the value witness table early // so that two-phase initialization can proceed as if this metadata is // complete for layout purposes when it appears as part of an aggregate type. - if (auto *classMetadata = dyn_cast(metadata)) { + // + // Note that we can't use (dyn_)cast here because the static + // template may have the "wrong" isSwift bit set in its Data pointer, if the + // binary was built to deploy back to pre-stable-Swift Objective-C runtimes. + // Such a template will fail the `isTypeMetadata` test and we'll think that it + // isn't Swift metadata but a plain old ObjC class instead. + if (metadata->getKind() == MetadataKind::Class) { + auto *classMetadata = static_cast(metadata); + classMetadata->setAsTypeMetadata(); auto *fullMetadata = asFullMetadata(metadata); // Begin by initializing the value witness table; everything else is diff --git a/stdlib/tools/swift-reflection-test/swift-reflection-test.c b/stdlib/tools/swift-reflection-test/swift-reflection-test.c index 98980f8f4f1f7..0f8d5b34af4a7 100644 --- a/stdlib/tools/swift-reflection-test/swift-reflection-test.c +++ b/stdlib/tools/swift-reflection-test/swift-reflection-test.c @@ -551,6 +551,20 @@ int doDumpHeapInstance(const char *BinaryFilename) { return EXIT_SUCCESS; } +#if defined(__APPLE__) && defined(__MACH__) +#include +static unsigned long long computeClassIsSwiftMask(void) { + uintptr_t *objc_debug_swift_stable_abi_bit_ptr = + (uintptr_t *)dlsym(RTLD_DEFAULT, "objc_debug_swift_stable_abi_bit"); + return objc_debug_swift_stable_abi_bit_ptr ? + *objc_debug_swift_stable_abi_bit_ptr : 1; +} +#else +static unsigned long long computeClassIsSwiftMask(void) { + return 1; +} +#endif + void printUsageAndExit() { fprintf(stderr, "swift-reflection-test \n"); exit(EXIT_FAILURE); @@ -561,6 +575,8 @@ int main(int argc, char *argv[]) { printUsageAndExit(); const char *BinaryFilename = argv[1]; + + swift_reflection_classIsSwiftMask = computeClassIsSwiftMask(); uint16_t Version = swift_reflection_getSupportedMetadataVersion(); printf("Metadata version: %u\n", Version); diff --git a/test/IRGen/class.sil b/test/IRGen/class.sil index 3dde15cbe5162..57b0b62c6f400 100644 --- a/test/IRGen/class.sil +++ b/test/IRGen/class.sil @@ -41,7 +41,7 @@ sil_vtable C {} // CHECK-INDIRECT-SAME: %swift.type* null, // CHECK-SAME: [[OPAQUE]]* @_objc_empty_cache, // CHECK-SAME: [[OPAQUE]]* null, -// CHECK-SAME: i64 add (i64 ptrtoint ({{.*}}* @_DATA__TtC5class1C to i64), i64 1) +// CHECK-SAME: i64 add (i64 ptrtoint ({{.*}}* @_DATA__TtC5class1C to i64), i64 [[IS_SWIFT_BIT:1|2]]) // CHECK-SAME: }> // Destroying destructor diff --git a/test/IRGen/class_resilience.swift b/test/IRGen/class_resilience.swift index 4066f8ffb1947..e7d1f03dd20d0 100644 --- a/test/IRGen/class_resilience.swift +++ b/test/IRGen/class_resilience.swift @@ -92,7 +92,7 @@ // -- ivar destroyer: // CHECK-SAME: i32 0, // -- flags: -// CHECK-SAME: i32 3, +// CHECK-SAME: i32 2, // -- RO data: // CHECK-objc-SAME: @_DATA__TtC16class_resilience14ResilientChild // CHECK-native-SAME: i32 0, diff --git a/test/IRGen/subclass.swift b/test/IRGen/subclass.swift index c611f8b841006..447317e74f567 100644 --- a/test/IRGen/subclass.swift +++ b/test/IRGen/subclass.swift @@ -20,7 +20,7 @@ // CHECK-INDIRECT-SAME: [[TYPE]]* null, // CHECK-SAME: [[OPAQUE]]* @_objc_empty_cache, // CHECK-SAME: [[OPAQUE]]* null, -// CHECK-SAME: i64 add (i64 ptrtoint ({ {{.*}} }* @_DATA__TtC8subclass1A to i64), i64 1), +// CHECK-SAME: i64 add (i64 ptrtoint ({ {{.*}} }* @_DATA__TtC8subclass1A to i64), i64 [[IS_SWIFT_BIT:1|2]]), // CHECK-SAME: i64 ([[A]]*)* @"$s8subclass1AC1fSiyF", // CHECK-SAME: [[A]]* ([[TYPE]]*)* @"$s8subclass1AC1gACyFZ" // CHECK-SAME: }> @@ -34,7 +34,7 @@ // CHECK-INDIRECT-SAME: [[TYPE]]* null, // CHECK-SAME: [[OPAQUE]]* @_objc_empty_cache, // CHECK-SAME: [[OPAQUE]]* null, -// CHECK-SAME: i64 add (i64 ptrtoint ({ {{.*}} }* @_DATA__TtC8subclass1B to i64), i64 1), +// CHECK-SAME: i64 add (i64 ptrtoint ({ {{.*}} }* @_DATA__TtC8subclass1B to i64), i64 [[IS_SWIFT_BIT]]), // CHECK-SAME: i64 ([[B]]*)* @"$s8subclass1BC1fSiyF", // CHECK-SAME: [[A]]* ([[TYPE]]*)* @"$s8subclass1AC1gACyFZ" // CHECK-SAME: }> diff --git a/test/Runtime/stable-bit-backward-deployment.swift b/test/Runtime/stable-bit-backward-deployment.swift new file mode 100644 index 0000000000000..aa0e7b33dea43 --- /dev/null +++ b/test/Runtime/stable-bit-backward-deployment.swift @@ -0,0 +1,29 @@ +// RUN: %empty-directory(%t) +// -- Deployment target is set to pre-10.14.4 so that we use the "old" +// Swift runtime bit in compiler-emitted classes +// RUN: %target-build-swift -target x86_64-apple-macosx10.9 %s -module-name main -o %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: OS=macosx + +import Foundation + +// A fixed-layout class should be considered Swift metadata by the OS runtime. +class FixedLayout { } + +debugPrint(FixedLayout.self) // CHECK: main.FixedLayout + +// A generic class +class GenericBase { } +debugPrint(GenericBase.self) // CHECK-NEXT: main.GenericBase + +// A singleton-initialized class +class SingletonInit: GenericBase { } +debugPrint(SingletonInit.self) // CHECK-NEXT: main.SingletonInit + +// A resilient-heritage class +class ResilientSubInit: JSONEncoder {} +debugPrint(ResilientSubInit.self) // CHECK-NEXT: main.ResilientSubInit + +print("nailed it") // CHECK-NEXT: nailed it diff --git a/tools/swift-remoteast-test/swift-remoteast-test.cpp b/tools/swift-remoteast-test/swift-remoteast-test.cpp index 6d6f207fefb70..391c7b3edb98e 100644 --- a/tools/swift-remoteast-test/swift-remoteast-test.cpp +++ b/tools/swift-remoteast-test/swift-remoteast-test.cpp @@ -38,6 +38,23 @@ using namespace swift; using namespace swift::remote; using namespace swift::remoteAST; +#if defined(__APPLE__) && defined(__MACH__) +#include +static unsigned long long computeClassIsSwiftMask(void) { + uintptr_t *objc_debug_swift_stable_abi_bit_ptr = + (uintptr_t *)dlsym(RTLD_DEFAULT, "objc_debug_swift_stable_abi_bit"); + return objc_debug_swift_stable_abi_bit_ptr ? + *objc_debug_swift_stable_abi_bit_ptr : 1; +} +#else +static unsigned long long computeClassIsSwiftMask(void) { + return 1; +} +#endif + +extern "C" unsigned long long _swift_classIsSwiftMask = + computeClassIsSwiftMask(); + /// The context for the code we're running. Set by the observer. static ASTContext *context = nullptr;