Skip to content
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
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

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

Any point in leaving this option behind? I think that we could just remove it right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not much point other than just being incremental. I think I'd like to rip out the option as part of a separate task.


set(SWIFT_DARWIN_XCRUN_TOOLCHAIN "XcodeDefault" CACHE STRING
"The name of the toolchain to pass to 'xcrun'")
Expand Down
25 changes: 25 additions & 0 deletions include/swift/ABI/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,31 @@ struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> {
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<Runtime> *metadata) {
return metadata->getKind() == MetadataKind::Class;
}
Expand Down
45 changes: 45 additions & 0 deletions include/swift/Runtime/BackDeployment.h
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions include/swift/Runtime/CMakeConfig.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
11 changes: 11 additions & 0 deletions include/swift/Runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
21 changes: 19 additions & 2 deletions include/swift/Runtime/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions include/swift/SwiftRemoteMirror/SwiftRemoteMirror.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 5 additions & 1 deletion lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
64 changes: 64 additions & 0 deletions stdlib/public/runtime/BackDeployment.cpp
Original file line number Diff line number Diff line change
@@ -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 <stdlib.h>

#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
Copy link

Choose a reason for hiding this comment

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

These.

// 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
Copy link
Member

Choose a reason for hiding this comment

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

Might be nice to have these version tuples statically defined always and just select between the right one based on the deployment target.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it's roughly the same ugliness either way.

};

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
1 change: 1 addition & 0 deletions stdlib/public/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion stdlib/public/runtime/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,15 @@ Metadata *TargetSingletonMetadataInitialization<InProcess>::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<ClassMetadata>(metadata)) {
//
// Note that we can't use (dyn_)cast<ClassMetadata> 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<ClassMetadata*>(metadata);
classMetadata->setAsTypeMetadata();
auto *fullMetadata = asFullMetadata(metadata);

// Begin by initializing the value witness table; everything else is
Expand Down
16 changes: 16 additions & 0 deletions stdlib/tools/swift-reflection-test/swift-reflection-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,20 @@ int doDumpHeapInstance(const char *BinaryFilename) {
return EXIT_SUCCESS;
}

#if defined(__APPLE__) && defined(__MACH__)
#include <dlfcn.h>
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 <binary filename>\n");
exit(EXIT_FAILURE);
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/class.sil
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/class_resilience.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions test/IRGen/subclass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: }>
Expand All @@ -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: }>
Expand Down
29 changes: 29 additions & 0 deletions test/Runtime/stable-bit-backward-deployment.swift
Original file line number Diff line number Diff line change
@@ -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<T> { }
debugPrint(GenericBase<Int>.self) // CHECK-NEXT: main.GenericBase<Swift.Int>

// A singleton-initialized class
class SingletonInit: GenericBase<Int> { }
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
17 changes: 17 additions & 0 deletions tools/swift-remoteast-test/swift-remoteast-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,23 @@ using namespace swift;
using namespace swift::remote;
using namespace swift::remoteAST;

#if defined(__APPLE__) && defined(__MACH__)
#include <dlfcn.h>
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;

Expand Down