Skip to content

[runtime] Remove TwoWordPair and use the Swift calling convention instead. #13299

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 8 commits into from
Dec 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
6 changes: 4 additions & 2 deletions include/swift/Runtime/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@
// convention.
#define SWIFT_LLVM_CC(CC) SWIFT_LLVM_CC_##CC

// Currently, RuntimeFunction.def uses the following calling conventions:
// DefaultCC, RegisterPreservingCC.
// Currently, RuntimeFunctions.def uses the following calling conventions:
// DefaultCC, RegisterPreservingCC, SwiftCC.
// If new runtime calling conventions are added later, they need to be mapped
// here to something appropriate.

Expand All @@ -131,6 +131,8 @@
#define SWIFT_CC_DefaultCC_IMPL SWIFT_CC_c
#define SWIFT_LLVM_CC_DefaultCC llvm::CallingConv::C

#define SWIFT_CC_SwiftCC SWIFT_CC_swift
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The Swift calling convention is called SWIFT_CC_swift directly and SWIFT_LLVM_CC_SwiftCC to refer to the LLVM name. These need to match (either swift or SwiftCC) for the purposes of the macros in RuntimeFunctions.def, so I chose to alias SwiftCC to swift here.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand why this is necessary. RuntimeFunctions.def already uses SwiftCC for two functions. Why is this only now a problem?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previously, both SwiftCC functions in RuntimeFunctions.def used the FUNCTION macro. However, swift_allocBox uses FUNCTION_WITH_GLOBAL_SYMBOL_AND_IMPL.

In RuntimeEntrySymbols.cpp, that FUNCTION_WITH_GLOBAL_SYMBOL_AND_IMPL expands into DEFINE_SYMBOL, whereas FUNCTION, as defined in that same file, expands to nothing for any calling convention other than RegisterPreservingCC.

DEFINE_SYMBOL uses the SWIFT_CC macro, which expands to SWIFT_CC_##CC. Since SWIFT_CC_SwiftCC was undefined, I defined it here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Got it. That's fine.


#define SWIFT_LLVM_CC_RegisterPreservingCC llvm::CallingConv::PreserveMost

