Skip to content

Commit 62ac48a

Browse files
committed
Complete support for outline existential storage
... or so I believe
1 parent eda5ead commit 62ac48a

File tree

7 files changed

+160
-17
lines changed

7 files changed

+160
-17
lines changed

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,14 @@ FUNCTION(NativeStrongReleaseDirect, Swift, swift_releaseDirect, SwiftDirectRR_CC
236236
EFFECT(RuntimeEffect::RefCounting, RuntimeEffect::Deallocating),
237237
UNKNOWN_MEMEFFECTS)
238238

239+
// void swift_releaseBox(void *ptr);
240+
FUNCTION(ReleaseBox, Swift, swift_releaseBox, C_CC, AlwaysAvailable,
241+
RETURNS(VoidTy),
242+
ARGS(RefCountedPtrTy),
243+
ATTRS(NoUnwind),
244+
EFFECT(RuntimeEffect::RefCounting, RuntimeEffect::Deallocating),
245+
UNKNOWN_MEMEFFECTS)
246+
239247
// void *swift_retain_n(void *ptr, int32_t n);
240248
FUNCTION(NativeStrongRetainN, Swift, swift_retain_n, C_CC, AlwaysAvailable,
241249
RETURNS(RefCountedPtrTy),

lib/IRGen/GenExistential.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2314,7 +2314,21 @@ Address irgen::emitAllocateBoxedOpaqueExistentialBuffer(
23142314
if (fixedTI->getFixedPacking(IGF.IGM) == FixedPacking::OffsetZero) {
23152315
return valueTI.getAddressForPointer(IGF.Builder.CreateBitCast(
23162316
existentialBuffer.getAddress(), IGF.IGM.PtrTy));
2317+
} else if (IGF.IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
2318+
llvm::Value *box, *address;
2319+
auto *metadata = existLayout.loadMetadataRef(IGF, existentialContainer);
2320+
IGF.emitAllocBoxCall(metadata, box, address);
2321+
llvm::Value *addressInBox =
2322+
IGF.Builder.CreateBitCast(address, IGF.IGM.OpaquePtrTy);
2323+
IGF.Builder.CreateStore(
2324+
box, Address(IGF.Builder.CreateBitCast(
2325+
existentialBuffer.getAddress(), IGF.IGM.PtrTy),
2326+
IGF.IGM.RefCountedPtrTy,
2327+
existLayout.getAlignment(IGF.IGM)));
2328+
2329+
return valueTI.getAddressForPointer(addressInBox);
23172330
}
2331+
23182332
// Otherwise, allocate a box with enough storage.
23192333
Address addr = emitAllocateExistentialBoxInBuffer(
23202334
IGF, valueType, existentialBuffer, genericEnv, "exist.box.addr",
@@ -2883,7 +2897,11 @@ static llvm::Function *getDestroyBoxedOpaqueExistentialBufferFunction(
28832897
Builder.CreateBitCast(buffer.getAddress(), IGM.PtrTy);
28842898
auto *reference = Builder.CreateLoad(Address(
28852899
referenceAddr, IGM.RefCountedPtrTy, buffer.getAlignment()));
2886-
IGF.emitNativeStrongRelease(reference, IGF.getDefaultAtomicity());
2900+
if (IGF.IGM.Context.LangOpts
2901+
.hasFeature(Feature::EmbeddedExistentials)) {
2902+
IGF.emitReleaseBox(reference);
2903+
} else
2904+
IGF.emitNativeStrongRelease(reference, IGF.getDefaultAtomicity());
28872905

28882906
Builder.CreateRetVoid();
28892907
}

lib/IRGen/GenHeap.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,12 @@ void IRGenFunction::emitNativeStrongRelease(llvm::Value *value,
12741274
emitUnaryRefCountCall(*this, function, value);
12751275
}
12761276

1277+
void IRGenFunction::emitReleaseBox(llvm::Value *value) {
1278+
if (doesNotRequireRefCounting(value))
1279+
return;
1280+
emitUnaryRefCountCall(*this, IGM.getReleaseBoxFn(), value);
1281+
}
1282+
12771283
void IRGenFunction::emitNativeSetDeallocating(llvm::Value *value) {
12781284
if (doesNotRequireRefCounting(value)) return;
12791285
emitUnaryRefCountCall(*this, IGM.getNativeSetDeallocatingFn(), value);

lib/IRGen/IRGenFunction.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,9 @@ class IRGenFunction {
626626
void emitNativeStrongRelease(llvm::Value *value, Atomicity atomicity);
627627
void emitNativeSetDeallocating(llvm::Value *value);
628628

629+
// Routines to deal with box (embedded) runtime calls.
630+
void emitReleaseBox(llvm::Value *value);
631+
629632
// Routines for the ObjC reference-counting style.
630633
void emitObjCStrongRetain(llvm::Value *value);
631634
llvm::Value *emitObjCRetainCall(llvm::Value *value);

stdlib/public/SwiftShims/swift/shims/EmbeddedShims.h

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,20 +113,42 @@ typedef struct {
113113
#endif
114114
} EmbeddedMetaDataPrefix;
115115

116-
static inline __swift_size_t _swift_embedded_metadata_get_size(void *metadata) {
116+
static inline __swift_size_t
117+
_swift_embedded_metadata_get_size(void *metadata) {
117118
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
118119
return fullmeta->vwt->size;
119120
}
120121

121-
static inline __swift_size_t _swift_embedded_metadata_get_align_mask(void *metadata) {
122-
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
123-
124-
unsigned flags = fullmeta->vwt->flags;
122+
static inline __swift_size_t
123+
_swift_embedded_metadata_get_align_mask_impl(EmbeddedMetaDataPrefix *fullMetadata) {
124+
unsigned flags = fullMetadata->vwt->flags;
125125
unsigned embeddedValueWitnessTableFlagsMask = 0xFF;
126126

127127
return flags & embeddedValueWitnessTableFlagsMask;
128128
}
129129

130+
static inline __swift_size_t
131+
_swift_embedded_metadata_get_align_mask(void *metadata) {
132+
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
133+
return _swift_embedded_metadata_get_align_mask_impl(fullmeta);
134+
}
135+
136+
static inline void
137+
_swift_embedded_invoke_box_destroy(void *object) {
138+
void *metadata = ((EmbeddedHeapObject *)object)->metadata;
139+
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
140+
__swift_size_t alignMask = _swift_embedded_metadata_get_align_mask_impl(fullmeta);
141+
__swift_size_t headerSize = sizeof(void*) + sizeof(__swift_size_t);
142+
__swift_size_t startOfBoxedValue = (headerSize + alignMask) & ~alignMask;
143+
void *addrInBox = (void *)(((unsigned char *)object) + startOfBoxedValue);
144+
fullmeta->vwt->destroyFn(addrInBox, metadata);
145+
}
146+
147+
static inline void
148+
_swift_embedded_initialize_box(void *metadata, void *newObjectAddr, void *oldObjectAddr) {
149+
EmbeddedMetaDataPrefix *fullmeta = (EmbeddedMetaDataPrefix*)&((void **)metadata)[-1];
150+
fullmeta->vwt->initializeWithCopyFn(newObjectAddr, oldObjectAddr, metadata);
151+
}
130152

131153
#ifdef __cplusplus
132154
} // extern "C"

stdlib/public/core/EmbeddedRuntime.swift

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ public func swift_allocEmptyBox() -> Builtin.RawPointer {
263263

264264

265265
@_silgen_name("swift_allocBox")
266-
public func swift_allocBox(metadata: Builtin.RawPointer) -> (Builtin.RawPointer, Builtin.RawPointer) {
266+
public func swift_allocBox(_ metadata: Builtin.RawPointer) -> (Builtin.RawPointer, Builtin.RawPointer) {
267267
let alignMask = Int(unsafe _swift_embedded_metadata_get_align_mask(UnsafeMutableRawPointer(metadata)))
268268
let size = Int(unsafe _swift_embedded_metadata_get_size(UnsafeMutableRawPointer(metadata)))
269269
let headerSize = unsafe MemoryLayout<Int>.size + MemoryLayout<UnsafeRawPointer>.size
@@ -283,12 +283,29 @@ public func swift_allocBox(metadata: Builtin.RawPointer) -> (Builtin.RawPointer,
283283
return (object._rawValue, boxedValueAddr._rawValue)
284284
}
285285

286-
@_cdecl("swift_deallocBox")
287-
public func swift_deallocBox(object: Builtin.RawPointer) {
286+
@c
287+
public func swift_deallocBox(_ object: Builtin.RawPointer) {
288288
unsafe free(UnsafeMutableRawPointer(object))
289289
}
290290

291-
291+
@_silgen_name("swift_makeBoxUnique")
292+
public func swifft_makeBoxUnique(buffer: Builtin.RawPointer, metadata: Builtin.RawPointer, alignMask: Int) -> (Builtin.RawPointer, Builtin.RawPointer){
293+
let addrOfHeapObjectPtr = unsafe UnsafeMutablePointer<Builtin.RawPointer>(buffer)
294+
let box = unsafe addrOfHeapObjectPtr.pointee
295+
let headerSize = unsafe MemoryLayout<Int>.size + MemoryLayout<UnsafeRawPointer>.size
296+
let startOfBoxedValue = ((headerSize + alignMask) & ~alignMask)
297+
let oldObjectAddr = unsafe UnsafeMutableRawPointer(box) + startOfBoxedValue
298+
299+
if !swift_isUniquelyReferenced_native(object: box) {
300+
let refAndObjectAddr = swift_allocBox(metadata)
301+
unsafe _swift_embedded_initialize_box(UnsafeMutableRawPointer(metadata), UnsafeMutableRawPointer(refAndObjectAddr.1), oldObjectAddr)
302+
swift_releaseBox(box)
303+
unsafe addrOfHeapObjectPtr.pointee = refAndObjectAddr.0
304+
return refAndObjectAddr
305+
} else {
306+
return (box, oldObjectAddr._rawValue)
307+
}
308+
}
292309

293310
/// Refcounting
294311

@@ -416,7 +433,7 @@ public func swift_release_n(object: Builtin.RawPointer, n: UInt32) {
416433
unsafe swift_release_n_(object: o, n: n)
417434
}
418435

419-
func swift_release_n_(object: UnsafeMutablePointer<HeapObject>?, n: UInt32) {
436+
func swift_release_n_(object: UnsafeMutablePointer<HeapObject>?, n: UInt32, isBoxRelease: Bool = false) {
420437
guard let object = unsafe object else {
421438
return
422439
}
@@ -441,12 +458,25 @@ func swift_release_n_(object: UnsafeMutablePointer<HeapObject>?, n: UInt32) {
441458
let doNotFree = unsafe (loadedRefcount & HeapObject.doNotFreeBit) != 0
442459
unsafe storeRelaxed(refcount, newValue: HeapObject.immortalRefCount | (doNotFree ? HeapObject.doNotFreeBit : 0))
443460

444-
unsafe _swift_embedded_invoke_heap_object_destroy(object)
461+
if isBoxRelease {
462+
unsafe _swift_embedded_invoke_box_destroy(object)
463+
} else {
464+
unsafe _swift_embedded_invoke_heap_object_destroy(object)
465+
}
445466
} else if resultingRefcount < 0 {
446467
fatalError("negative refcount")
447468
}
448469
}
449470

471+
@c
472+
public func swift_releaseBox(_ object: Builtin.RawPointer) {
473+
if !isValidPointerForNativeRetain(object: object) {
474+
fatalError("not a valid pointer for releaseBox")
475+
}
476+
let o = unsafe UnsafeMutablePointer<HeapObject>(object)
477+
unsafe swift_release_n_(object: o, n: 1, isBoxRelease: true)
478+
}
479+
450480
@c
451481
public func swift_bridgeObjectRelease(object: Builtin.RawPointer) {
452482
swift_bridgeObjectRelease_n(object: object, n: 1)

test/embedded/existential.swift

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ enum GenericEnumWithClass<T> {
5757
// OUTPUT: deinit called
5858
// OUTPUT: deinit called
5959

60+
// OUTPUT: deinit called
61+
// OUTPUT: deinit called
62+
// OUTPUT: deinit called
63+
// OUTPUT: deinit called
64+
6065
// OUTPUT-NOT: deinit called
6166

6267
func test() {
@@ -68,6 +73,8 @@ func test() {
6873
let _: any Any = GenericEnumWithClass.c(GC<Int>())
6974
let _: any Any = (3, 4)
7075
let _: any Any = (StructWithClass(), StructWithClass())
76+
// outline storage case
77+
let _: any Any = (StructWithClass(), StructWithClass(), StructWithClass(), StructWithClass())
7178
}
7279

7380
protocol Basic {
@@ -94,6 +101,14 @@ struct MyStruct : Derived {
94101
func b() { print("b MyStruct") }
95102
}
96103

104+
struct LargeMyStruct : Derived {
105+
var x = (1, 2, 3, 4, 5)
106+
var refCounted = StructWithClass()
107+
108+
func a() { print("a LargeMyStruct \(self.x.4)") }
109+
func b() { print("b LargeMyStruct") }
110+
}
111+
97112
enum MyEnum : Derived {
98113
case a
99114
case b(Int)
@@ -120,6 +135,7 @@ func test2(_ p: any Derived) {
120135

121136
protocol ValuePrinter {
122137
func printValue()
138+
mutating func mutate()
123139
}
124140
protocol WithAssoc {
125141
associatedtype Assoc : ValuePrinter
@@ -130,6 +146,20 @@ extension Int : ValuePrinter {
130146
func printValue() {
131147
print("my value: \(self)")
132148
}
149+
mutating func mutate() {
150+
self = 8
151+
print("my value (mutating expect 8): \(self)")
152+
}
153+
}
154+
155+
extension LargeMyStruct : ValuePrinter {
156+
func printValue() {
157+
print("my value of LargeMyStruct: \(self.x.4)")
158+
}
159+
mutating func mutate() {
160+
self.x = (6, 7, 8, 9, 10)
161+
print("my value of LargeMyStruct (mutating expect 10): \(self.x.4)")
162+
}
133163
}
134164

135165
struct ConformWithAssoc : WithAssoc {
@@ -139,30 +169,56 @@ struct ConformWithAssoc : WithAssoc {
139169
}
140170
}
141171

172+
struct ConformWithLargeAssoc : WithAssoc {
173+
var x = LargeMyStruct()
174+
175+
func a() -> LargeMyStruct {
176+
return x
177+
}
178+
}
179+
142180
func test3(_ p: any WithAssoc) {
143181
let x = p.a()
144182
x.printValue()
145183
}
146184

185+
func test4(_ p: any WithAssoc) {
186+
var x = p.a()
187+
let c = x
188+
x.mutate()
189+
c.printValue()
190+
}
191+
147192
@main
148193
struct Main {
149194
static func main() {
150195
test()
151196

197+
test2(Implementor())
152198
// OUTPUT: a
153199
// OUTPUT: b
200+
test2(5)
154201
// OUTPUT: a Int 5
155202
// OUTPUT: b Int 5
203+
test2(MyStruct())
156204
// OUTPUT: a MyStruct 5
157205
// OUTPUT: b MyStruct
206+
test2(MyEnum.b(5))
158207
// OUTPUT: a MyEnum
159208
// OUTPUT: 5
160209
// OUTPUT: b MyEnum
161-
// OUTPUT: my value: 1
162-
test2(Implementor())
163-
test2(5)
164-
test2(MyStruct())
165-
test2(MyEnum.b(5))
166210
test3(ConformWithAssoc())
211+
// OUTPUT: my value: 1
212+
test3(ConformWithLargeAssoc())
213+
// OUTPUT: my value of LargeMyStruct: 5
214+
// OUTPUT: deinit called
215+
test4(ConformWithAssoc())
216+
// OUTPUT: my value (mutating expect 8): 8
217+
// OUTPUT: my value: 1
218+
test4(ConformWithLargeAssoc())
219+
// OUTPUT: my value of LargeMyStruct (mutating expect 10): 10
220+
// OUTPUT: my value of LargeMyStruct: 5
221+
// OUTPUT: deinit called
222+
// OUTPUT-NOT: deinit called
167223
}
168224
}

0 commit comments

Comments
 (0)