IRGen: EmptyBoxType's representation cannot be nil because of a conflict with extra inhabitant assumption in indirect enums (#10326)

* IRGen: EmptyBoxType's representation cannot be nil because of a conflict with extra inhabitant assumption in indirect enums

We map nil to the .None case of Optional. Instead use a singleton object.

SR-5148
rdar://32618580
This commit is contained in:
Arnold Schwaighofer
2017-06-17 09:33:41 -07:00
committed by GitHub
parent 6f3b8ca60f
commit 48e889b51b
13 changed files with 113 additions and 5 deletions

View File

@@ -71,6 +71,7 @@ Rename with a non-`stdlib` naming scheme.
``` ```
000000000001cb30 T _swift_allocBox 000000000001cb30 T _swift_allocBox
000000000001cb30 T _swift_allocEmptyBox
000000000001c990 T _swift_allocObject 000000000001c990 T _swift_allocObject
000000000001ca60 T _swift_bufferAllocate 000000000001ca60 T _swift_bufferAllocate
000000000001ca90 T _swift_bufferHeaderSize 000000000001ca90 T _swift_bufferHeaderSize

View File

@@ -169,6 +169,10 @@ SWIFT_RUNTIME_EXPORT
BoxPair::Return swift_makeBoxUnique(OpaqueValue *buffer, Metadata const *type, BoxPair::Return swift_makeBoxUnique(OpaqueValue *buffer, Metadata const *type,
size_t alignMask); size_t alignMask);
/// Returns the address of a heap object representing all empty box types.
SWIFT_RUNTIME_EXPORT
HeapObject* swift_allocEmptyBox();
// Allocate plain old memory. This is the generalized entry point // Allocate plain old memory. This is the generalized entry point
// Never returns nil. The returned memory is uninitialized. // Never returns nil. The returned memory is uninitialized.
// //

View File

@@ -1761,6 +1761,9 @@ struct TargetHeapLocalVariableMetadata
static bool classof(const TargetMetadata<Runtime> *metadata) { static bool classof(const TargetMetadata<Runtime> *metadata) {
return metadata->getKind() == MetadataKind::HeapLocalVariable; return metadata->getKind() == MetadataKind::HeapLocalVariable;
} }
constexpr TargetHeapLocalVariableMetadata()
: TargetHeapMetadata<Runtime>(MetadataKind::HeapLocalVariable),
OffsetToFirstCapture(0), CaptureDescription(nullptr) {}
}; };
using HeapLocalVariableMetadata using HeapLocalVariableMetadata
= TargetHeapLocalVariableMetadata<InProcess>; = TargetHeapLocalVariableMetadata<InProcess>;

View File