#if SWIFT_USE_SWIFTCALL
Expand Down
81 changes: 15 additions & 66 deletions include/swift/Runtime/HeapObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,66 +97,10 @@ HeapObject *swift_initStaticObject(HeapMetadata const *metadata,
SWIFT_RUNTIME_EXPORT
void swift_verifyEndOfLifetime(HeapObject *object);

/// A structure that's two pointers in size.
///
/// C functions can use the TwoWordPair::Return type to return a value in
/// two registers, compatible with Swift's calling convention for tuples
/// and structs of two word-sized elements.
template<typename A, typename B>
struct TwoWordPair {
A first;
B second;

TwoWordPair() = default;
TwoWordPair(A first, B second);

// FIXME: rdar://16257592 arm codegen doesn't call swift_allocBox correctly.
Copy link
Contributor

Choose a reason for hiding this comment

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

rdar://16257592 was closed long ago, so we're good here.

// Structs are returned indirectly on these platforms, but we want to return
// in registers, so cram the result into an unsigned long long.
// Use an enum class with implicit conversions so we don't dirty C callers
// too much.
#if __arm__ || __i386__ || defined(__CYGWIN__) || defined(_MSC_VER)
Copy link
Contributor Author

@troughton troughton Dec 6, 2017

Choose a reason for hiding this comment

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

One consideration is that this could cause any of these platforms that tries to build without SWIFT_USE_SWIFTCALL (e.g. with an unsupported Clang) to silently break. Is there any reason that SWIFT_USE_SWIFTCALL can't be required now, so not supporting it is an error? Otherwise it might be worth adding back an #if block for only these platforms with an #error that the Swift calling convention must be enabled.

Copy link
Contributor

@gparker42 gparker42 Dec 6, 2017

Choose a reason for hiding this comment

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

Good point. If we start requiring SWIFT_USE_SWIFTCALL then we ought to remove its other uses in Config.h and add the #error check there.

I see at least one reference to the C compiler being MSVC (swift/Basic/Compiler.h). I assume it has no swiftcall support. Do we actually support MSVC? Is anyone using it?

Formally requiring recent clang would be worth a heads-up notice or a discussion on swift-evolution.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, I see that the default definition of SWIFT_USE_SWIFTCALL in Config.h still disables it. Are you building locally with it enabled?

Copy link
Contributor Author

@troughton troughton Dec 7, 2017

Choose a reason for hiding this comment

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

Yes; I've submitted a PR for Clang that means we can enable it on Windows.

As for MSVC: I know @hughbe was doing a lot of work around getting the compiler building under MSVC. However, if I'm not mistaken, SWIFT_USE_SWIFTCALL and its descendants are only used in the stdlib/runtime, which is normally built using the just-built Clang anyway. The requirement then is a little less strict: the stdlib/runtime must be built with a recent Clang - and I think that was already a requirement.

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 that distinction will work. Probably looks something like this:

  • Remove SWIFT_USE_SWIFTCALL from Config.h. (In the interim add some #if !SWIFT_USE_SWIFTCALL #error "SWIFT_USE_SWIFTCALL=0 is not supported" to catch anyone switching it externally.)
  • Use __has_attribute(swiftcall) to define the macros formerly controlled by SWIFT_USE_SWIFTCALL. Or possibly define them unconditionally, if they are never used in code that is included in the compiler's build.
  • Add __has_attribute(swiftcall) enforcement somewhere in runtime-only code. I don't know of any established place for such a thing.

#if defined(__CYGWIN__)
enum class Return : unsigned __int128 {};
#else
enum class Return : unsigned long long {};
#endif

operator Return() const {
union {
TwoWordPair value;
Return mangled;
} reinterpret = {*this};

return reinterpret.mangled;
}

/*implicit*/ TwoWordPair(Return r) {
union {
Return mangled;
TwoWordPair value;
} reinterpret = {r};

*this = reinterpret.value;
}
#else
using Return = TwoWordPair;
#endif
struct BoxPair {
HeapObject *object;
OpaqueValue *buffer;
};

template<typename A, typename B>
inline TwoWordPair<A,B>::TwoWordPair(A first, B second)
: first(first), second(second)
{
static_assert(sizeof(A) == sizeof(void*),
"first type must be word-sized");
static_assert(sizeof(B) == sizeof(void*),
"second type must be word-sized");
static_assert(alignof(TwoWordPair) == alignof(void*),
"pair must be word-aligned");
}

using BoxPair = TwoWordPair<HeapObject *, OpaqueValue *>;

/// Allocates a heap object that can contain a value of the given type.
/// Returns a Box structure containing a HeapObject* pointer to the
Expand All @@ -165,19 +109,19 @@ using BoxPair = TwoWordPair<HeapObject *, OpaqueValue *>;
/// appropriate to store a value of the given type.
/// The heap object has an initial retain count of 1, and its metadata is set
/// such that destroying the heap object destroys the contained value.
SWIFT_RUNTIME_EXPORT
BoxPair::Return swift_allocBox(Metadata const *type);
SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT
BoxPair swift_allocBox(Metadata const *type);

SWIFT_RUNTIME_EXPORT
BoxPair::Return (*_swift_allocBox)(Metadata const *type);
SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT
BoxPair (*_swift_allocBox)(Metadata const *type);

/// Performs a uniqueness check on the pointer to a box structure. If the check
/// fails allocates a new box and stores the pointer in the buffer.
///
/// if (!isUnique(buffer[0]))
/// buffer[0] = swift_allocBox(type)
SWIFT_RUNTIME_EXPORT
BoxPair::Return swift_makeBoxUnique(OpaqueValue *buffer, Metadata const *type,
SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT
BoxPair swift_makeBoxUnique(OpaqueValue *buffer, Metadata const *type,
size_t alignMask);

/// Returns the address of a heap object representing all empty box types.
Expand Down Expand Up @@ -1242,11 +1186,16 @@ static inline bool swift_unknownUnownedIsEqual(UnownedReference *ref,

#endif /* SWIFT_OBJC_INTEROP */

struct TypeNamePair {
const char *data;
uintptr_t length;
};

/// Return the name of a Swift type represented by a metadata object.
/// func _getTypeName(_ type: Any.Type, qualified: Bool)
/// -> (UnsafePointer<UInt8>, Int)
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
TwoWordPair<const char *, uintptr_t>::Return
TypeNamePair
swift_getTypeName(const Metadata *type, bool qualified);

} // end namespace swift
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Runtime/InstrumentsSupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ HeapObject *(*_swift_allocObject)(HeapMetadata const *metadata,
size_t requiredAlignmentMask);

SWIFT_RUNTIME_EXPORT
BoxPair::Return (*_swift_allocBox)(Metadata const *type);
BoxPair (*swift_allocBox)(Metadata const *type);

SWIFT_RUNTIME_EXPORT
HeapObject *(*_swift_retain)(HeapObject *object);
Expand Down
6 changes: 3 additions & 3 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@
#endif

FUNCTION_WITH_GLOBAL_SYMBOL_AND_IMPL(AllocBox, swift_allocBox,
_swift_allocBox, _swift_allocBox_, DefaultCC,
_swift_allocBox, _swift_allocBox_, SwiftCC,
RETURNS(RefCountedPtrTy, OpaquePtrTy),
ARGS(TypeMetadataPtrTy),
ATTRS(NoUnwind))

// BoxPair swift_makeBoxUnique(OpaqueValue *buffer, Metadata *type, size_t alignMask);
FUNCTION(MakeBoxUnique,
swift_makeBoxUnique,
DefaultCC,
SwiftCC,
RETURNS(RefCountedPtrTy, OpaquePtrTy),
ARGS(OpaquePtrTy, TypeMetadataPtrTy, SizeTy),
ATTRS(NoUnwind))
Expand Down Expand Up @@ -1332,7 +1332,7 @@ FUNCTION(DeletedMethodError, swift_deletedMethodError, C_CC,
ARGS(),
ATTRS(NoUnwind))

FUNCTION(AllocError, swift_allocError, C_CC,
FUNCTION(AllocError, swift_allocError, SwiftCC,
RETURNS(ErrorPtrTy, OpaquePtrTy),
ARGS(TypeMetadataPtrTy, WitnessTablePtrTy, OpaquePtrTy, Int1Ty),
ATTRS(NoUnwind))
Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/IRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,10 @@ llvm::Constant *swift::getWrapperFn(llvm::Module &Module,
RETURNS, ARGS, ATTRS) \
FUNCTION_IMPL(ID, NAME, CC, QUOTE(RETURNS), QUOTE(ARGS), QUOTE(ATTRS))

#define FUNCTION_WITH_GLOBAL_SYMBOL_FOR_CONV_SwiftCC(ID, NAME, SYMBOL, CC, \
RETURNS, ARGS, ATTRS) \
FUNCTION_IMPL(ID, NAME, CC, QUOTE(RETURNS), QUOTE(ARGS), QUOTE(ATTRS))
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't it a problem to drop the SYMBOL part here? I thought that's what you were trying to fix on Windows.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not actually unused. Like for the other calling conventions in this file, the SYMBOL arg is dropped here but used in RuntimeEntrySymbols.cpp. I only had to add this macro here because SwiftCC wasn't previously being used on a global symbol.

This 'fix' for Windows is really a unification across all platforms by making sure the calling convention for these functions is guaranteed to be compatible with Swift.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oops, didn't think to check for that. Thanks for the explanation.


#define FUNCTION_WITH_GLOBAL_SYMBOL_FOR_CONV_RegisterPreservingCC( \
ID, NAME, SYMBOL, CC, RETURNS, ARGS, ATTRS) \
FUNCTION_WITH_GLOBAL_SYMBOL_IMPL(ID, NAME, SYMBOL, CC, QUOTE(RETURNS), \
Expand Down
6 changes: 3 additions & 3 deletions stdlib/public/SDK/Foundation/CheckClass.mm
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ static void logIfFirstOccurrence(Class objcClass, void (^log)(void)) {
template <size_t N>
StringRefLite(const char (&staticStr)[N]) : data(staticStr), length(N) {}

StringRefLite(swift::TwoWordPair<const char *, uintptr_t>::Return rawValue)
: data(swift::TwoWordPair<const char *, uintptr_t>(rawValue).first),
length(swift::TwoWordPair<const char *, uintptr_t>(rawValue).second){}
StringRefLite(swift::TypeNamePair rawValue)
: data(rawValue.data),
length(rawValue.length){}

NS_RETURNS_RETAINED
NSString *newNSStringNoCopy() const {
Expand Down
13 changes: 6 additions & 7 deletions stdlib/public/runtime/Casting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,8 @@ std::string swift::nameForMetadata(const Metadata *type,
return result;
}

TwoWordPair<const char *, uintptr_t>::Return
TypeNamePair
swift::swift_getTypeName(const Metadata *type, bool qualified) {
using Pair = TwoWordPair<const char *, uintptr_t>;
using Key = llvm::PointerIntPair<const Metadata *, 1, bool>;

static StaticReadWriteLock TypeNameCacheLock;
Expand All @@ -132,7 +131,7 @@ swift::swift_getTypeName(const Metadata *type, bool qualified) {
auto found = cache.find(key);
if (found != cache.end()) {
auto result = found->second;
return Pair{result.first, result.second};
return TypeNamePair{result.first, result.second};
}
}

Expand All @@ -145,7 +144,7 @@ swift::swift_getTypeName(const Metadata *type, bool qualified) {
auto found = cache.find(key);
if (found != cache.end()) {
auto result = found->second;
return Pair{result.first, result.second};
return TypeNamePair{result.first, result.second};
}

// Build the metadata name.
Expand All @@ -157,7 +156,7 @@ swift::swift_getTypeName(const Metadata *type, bool qualified) {
result[size] = 0;

cache.insert({key, {result, size}});
return Pair{result, size};
return TypeNamePair{result, size};
}
}

Expand Down Expand Up @@ -955,7 +954,7 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
(canConsumeDynamicValue && (flags & DynamicCastFlags::TakeOnSuccess));
BoxPair destBox = swift_allocError(srcDynamicType, errorWitness,
srcDynamicValue, isTake);
*destBoxAddr = reinterpret_cast<SwiftError*>(destBox.first);
*destBoxAddr = reinterpret_cast<SwiftError*>(destBox.object);
maybeDeallocateSource(true);
return true;
}
Expand Down Expand Up @@ -1977,7 +1976,7 @@ static id dynamicCastValueToNSError(OpaqueValue *src,

BoxPair errorBox = swift_allocError(srcType, srcErrorWitness, src,
/*isTake*/ flags & DynamicCastFlags::TakeOnSuccess);
return _swift_stdlib_bridgeErrorToNSError((SwiftError*)errorBox.first);
return _swift_stdlib_bridgeErrorToNSError((SwiftError*)errorBox.object);
}

#endif
Expand Down
8 changes: 4 additions & 4 deletions stdlib/public/runtime/ErrorObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,10 @@ struct SwiftError : SwiftErrorHeader {
/// copied (or taken if \c isTake is true) into the newly-allocated error box.
/// If value is null, the box's contents will be left uninitialized, and
/// \c isTake should be false.
SWIFT_RUNTIME_STDLIB_API
BoxPair::Return swift_allocError(const Metadata *type,
const WitnessTable *errorConformance,
OpaqueValue *value, bool isTake);
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API
BoxPair swift_allocError(const Metadata *type,
const WitnessTable *errorConformance,
OpaqueValue *value, bool isTake);

/// Deallocate an error object whose contained object has already been
/// destroyed.
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/runtime/ErrorObject.mm
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ static Class getSwiftNativeNSErrorClass() {
}

/// Allocate a catchable error object.
BoxPair::Return
BoxPair
swift::swift_allocError(const Metadata *type,
const WitnessTable *errorConformance,
OpaqueValue *initialValue,
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/runtime/ErrorObjectNative.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ static const FullMetadata<HeapMetadata> ErrorMetadata{
Metadata{MetadataKind::ErrorObject},
};

BoxPair::Return
BoxPair
swift::swift_allocError(const swift::Metadata *type,
const swift::WitnessTable *errorConformance,
OpaqueValue *initialValue,
Expand Down
10 changes: 5 additions & 5 deletions stdlib/public/runtime/HeapObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,11 @@ class BoxCacheEntry {

static SimpleGlobalCache<BoxCacheEntry> Boxes;

BoxPair::Return swift::swift_allocBox(const Metadata *type) {
BoxPair swift::swift_allocBox(const Metadata *type) {
return SWIFT_RT_ENTRY_REF(swift_allocBox)(type);
}

BoxPair::Return swift::swift_makeBoxUnique(OpaqueValue *buffer, const Metadata *type,
BoxPair swift::swift_makeBoxUnique(OpaqueValue *buffer, const Metadata *type,
size_t alignMask) {
auto *inlineBuffer = reinterpret_cast<ValueBuffer*>(buffer);
HeapObject *box = reinterpret_cast<HeapObject *>(inlineBuffer->PrivateData[0]);
Expand All @@ -219,8 +219,8 @@ BoxPair::Return swift::swift_makeBoxUnique(OpaqueValue *buffer, const Metadata *
auto *oldObjectAddr = reinterpret_cast<OpaqueValue *>(
reinterpret_cast<char *>(box) + headerOffset);
// Copy the data.
type->vw_initializeWithCopy(refAndObjectAddr.second, oldObjectAddr);
inlineBuffer->PrivateData[0] = refAndObjectAddr.first;
type->vw_initializeWithCopy(refAndObjectAddr.buffer, oldObjectAddr);
inlineBuffer->PrivateData[0] = refAndObjectAddr.object;
// Release ownership of the old box.
swift_release(box);
return refAndObjectAddr;
Expand All @@ -234,7 +234,7 @@ BoxPair::Return swift::swift_makeBoxUnique(OpaqueValue *buffer, const Metadata *

SWIFT_RT_ENTRY_IMPL_VISIBILITY
extern "C"
BoxPair::Return SWIFT_RT_ENTRY_IMPL(swift_allocBox)(const Metadata *type) {
BoxPair SWIFT_RT_ENTRY_IMPL(swift_allocBox)(const Metadata *type) {
// Get the heap metadata for the box.
auto metadata = &Boxes.getOrInsert(type).first->Data;

Expand Down
8 changes: 4 additions & 4 deletions stdlib/public/runtime/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,8 +636,8 @@ static OpaqueValue *tuple_allocateBuffer(ValueBuffer *buffer,
if (IsInline)
return reinterpret_cast<OpaqueValue*>(buffer);
BoxPair refAndValueAddr(swift_allocBox(metatype));
*reinterpret_cast<HeapObject **>(buffer) = refAndValueAddr.first;
return refAndValueAddr.second;
*reinterpret_cast<HeapObject **>(buffer) = refAndValueAddr.object;
return refAndValueAddr.buffer;
}

/// Generic tuple value witness for 'destroy'.
Expand Down Expand Up @@ -2589,8 +2589,8 @@ template <> OpaqueValue *Metadata::allocateBoxForExistentialIn(ValueBuffer *buff

// Allocate the box.
BoxPair refAndValueAddr(swift_allocBox(this));
buffer->PrivateData[0] = refAndValueAddr.first;
return refAndValueAddr.second;
buffer->PrivateData[0] = refAndValueAddr.object;
return refAndValueAddr.buffer;
}

template <> OpaqueValue *Metadata::allocateBufferIn(ValueBuffer *buffer) const {
Expand Down
16 changes: 8 additions & 8 deletions stdlib/public/runtime/Reflection.mm
Original file line number Diff line number Diff line change
Expand Up @@ -633,21 +633,21 @@ void swift_EnumMirror_subscript(String *outString,
BoxPair pair = swift_allocBox(boxType);

type->vw_destructiveProjectEnumData(const_cast<OpaqueValue *>(value));
boxType->vw_initializeWithCopy(pair.second, const_cast<OpaqueValue *>(value));
boxType->vw_initializeWithCopy(pair.buffer, const_cast<OpaqueValue *>(value));
type->vw_destructiveInjectEnumTag(const_cast<OpaqueValue *>(value),
(int) (tag - Description.getNumPayloadCases()));

swift_release(owner);

owner = pair.first;
value = pair.second;
owner = pair.object;
value = pair.buffer;

// If the payload is indirect, we need to jump through the box to get it.
if (indirect) {
owner = *reinterpret_cast<HeapObject * const *>(value);
value = swift_projectBox(const_cast<HeapObject *>(owner));
swift_retain(owner);
swift_release(pair.first);
swift_release(pair.object);
}

new (outString) String(getFieldName(Description.CaseNames, tag));
Expand Down Expand Up @@ -1035,12 +1035,12 @@ static Mirror ObjC_getMirrorForSuperclass(Class sup,
BoxPair box = swift_allocBox(T);

if (take)
T->vw_initializeWithTake(box.second, value);
T->vw_initializeWithTake(box.buffer, value);
else
T->vw_initializeWithCopy(box.second, value);
std::tie(T, Self, MirrorWitness) = getImplementationForType(T, box.second);
T->vw_initializeWithCopy(box.buffer, value);
std::tie(T, Self, MirrorWitness) = getImplementationForType(T, box.buffer);

Data = {box.first, box.second, T};
Data = {box.object, box.buffer, T};
}

/// MagicMirror ownership-sharing subvalue constructor.
Expand Down
Loading