Swift 4.2: FileCheck error: '_' is empty when running testcases on s390x

I have been trying to build 4.2 on s390x and running into multiple testcase failures with following error:

******************** TEST 'Swift(linux-s390x) :: Generics/conditional_conformances_execute_smoke.swift' FAILED ********************
rm -rf /home/swift/swift4.2/build/Ninja-RelWithDebInfoAssert/swift-linux-s390x/test-linux-s390x/Generics/Output/conditional_conformances_execute_smoke.swift.tmp && mkdir -p /home/swift/swift4.2/build/Ninja-RelWithDebInfoAssert/swift-linux-s390x/test-linux-s390x/Generics/Output/conditional_conformances_execute_smoke.swift.tmp && /home/swift/swift4.2/build/Ninja-RelWithDebInfoAssert/swift-linux-s390x/bin/swiftc -target s390x-unknown-linux-gnu  -module-cache-path '/home/swift/swift4.2/build/Ninja-RelWithDebInfoAssert/swift-linux-s390x/./swift-test-results/s390x-unknown-linux-gnu/clang-module-cache' -swift-version 3   -module-cache-path '/home/swift/swift4.2/build/Ninja-RelWithDebInfoAssert/swift-linux-s390x/./swift-test-results/s390x-unknown-linux-gnu/clang-module-cache' /home/swift/swift4.2/swift/test/Generics/conditional_conformances_execute_smoke.swift -o /home/swift/swift4.2/build/Ninja-RelWithDebInfoAssert/swift-linux-s390x/test-linux-s390x/Generics/Output/conditional_conformances_execute_smoke.swift.tmp/a.out -module-name main &&  /home/swift/swift4.2/build/Ninja-RelWithDebInfoAssert/swift-linux-s390x/test-linux-s390x/Generics/Output/conditional_conformances_execute_smoke.swift.tmp/a.out | /home/swift/swift4.2/swift/utils/PathSanitizingFileCheck --sanitize 'BUILD_DIR=/home/swift/swift4.2/build/Ninja-RelWithDebInfoAssert/swift-linux-s390x' --sanitize 'SOURCE_DIR=/home/swift/swift4.2/swift' --use-filecheck '/home/swift/swift4.2/build/Ninja-RelWithDebInfoAssert/llvm-linux-s390x/bin/FileCheck' /home/swift/swift4.2/swift/test/Generics/conditional_conformances_execute_smoke.swift
Exit Code: 2

Command Output (stderr):
Fatal error: file /home/swift/swift4.2/swift/stdlib/public/core/Builtin.swift, line 466
Current stack trace:
FileCheck error: '-' is empty.
FileCheck command line:  /home/swift/swift4.2/build/Ninja-RelWithDebInfoAssert/llvm-linux-s390x/bin/FileCheck /home/swift/swift4.2/swift/test/Generics/conditional_conformances_execute_smoke.swift


Testing Time: 0.42s
Failing Tests (1):
    Swift(linux-s390x) :: Generics/conditional_conformances_execute_smoke.swift

Some of the forum entries point to potential swift-clang issues but I cannot be sure. Is there a good way of debugging such problems?

I have already looked at FileCheck and Generics/conditional_conformances_execute_smoke.swift code and nothing looks out of ordinary.

These testcases run just fine in 4.1.2 - One difference I see is the addition of this method in Builtin.swift:

public func _bridgeObject(fromTagged x: UInt) -> Builtin.BridgeObject {
  _sanityCheck(x & _objCTaggedPointerBits != 0)
  let object: Builtin.BridgeObject = Builtin.valueToBridgeObject(x)
  return object

Thanks in advance for any tip.

That looks like there was a crash and therefore nothing got written to stdout. The failure in Builtin.swift at line 466 is whatever's actually going on.

I think there's definitely a possibility that the implementation of the various BridgeObject helpers (in the runtime, not in the stdlib) needs to take big- vs. little-endian into account in some way.

This may be due to the fact that String uses tagged BridgeObject representations for literals and small strings, while the Swift runtime conditionally compiles the code out on non-ObjC platforms:

/* runtime/Private.h */

/// Is the given value an Objective-C tagged pointer?
static inline bool isObjCTaggedPointer(const void *object) {
  return (((uintptr_t) object) & heap_object_abi::ObjCReservedBitsMask);
  assert(!(((uintptr_t) object) & heap_object_abi::ObjCReservedBitsMask));
  return false;

/* runtime/SwiftObject.mm */
void *swift::swift_bridgeObjectRetain(void *object) {
  if (isObjCTaggedPointer(object))
    return object;
  ... perform normal retain, but only if isValidPointerForNativeRetain return true ...

... as well as release and unknown variants ...

/* runtime/HeapObject.cpp */

/// Returns true if the pointer passed to a native retain or release is valid.
/// If false, the operation should immediately return.
static inline bool isValidPointerForNativeRetain(const void *p) {
#if defined(__x86_64__) || defined(__arm64__)
  // On these platforms, the upper half of address space is reserved for the
  // kernel, so we can assume that pointer values in this range are invalid.
  return (intptr_t)p > 0;
  return p != nullptr;

This means that on x86_64 Linux, small strings and string literals end up nop-ing inside retain, which is why this doesn't surface there. i386 Linux doesn't use BridgeObject for these purposes.

@Joe_Groff, do you think we should just drop the #if SWIFT_OBJC_INTEROP, or enhance isValidPointerForNativeRetain for s390x support? Can you think of other solutions?

Even with the SWIFT_OBJC_INTEROP check taken out, there's the problem of isValidPointerForNativeRetain. We reserve 64-bit pointers with the high bit set on x86-64 and arm64, but not formally on s390x. Someone who manages that platform would have to decide that that's an acceptable constraint on s390x as well, or we'd need to choose a different representation of String that doesn't make that assumption on s390x.

@Joe_Groff Theoretically an application may choose to mmap memory addresses with the MSB set on Z. However in practice I’m not aware of any applications that do this, nor any reason to do this, so for the time being it is probably safe to assume that addresses with the MSB set can be reserved.

What changes are required to support this assumption?


That should be fine, as long as Swift's allocator doesn't put Swift-reference-countable things in that memory range. It should be sufficient to change the #if condition in isValidPointerForNativeRetain to include __s390x__; I don't believe we exploit this in the compiler anywhere yet, only in the runtime implementation of BridgeObject.