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_allocEmptyBox
000000000001c990 T _swift_allocObject
000000000001ca60 T _swift_bufferAllocate
000000000001ca90 T _swift_bufferHeaderSize

View File

@@ -169,6 +169,10 @@ SWIFT_RUNTIME_EXPORT
BoxPair::Return swift_makeBoxUnique(OpaqueValue *buffer, Metadata const *type,
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
// Never returns nil. The returned memory is uninitialized.
//

View File

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

View File

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

View File

@@ -1430,7 +1430,7 @@ public:
allocate(IRGenFunction &IGF, SILType boxedType, GenericEnvironment *env,
const llvm::Twine &name) const override {
return OwnedAddress(IGF.getTypeInfo(boxedType).getUndefAddress(),
IGF.IGM.RefCountedNull);
IGF.emitAllocEmptyBoxCall());
}
void
@@ -1584,7 +1584,11 @@ const TypeInfo *TypeConverter::convertBoxType(SILBoxType *T) {
// For fixed-sized types, we can emit concrete box metadata.
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 (!EmptyBoxTI)
EmptyBoxTI = new EmptyBoxTypeInfo(IGM);

View File

@@ -209,6 +209,20 @@ llvm::Value *IRGenFunction::emitProjectBoxCall(llvm::Value *box,
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,
std::initializer_list<llvm::Value *> args) {
auto cc = IGF.IGM.DefaultCC;

View File

@@ -188,6 +188,8 @@ public:
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
// type. This can be used to identify the type at runtime. For types with
// 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_uint64_t _swift_stdlib_HashingDetail_fixedSeedOverride;
struct _SwiftEmptyBoxStorage {
struct HeapObject header;
};
SWIFT_RUNTIME_STDLIB_INTERFACE
struct _SwiftEmptyBoxStorage _EmptyBoxStorage;
#ifdef __cplusplus
static_assert(std::is_pod<_SwiftEmptyArrayStorage>::value,

View File

@@ -31,6 +31,7 @@
#include <cstdio>
#include <cstdlib>
#include <thread>
#include "../SwiftShims/GlobalObjects.h"
#include "../SwiftShims/RuntimeShims.h"
#if SWIFT_OBJC_INTEROP
# include <objc/NSObject.h>
@@ -227,6 +228,12 @@ OpaqueValue *swift::swift_projectBox(HeapObject *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.
extern "C" LLVM_LIBRARY_VISIBILITY LLVM_ATTRIBUTE_NOINLINE LLVM_ATTRIBUTE_USED
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
SWIFT_RUNTIME_STDLIB_INTERFACE
ClassMetadata CLASS_METADATA_SYM(s20_RawNativeSetStorage);
// _direct type metadata for Swift._EmptyBoxStorage
SWIFT_RUNTIME_STDLIB_INTERFACE
ClassMetadata CLASS_METADATA_SYM(s16_EmptyBoxStorage);
} // namespace swift
swift::_SwiftEmptyArrayStorage swift::_swiftEmptyArrayStorage = {
@@ -127,6 +131,18 @@ void swift::_swift_instantiateInertHeapObject(void *address,
::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 {
// An extern variable expected by LLVM's hashing templates. We don't link any
// LLVM libs into the runtime, so define this here.

View File

@@ -114,8 +114,7 @@ sil @testPairedBox : $(@guaranteed { var () }) -> () {
bb0(%0 : ${ var () }):
// CHECK: entry:
%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 : $*()
%write_fn = function_ref @writeEmptyTuple : $@convention(thin) (@inout ()) -> ()
apply %write_fn(%3) : $@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()
sil @empty_box : $@convention(thin) () -> () {
entry:
// CHECK: store %swift.refcounted* null
// CHECK: [[BOX:%.*]] = call {{.*}}swift_allocEmptyBox
// CHECK: store %swift.refcounted* [[BOX]]
// CHECK: store %swift.opaque* undef
%b = alloc_box $<τ_0_0> { var τ_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)
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
print("done")