Skip to content

Commit

Permalink
Runtime: Use 'once' instead of static local variable initialization.
Browse files Browse the repository at this point in the history
The C++ ABI for static locals is a bit heavy compared to dispatch_once; doing this saves more than 1KB in runtime code size. Dispatch_once/call_once is also more likely to be hot because it's also used by Swift and ObjC code.

Alas, llvm::get_execution_seed() from llvm/ADT/Hashing.h still inflicts one static local initialization on us we can't override (without forking Hashing.h, anyway).
  • Loading branch information
jckarter committed Nov 30, 2015
1 parent 4a11d81 commit 5e5cdc6
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 33 deletions.
26 changes: 17 additions & 9 deletions include/swift/Basic/Lazy.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,22 @@ namespace swift {
# define SWIFT_ONCE_F(TOKEN, FUNC, CONTEXT) \
std::call_once(TOKEN, FUNC, CONTEXT)
#endif

/// A template for lazily-constructed, zero-initialized, leaked-on-exit
/// global objects.
template <class T> class Lazy {
typename std::aligned_storage<sizeof(T), alignof(T)>::type Value;

OnceToken_t OnceToken;

static void defaultInitCallback(void *ValueAddr) {
::new (ValueAddr) T();
}

public:
T &get();
using Type = T;

T &get(void (*initCallback)(void *) = defaultInitCallback);

/// Get the value, assuming it must have already been initialized by this
/// point.
Expand All @@ -53,23 +59,25 @@ template <class T> class Lazy {
T &operator*() { return get(); }

private:
static void lazyInitCallback(void *Argument) {
auto self = reinterpret_cast<Lazy *>(Argument);
::new (&self->unsafeGetAlreadyInitialized()) T();
}

Lazy(const Lazy &) = delete;
Lazy &operator=(const Lazy &) = delete;
};

template <typename T> inline T &Lazy<T>::get() {
template <typename T> inline T &Lazy<T>::get(void (*initCallback)(void*)) {
static_assert(std::is_literal_type<Lazy<T>>::value,
"Lazy<T> must be a literal type");

SWIFT_ONCE_F(OnceToken, lazyInitCallback, this);
SWIFT_ONCE_F(OnceToken, initCallback, &Value);
return unsafeGetAlreadyInitialized();
}

} // namespace swift

#define SWIFT_LAZY_CONSTANT(INITIAL_VALUE) \
({ \
using T = std::remove_reference<decltype(INITIAL_VALUE)>::type; \
static Lazy<T> TheLazy; \
TheLazy.get([](void *ValueAddr){ ::new(ValueAddr) T{INITIAL_VALUE}; });\
})

#endif // SWIFT_BASIC_LAZY_H
5 changes: 2 additions & 3 deletions stdlib/public/runtime/Casting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1448,9 +1448,8 @@ static const WitnessTable *findErrorTypeWitness(const Metadata *srcType) {
}

static const Metadata *getNSErrorTypeMetadata() {
static auto TheNSErrorMetadata
= swift_getObjCClassMetadata((const ClassMetadata *)getNSErrorClass());
return TheNSErrorMetadata;
return SWIFT_LAZY_CONSTANT(
swift_getObjCClassMetadata((const ClassMetadata *)getNSErrorClass()));
}
#endif

Expand Down
38 changes: 19 additions & 19 deletions stdlib/public/runtime/ErrorObject.mm
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include "swift/Runtime/Debug.h"
#include "swift/Runtime/ObjCBridge.h"
#include "swift/Basic/Lazy.h"
#include "ErrorObject.h"
#include "Private.h"
#include <dlfcn.h>
Expand Down Expand Up @@ -83,21 +84,19 @@ - (NSDictionary*)userInfo {
return (NSDictionary*)userInfo;
} else {
// -[NSError userInfo] never returns nil on OS X 10.8 or later.
static NSDictionary *emptyDict = @{};
NSDictionary *emptyDict = SWIFT_LAZY_CONSTANT(@{});
return emptyDict;
}
}

@end

Class swift::getNSErrorClass() {
static auto TheNSError = [NSError class];
return TheNSError;
return SWIFT_LAZY_CONSTANT([NSError class]);
}

static Class getSwiftNativeNSErrorClass() {
static auto TheSwiftNativeNSError = [_SwiftNativeNSError class];
return TheSwiftNativeNSError;
return SWIFT_LAZY_CONSTANT([_SwiftNativeNSError class]);
}

/// Allocate a catchable error object.
Expand Down Expand Up @@ -183,8 +182,8 @@ static Class getSwiftNativeNSErrorClass() {
// to assume that that's been linked in if a user is using NSError in their
// Swift source.

static auto TheWitnessTable = dlsym(RTLD_DEFAULT,
"_TWPCSo7CFErrors9ErrorType10Foundation");
auto TheWitnessTable = SWIFT_LAZY_CONSTANT(dlsym(RTLD_DEFAULT,
"_TWPCSo7CFErrors9ErrorType10Foundation"));
assert(TheWitnessTable &&
"Foundation overlay not loaded, or CFError: ErrorType conformance "
"not available");
Expand Down Expand Up @@ -337,20 +336,21 @@ static id _swift_bridgeErrorTypeToNSError_(SwiftError *errorObject) {
const Metadata *srcType,
const Metadata *destType,
DynamicCastFlags flags) {
Class TheNSErrorClass = [NSError class];
static CFTypeID TheCFErrorTypeID = CFErrorGetTypeID();
Class NSErrorClass = getNSErrorClass();

auto CFErrorTypeID = SWIFT_LAZY_CONSTANT(CFErrorGetTypeID());
// @_silgen_name("swift_stdlib_bridgeNSErrorToErrorType")
// public func _stdlib_bridgeNSErrorToErrorType<
// T : _ObjectiveCBridgeableErrorType
// >(error: NSError, out: UnsafeMutablePointer<T>) -> Bool {
static auto bridgeNSErrorToErrorType
= reinterpret_cast<bool (*)(NSError *, OpaqueValue*, const Metadata *,
const WitnessTable *)>
(dlsym(RTLD_DEFAULT, "swift_stdlib_bridgeNSErrorToErrorType"));
auto bridgeNSErrorToErrorType = SWIFT_LAZY_CONSTANT(
reinterpret_cast<bool (*)(NSError *, OpaqueValue*, const Metadata *,
const WitnessTable *)>
(dlsym(RTLD_DEFAULT, "swift_stdlib_bridgeNSErrorToErrorType")));
// protocol _ObjectiveCBridgeableErrorType
static auto TheObjectiveCBridgeableErrorTypeProtocol
= reinterpret_cast<const ProtocolDescriptor *>
(dlsym(RTLD_DEFAULT, "_TMp10Foundation30_ObjectiveCBridgeableErrorType"));
auto TheObjectiveCBridgeableErrorTypeProtocol = SWIFT_LAZY_CONSTANT(
reinterpret_cast<const ProtocolDescriptor *>(dlsym(RTLD_DEFAULT,
"_TMp10Foundation30_ObjectiveCBridgeableErrorType")));

// If the Foundation overlay isn't loaded, then NSErrors can't be bridged.
if (!bridgeNSErrorToErrorType || !TheObjectiveCBridgeableErrorTypeProtocol)
Expand All @@ -360,21 +360,21 @@ static id _swift_bridgeErrorTypeToNSError_(SwiftError *errorObject) {
switch (srcType->getKind()) {
case MetadataKind::Class:
// Native class should be an NSError subclass.
if (![(Class)srcType isSubclassOfClass: TheNSErrorClass])
if (![(Class)srcType isSubclassOfClass: NSErrorClass])
return false;
break;
case MetadataKind::ForeignClass: {
// Foreign class should be CFError.
CFTypeRef srcInstance = *reinterpret_cast<CFTypeRef *>(src);
if (CFGetTypeID(srcInstance) != TheCFErrorTypeID)
if (CFGetTypeID(srcInstance) != CFErrorTypeID)
return false;
break;
}
case MetadataKind::ObjCClassWrapper: {
// ObjC class should be an NSError subclass.
auto srcWrapper = static_cast<const ObjCClassWrapperMetadata *>(srcType);
if (![(Class)srcWrapper->getClassObject()
isSubclassOfClass: TheNSErrorClass])
isSubclassOfClass: NSErrorClass])
return false;
break;
}
Expand Down
4 changes: 2 additions & 2 deletions stdlib/public/stubs/Stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <limits>
#include "llvm/ADT/StringExtras.h"
#include "swift/Runtime/Debug.h"
#include "swift/Basic/Lazy.h"

static uint64_t uint64ToStringImpl(char *Buffer, uint64_t Value,
int64_t Radix, bool Uppercase,
Expand Down Expand Up @@ -108,8 +109,7 @@ static locale_t makeCLocale() {
}

static locale_t getCLocale() {
static locale_t CLocale = makeCLocale();
return CLocale;
return SWIFT_LAZY_CONSTANT(makeCLocale());
}
#endif

Expand Down

0 comments on commit 5e5cdc6

Please sign in to comment.