@@ -73,6 +73,11 @@ FUNCTION(ProjectBox, swift_projectBox, DefaultCC,
ARGS(RefCountedPtrTy), ARGS(RefCountedPtrTy),
ATTRS(NoUnwind, ReadNone)) ATTRS(NoUnwind, ReadNone))
FUNCTION(AllocEmptyBox, swift_allocEmptyBox, DefaultCC,
RETURNS(RefCountedPtrTy),
ARGS(),
ATTRS(NoUnwind))
// RefCounted *swift_allocObject(Metadata *type, size_t size, size_t alignMask); // RefCounted *swift_allocObject(Metadata *type, size_t size, size_t alignMask);
FUNCTION_WITH_GLOBAL_SYMBOL_AND_IMPL(AllocObject, swift_allocObject, FUNCTION_WITH_GLOBAL_SYMBOL_AND_IMPL(AllocObject, swift_allocObject,
_swift_allocObject, _swift_allocObject_, RegisterPreservingCC, _swift_allocObject, _swift_allocObject_, RegisterPreservingCC,

View File

@@ -1430,7 +1430,7 @@ public:
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env, allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
const llvm::Twine &name) const override { const llvm::Twine &name) const override {
return OwnedAddress(IGF.getTypeInfo(boxedType).getUndefAddress(), return OwnedAddress(IGF.getTypeInfo(boxedType).getUndefAddress(),
IGF.IGM.RefCountedNull); IGF.emitAllocEmptyBoxCall());
} }
void void
@@ -1584,7 +1584,11 @@ const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) {
// For fixed-sized types, we can emit concrete box metadata. // For fixed-sized types, we can emit concrete box metadata.
auto &fixedTI = cast<FixedTypeInfo>(eltTI); auto &fixedTI = cast<FixedTypeInfo>(eltTI);
// For empty types, we don't really need to allocate anything. // Because we assume in enum's that payloads with a Builtin.NativeObject which
// is also the type for indirect enum cases have extra inhabitants of pointers
// we can't have a nil pointer as a representation for an empty box type --
// nil conflicts with the extra inhabitants. We return a static singleton
// empty box object instead.
if (fixedTI.isKnownEmpty(ResilienceExpansion::Maximal)) { if (fixedTI.isKnownEmpty(ResilienceExpansion::Maximal)) {
if (!EmptyBoxTI) if (!EmptyBoxTI)
EmptyBoxTI = new EmptyBoxTypeInfo(IGM); EmptyBoxTI = new EmptyBoxTypeInfo(IGM);

View File

@@ -209,6 +209,20 @@ llvm::Value *IRGenFunction::emitProjectBoxCall(llvm::Value *box,
return call; return call;
} }
llvm::Value *IRGenFunction::emitAllocEmptyBoxCall() {
llvm::Attribute::AttrKind attrKinds[] = {
llvm::Attribute::NoUnwind,
};
auto attrs = llvm::AttributeSet::get(IGM.LLVMContext,
llvm::AttributeSet::FunctionIndex,
attrKinds);
llvm::CallInst *call =
Builder.CreateCall(IGM.getAllocEmptyBoxFn(), {});
call->setCallingConv(IGM.DefaultCC);
call->setAttributes(attrs);
return call;
}
static void emitDeallocatingCall(IRGenFunction &IGF, llvm::Constant *fn, static void emitDeallocatingCall(IRGenFunction &IGF, llvm::Constant *fn,
std::initializer_list<llvm::Value *> args) { std::initializer_list<llvm::Value *> args) {
auto cc = IGF.IGM.DefaultCC; auto cc = IGF.IGM.DefaultCC;

View File

@@ -188,6 +188,8 @@ public:
llvm::Value *emitProjectBoxCall(llvm::Value *box, llvm::Value *typeMetadata); llvm::Value *emitProjectBoxCall(llvm::Value *box, llvm::Value *typeMetadata);
llvm::Value *emitAllocEmptyBoxCall();
// Emit a reference to the canonical type metadata record for the given AST // Emit a reference to the canonical type metadata record for the given AST
// type. This can be used to identify the type at runtime. For types with // type. This can be used to identify the type at runtime. For types with
// abstraction difference, the metadata contains the layout information for // abstraction difference, the metadata contains the layout information for

View File

@@ -88,6 +88,13 @@ struct _SwiftHashingSecretKey _swift_stdlib_Hashing_secretKey;
SWIFT_RUNTIME_STDLIB_INTERFACE SWIFT_RUNTIME_STDLIB_INTERFACE
__swift_uint64_t _swift_stdlib_HashingDetail_fixedSeedOverride; __swift_uint64_t _swift_stdlib_HashingDetail_fixedSeedOverride;
struct _SwiftEmptyBoxStorage {
struct HeapObject header;
};
SWIFT_RUNTIME_STDLIB_INTERFACE
struct _SwiftEmptyBoxStorage _EmptyBoxStorage;
#ifdef __cplusplus #ifdef __cplusplus
static_assert(std::is_pod<_SwiftEmptyArrayStorage>::value, static_assert(std::is_pod<_SwiftEmptyArrayStorage>::value,

View File

@@ -31,6 +31,7 @@
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <thread> #include <thread>
#include "../SwiftShims/GlobalObjects.h"
#include "../SwiftShims/RuntimeShims.h" #include "../SwiftShims/RuntimeShims.h"
#if SWIFT_OBJC_INTEROP #if SWIFT_OBJC_INTEROP
# include <objc/NSObject.h> # include <objc/NSObject.h>
@@ -227,6 +228,12 @@ OpaqueValue *swift::swift_projectBox(HeapObject *o) {
return metadata->project(o); return metadata->project(o);
} }
HeapObject *swift::swift_allocEmptyBox() {
auto heapObject = reinterpret_cast<HeapObject*>(&_EmptyBoxStorage);
SWIFT_RT_ENTRY_CALL(swift_retain)(heapObject);
return heapObject;
}
// Forward-declare this, but define it after swift_release. // Forward-declare this, but define it after swift_release.
extern "C" LLVM_LIBRARY_VISIBILITY LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED extern "C" LLVM_LIBRARY_VISIBILITY LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED
void _swift_release_dealloc(HeapObject *object) SWIFT_CC(RegisterPreservingCC_IMPL); void _swift_release_dealloc(HeapObject *object) SWIFT_CC(RegisterPreservingCC_IMPL);

View File

@@ -35,6 +35,10 @@ ClassMetadata CLASS_METADATA_SYM(s27_RawNativeDictionaryStorage);
// _direct type metadata for Swift._RawNativeSetStorage // _direct type metadata for Swift._RawNativeSetStorage
SWIFT_RUNTIME_STDLIB_INTERFACE SWIFT_RUNTIME_STDLIB_INTERFACE
ClassMetadata CLASS_METADATA_SYM(s20_RawNativeSetStorage); ClassMetadata CLASS_METADATA_SYM(s20_RawNativeSetStorage);
// _direct type metadata for Swift._EmptyBoxStorage
SWIFT_RUNTIME_STDLIB_INTERFACE
ClassMetadata CLASS_METADATA_SYM(s16_EmptyBoxStorage);
} // namespace swift } // namespace swift
swift::_SwiftEmptyArrayStorage swift::_swiftEmptyArrayStorage = { swift::_SwiftEmptyArrayStorage swift::_swiftEmptyArrayStorage = {
@@ -127,6 +131,18 @@ void swift::_swift_instantiateInertHeapObject(void *address,
::new (address) HeapObject{metadata}; ::new (address) HeapObject{metadata};
} }
swift::HeapLocalVariableMetadata _emptyBoxStorageMetadata;
/// The signleton empty box storage object.
swift::_SwiftEmptyBoxStorage swift::_EmptyBoxStorage = {
// HeapObject header;
{
&_emptyBoxStorageMetadata,
//&swift::CLASS_METADATA_SYM(s16_EmptyBoxStorage), // isa pointer
}
};
namespace llvm { namespace hashing { namespace detail { namespace llvm { namespace hashing { namespace detail {
// An extern variable expected by LLVM's hashing templates. We don't link any // An extern variable expected by LLVM's hashing templates. We don't link any
// LLVM libs into the runtime, so define this here. // LLVM libs into the runtime, so define this here.

View File

@@ -114,7 +114,6 @@ sil @testPairedBox : $(@guaranteed { var () }) -> () {
bb0(%0 : ${ var () }): bb0(%0 : ${ var () }):
// CHECK: entry: // CHECK: entry:
%2 = project_box %0 : ${ var () }, 0 %2 = project_box %0 : ${ var () }, 0
// CHECK-NEXT: call {{.*}}void @writeEmptyTuple(%swift.opaque* nocapture undef) // CHECK-NEXT: call {{.*}}void @writeEmptyTuple(%swift.opaque* nocapture undef)
%3 = begin_access [modify] [dynamic] %2 : $*() %3 = begin_access [modify] [dynamic] %2 : $*()
%write_fn = function_ref @writeEmptyTuple : $@convention(thin) (@inout ()) -> () %write_fn = function_ref @writeEmptyTuple : $@convention(thin) (@inout ()) -> ()

View File

@@ -365,7 +365,8 @@ sil public_external @partial_empty_box : $@convention(thin) (@owned <τ_0_0> { v
// CHECK-LABEL: define{{( protected)?}} swiftcc void @empty_box() // CHECK-LABEL: define{{( protected)?}} swiftcc void @empty_box()
sil @empty_box : $@convention(thin) () -> () { sil @empty_box : $@convention(thin) () -> () {
entry: entry:
// CHECK: store %swift.refcounted* null // CHECK: [[BOX:%.*]] = call {{.*}}swift_allocEmptyBox
// CHECK: store %swift.refcounted* [[BOX]]
// CHECK: store %swift.opaque* undef // CHECK: store %swift.opaque* undef
%b = alloc_box $<τ_0_0> { var τ_0_0 } <()> %b = alloc_box $<τ_0_0> { var τ_0_0 } <()>
%ba = project_box %b : $<τ_0_0> { var τ_0_0 } <()>, 0 %ba = project_box %b : $<τ_0_0> { var τ_0_0 } <()>, 0

View File

@@ -553,5 +553,50 @@ presentEitherOr(EitherOr<(), String>.Right("foo")) // CHECK-NEXT: Right(foo)
// CHECK-NEXT: Right(foo) // CHECK-NEXT: Right(foo)
presentEitherOrsOf(t: (), u: "foo") presentEitherOrsOf(t: (), u: "foo")
// SR-5148
enum Payload {
case email
}
enum Test {
case a
indirect case b(Payload)
}
@inline(never)
func printA() {
print("an a")
}
@inline(never)
func printB() {
print("an b")
}
@inline(never)
func testCase(_ testEmail: Test) {
switch testEmail {
case .a:
printA()
case .b:
printB()
}
}
@inline(never)
func createTestB() -> Test {
return Test.b(.email)
}
@inline(never)
func createTestA() -> Test {
return Test.a
}
// CHECK-NEXT: an b
testCase(createTestB())
// CHECK-NEXT: b(a.Payload.email)
print(createTestB())
// CHECK-NEXT: a
print(createTestA())
// CHECK-NEXT: done // CHECK-NEXT: done
print("done") print("done")