Skip to content

Commit 335f426

Browse files
committed
Fixes for TypeLayout Procol Compositions
This fixes several issues with the original Scalar Type Layouts patch: - AddressOnly types need to be lowered to ScalarKinds that do not load when releasing. - ClassExistentials now handle the case when the existential is an inline aggregation of the class pointer and witness table pointers
1 parent 13bb43b commit 335f426

File tree

2 files changed

+175
-70
lines changed

2 files changed

+175
-70
lines changed

lib/IRGen/GenExistential.cpp

Lines changed: 70 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
#include "GenExistential.h"
1818

19-
#include "TypeLayout.h"
2019
#include "swift/AST/ASTContext.h"
2120
#include "swift/AST/Decl.h"
2221
#include "swift/AST/ExistentialLayout.h"
@@ -29,7 +28,6 @@
2928
#include "llvm/IR/DerivedTypes.h"
3029
#include "llvm/IR/Function.h"
3130
#include "llvm/IR/Module.h"
32-
#include "llvm/Support/ErrorHandling.h"
3331
#include "llvm/Support/raw_ostream.h"
3432

3533
#include "BitPatternBuilder.h"
@@ -663,60 +661,52 @@ namespace {
663661
return Super::getFixedExtraInhabitantMask(IGM); \
664662
} \
665663
}
666-
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \
667-
class AddressOnly##Name##ClassExistentialTypeInfo final : \
668-
public AddressOnlyClassExistentialTypeInfoBase< \
669-
AddressOnly##Name##ClassExistentialTypeInfo, \
670-
FixedTypeInfo> { \
671-
bool IsOptional; \
672-
public: \
673-
AddressOnly##Name##ClassExistentialTypeInfo( \
674-
ArrayRef<const ProtocolDecl *> protocols, \
675-
llvm::Type *ty, \
676-
SpareBitVector &&spareBits, \
677-
Size size, Alignment align, \
678-
ReferenceCounting refcounting, \
679-
bool isOptional) \
680-
: AddressOnlyClassExistentialTypeInfoBase(protocols, refcounting, \
681-
ty, size, std::move(spareBits), \
682-
align, IsNotPOD, \
683-
IsNotBitwiseTakable, \
684-
IsFixedSize), \
685-
IsOptional(isOptional) {} \
686-
TypeLayoutEntry *buildTypeLayoutEntry(IRGenModule &IGM, \
687-
SILType T) const override { \
688-
ScalarKind kind; \
689-
switch (Refcounting) { \
690-
case ReferenceCounting::Native: kind = ScalarKind::NativeStrongReference; break; \
691-
case ReferenceCounting::ObjC: kind = ScalarKind::ObjCReference; break; \
692-
case ReferenceCounting::Block: kind = ScalarKind::BlockReference; break; \
693-
case ReferenceCounting::Unknown: kind = ScalarKind::UnknownReference; break; \
694-
case ReferenceCounting::Bridge: kind = ScalarKind::BridgeReference; break; \
695-
case ReferenceCounting::Error: kind = ScalarKind::ErrorReference; break; \
696-
} \
697-
return IGM.typeLayoutCache.getOrCreateScalarEntry(*this, T, kind); \
698-
} \
699-
void emitValueAssignWithCopy(IRGenFunction &IGF, \
700-
Address dest, Address src) const { \
701-
IGF.emit##Name##CopyAssign(dest, src, Refcounting); \
702-
} \
703-
void emitValueInitializeWithCopy(IRGenFunction &IGF, \
704-
Address dest, Address src) const { \
705-
IGF.emit##Name##CopyInit(dest, src, Refcounting); \
706-
} \
707-
void emitValueAssignWithTake(IRGenFunction &IGF, \
708-
Address dest, Address src) const { \
709-
IGF.emit##Name##TakeAssign(dest, src, Refcounting); \
710-
} \
711-
void emitValueInitializeWithTake(IRGenFunction &IGF, \
712-
Address dest, Address src) const { \
713-
IGF.emit##Name##TakeInit(dest, src, Refcounting); \
714-
} \
715-
void emitValueDestroy(IRGenFunction &IGF, Address addr) const { \
716-
IGF.emit##Name##Destroy(addr, Refcounting); \
717-
} \
718-
StringRef getStructNameSuffix() const { return "." #name "ref"; } \
719-
REF_STORAGE_HELPER(Name, FixedTypeInfo) \
664+
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, name, ...) \
665+
class AddressOnly##Name##ClassExistentialTypeInfo final \
666+
: public AddressOnlyClassExistentialTypeInfoBase< \
667+
AddressOnly##Name##ClassExistentialTypeInfo, FixedTypeInfo> { \
668+
bool IsOptional; \
669+
\
670+
public: \
671+
AddressOnly##Name##ClassExistentialTypeInfo( \
672+
ArrayRef<const ProtocolDecl *> protocols, llvm::Type *ty, \
673+
SpareBitVector &&spareBits, Size size, Alignment align, \
674+
ReferenceCounting refcounting, bool isOptional) \
675+
: AddressOnlyClassExistentialTypeInfoBase( \
676+
protocols, refcounting, ty, size, std::move(spareBits), align, \
677+
IsNotPOD, IsNotBitwiseTakable, IsFixedSize), \
678+
IsOptional(isOptional) {} \
679+
TypeLayoutEntry *buildTypeLayoutEntry(IRGenModule &IGM, \
680+
SILType T) const override { \
681+
if (Refcounting == ReferenceCounting::Native) { \
682+
return IGM.typeLayoutCache.getOrCreateScalarEntry( \
683+
*this, T, ScalarKind::Native##Name##Reference); \
684+
} else { \
685+
return IGM.typeLayoutCache.getOrCreateScalarEntry( \
686+
*this, T, ScalarKind::Unknown##Name##Reference); \
687+
} \
688+
} \
689+
void emitValueAssignWithCopy(IRGenFunction &IGF, Address dest, \
690+
Address src) const { \
691+
IGF.emit##Name##CopyAssign(dest, src, Refcounting); \
692+
} \
693+
void emitValueInitializeWithCopy(IRGenFunction &IGF, Address dest, \
694+
Address src) const { \
695+
IGF.emit##Name##CopyInit(dest, src, Refcounting); \
696+
} \
697+
void emitValueAssignWithTake(IRGenFunction &IGF, Address dest, \
698+
Address src) const { \
699+
IGF.emit##Name##TakeAssign(dest, src, Refcounting); \
700+
} \
701+
void emitValueInitializeWithTake(IRGenFunction &IGF, Address dest, \
702+
Address src) const { \
703+
IGF.emit##Name##TakeInit(dest, src, Refcounting); \
704+
} \
705+
void emitValueDestroy(IRGenFunction &IGF, Address addr) const { \
706+
IGF.emit##Name##Destroy(addr, Refcounting); \
707+
} \
708+
StringRef getStructNameSuffix() const { return "." #name "ref"; } \
709+
REF_STORAGE_HELPER(Name, FixedTypeInfo) \
720710
};
721711
#define ALWAYS_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
722712
class Loadable##Name##ClassExistentialTypeInfo final \
@@ -1016,21 +1006,31 @@ class ClassExistentialTypeInfo final
10161006

10171007
TypeLayoutEntry *buildTypeLayoutEntry(IRGenModule &IGM,
10181008
SILType T) const override {
1019-
ScalarKind kind;
1020-
switch (Refcounting) {
1021-
case ReferenceCounting::Native:
1022-
kind = ScalarKind::NativeStrongReference;
1023-
break;
1024-
case ReferenceCounting::ObjC:
1025-
kind = ScalarKind::ObjCReference;
1026-
break;
1027-
case ReferenceCounting::Unknown:
1028-
kind = ScalarKind::UnknownReference;
1029-
break;
1030-
default:
1031-
llvm_unreachable("Unsupported refcounting style");
1009+
// We can't create an objc typeinfo by itself, so don't destructure if we
1010+
// have one
1011+
if (Refcounting == ReferenceCounting::ObjC) {
1012+
return IGM.typeLayoutCache.getOrCreateTypeInfoBasedEntry(*this, T);
10321013
}
1033-
return IGM.typeLayoutCache.getOrCreateScalarEntry(*this, T, kind);
1014+
1015+
/// The storage type of a class existential is a struct containing
1016+
/// a refcounted pointer to the class instance value followed by
1017+
/// witness table pointers for each conformed-to protocol.
1018+
std::vector<TypeLayoutEntry *> alignedGroup;
1019+
const TypeInfo &typeinfo = Refcounting == ReferenceCounting::Native
1020+
? IGM.getNativeObjectTypeInfo()
1021+
: IGM.getUnknownObjectTypeInfo();
1022+
1023+
alignedGroup.push_back(IGM.typeLayoutCache.getOrCreateScalarEntry(
1024+
typeinfo, T, refcountingToScalarKind(Refcounting)));
1025+
for (unsigned i = 0; i < getNumStoredProtocols(); i++) {
1026+
alignedGroup.push_back(IGM.typeLayoutCache.getOrCreateScalarEntry(
1027+
IGM.getWitnessTablePtrTypeInfo(),
1028+
SILType::getBuiltinIntegerType(IGM.getPointerSize().getValue(),
1029+
IGM.Context),
1030+
ScalarKind::POD));
1031+
}
1032+
1033+
return IGM.typeLayoutCache.getOrCreateAlignedGroupEntry(alignedGroup, 0);
10341034
}
10351035

10361036
/// Given an explosion with multiple pointer elements in them, pack them
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// REQUIRES: objc_interop
2+
// REQUIRES: executable_test
3+
4+
// RUN: %empty-directory(%t)
5+
// RUN: %target-build-swift -Onone -Xfrontend -enable-type-layout %s -o %t/main_type_layouts
6+
// RUN: %target-build-swift -Onone -Xfrontend -disable-type-layout %s -o %t/main_no_type_layouts
7+
// RUN: %target-codesign %t/main_type_layouts
8+
// RUN: %target-codesign %t/main_no_type_layouts
9+
// RUN: %target-run %t/main_type_layouts
10+
// RUN: %target-run %t/main_no_type_layouts
11+
12+
import Swift
13+
import Foundation
14+
import StdlibUnittest
15+
16+
public class SomeObject {
17+
init() {
18+
a = LifetimeTracked(0)
19+
}
20+
let a: LifetimeTracked
21+
}
22+
public class SomeNSObject : NSObject {
23+
override init() {
24+
a = LifetimeTracked(0)
25+
}
26+
let a: LifetimeTracked
27+
}
28+
29+
public protocol ProtoA{}
30+
31+
public protocol SomeProtoType { }
32+
public typealias ObjcComposition = SomeNSObject & SomeProtoType
33+
public typealias NativeComposition = SomeObject & SomeProtoType
34+
35+
public struct Thing1<T: ProtoA> {
36+
let a: ObjcComposition
37+
let b: T
38+
39+
init(
40+
a: ObjcComposition,
41+
b: T
42+
) {
43+
self.a = a
44+
self.b = b
45+
}
46+
}
47+
48+
public struct Thing2<T: ProtoA> {
49+
let a: NativeComposition
50+
let b: T
51+
52+
init(a: NativeComposition,
53+
b: T
54+
) {
55+
self.a = a
56+
self.b = b
57+
}
58+
}
59+
60+
61+
public class Comp1 : SomeNSObject, SomeProtoType {
62+
override init() {
63+
b = LifetimeTracked(0)
64+
}
65+
let b: LifetimeTracked
66+
67+
}
68+
public class Comp2 : SomeObject, SomeProtoType {
69+
override init() {
70+
b = LifetimeTracked(0)
71+
}
72+
let b: LifetimeTracked
73+
}
74+
public struct Proto1 : ProtoA {
75+
init() {
76+
a = LifetimeTracked(0)
77+
}
78+
let a: LifetimeTracked
79+
}
80+
81+
let ProtocolComp = TestSuite("ProtocolComposition")
82+
83+
func forceVWUsage<T>(_ value1: T, _ value2: T) -> T {
84+
var a = value1
85+
var b = value2
86+
var c = a
87+
a = b
88+
b = c
89+
c = a
90+
return c;
91+
}
92+
ProtocolComp.test("Objc Class Compositions should destroy properly") {
93+
var thing1 = Thing1<Proto1>(a: Comp1(), b: Proto1())
94+
expectTrue((thing1.a as! Comp1).b.value == 0)
95+
thing1 = forceVWUsage(thing1, thing1)
96+
expectTrue((thing1.a as! Comp1).b.value == 0)
97+
}
98+
99+
ProtocolComp.test("NativeClassCompositions") {
100+
var thing2 = Thing2<Proto1>(a: Comp2(), b: Proto1())
101+
expectTrue((thing2.a as! Comp2).b.value == 0)
102+
thing2 = forceVWUsage(thing2, thing2)
103+
}
104+
105+
runAllTests()

0 commit comments

Comments
 (0)