Skip to content

Commit 01298a0

Browse files
committed
[embedded] Expand embedded runtime to handle stack promoted refcounted objects
1 parent 125343d commit 01298a0

File tree

2 files changed

+117
-4
lines changed

2 files changed

+117
-4
lines changed

stdlib/public/core/EmbeddedRuntime.swift

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ public struct HeapObject {
2828
// to think about supporting (or banning) weak and/or unowned references.
2929
var refcount: Int
3030

31+
#if _pointerBitWidth(_64)
32+
static let doNotFreeBit = Int(bitPattern: 0x8000_0000_0000_0000)
33+
static let refcountMask = Int(bitPattern: 0x7fff_ffff_ffff_ffff)
34+
#else
35+
static let doNotFreeBit = Int(bitPattern: 0x8000_0000)
36+
static let refcountMask = Int(bitPattern: 0x7fff_ffff)
37+
#endif
38+
3139
static let immortalRefCount = -1
3240
}
3341

@@ -65,18 +73,24 @@ public func swift_allocObject(metadata: UnsafeMutablePointer<ClassMetadata>, req
6573

6674
@_silgen_name("swift_deallocClassInstance")
6775
public func swift_deallocClassInstance(object: UnsafeMutablePointer<HeapObject>, allocatedSize: Int, allocatedAlignMask: Int) {
76+
if (object.pointee.refcount & HeapObject.doNotFreeBit) != 0 {
77+
return
78+
}
79+
6880
free(object)
6981
}
7082

7183
@_silgen_name("swift_initStackObject")
7284
public func swift_initStackObject(metadata: UnsafeMutablePointer<ClassMetadata>, object: UnsafeMutablePointer<HeapObject>) -> UnsafeMutablePointer<HeapObject> {
7385
object.pointee.metadata = metadata
74-
75-
// TODO/FIXME: Making all stack promoted objects immortal is not correct.
76-
object.pointee.refcount = HeapObject.immortalRefCount
86+
object.pointee.refcount = 1 | HeapObject.doNotFreeBit
7787
return object
7888
}
7989

90+
@_silgen_name("swift_setDeallocating")
91+
public func swift_setDeallocating(object: UnsafeMutablePointer<HeapObject>) {
92+
}
93+
8094
// TODO/FIXME: Refcounting and swift_once is not thread-safe, the following only works in single-threaded environments.
8195

8296
@_silgen_name("swift_isUniquelyReferenced_nonNull_native")
@@ -102,7 +116,7 @@ public func swift_release(object: Builtin.RawPointer) {
102116
// TODO/FIXME: Refcounting is not thread-safe, the following only works in single-threaded environments.
103117
if o.pointee.refcount == HeapObject.immortalRefCount { return }
104118
o.pointee.refcount -= 1
105-
if o.pointee.refcount == 0 {
119+
if (o.pointee.refcount & HeapObject.refcountMask) == 0 {
106120
_swift_runtime_invoke_heap_object_destroy(o.pointee.metadata.pointee.destroy, o)
107121
}
108122
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -O -Xllvm -sil-disable-pass=function-signature-opts %s %S/Inputs/print.swift -enable-experimental-feature Embedded -module-name main -emit-irgen | %FileCheck %s --check-prefix CHECK-IR
3+
// RUN: %target-swift-frontend -O -Xllvm -sil-disable-pass=function-signature-opts %s %S/Inputs/print.swift -enable-experimental-feature Embedded -c -o %t/main.o
4+
// RUN: %target-clang %t/main.o -o %t/a.out -dead_strip
5+
// RUN: %target-run %t/a.out | %FileCheck %s
6+
7+
// REQUIRES: executable_test
8+
// REQUIRES: optimized_stdlib
9+
// REQUIRES: VENDOR=apple
10+
// REQUIRES: OS=macosx
11+
12+
public class MyClass {
13+
public init() { print("MyClass.init") }
14+
deinit { print("MyClass.deinit") }
15+
public func foo() { print("MyClass.foo") }
16+
}
17+
18+
public class MySubClass: MyClass {
19+
public override init() { print("MySubClass.init") }
20+
deinit { print("MySubClass.deinit") }
21+
public override func foo() { print("MySubClass.foo") }
22+
}
23+
24+
@inline(never)
25+
public func bar(o: MyClass) {
26+
o.foo()
27+
}
28+
29+
final public class MyFinalClass {
30+
public init() { print("MyFinalClass.init") }
31+
deinit { print("MyFinalClass.deinit") }
32+
public func foo() { print("MyFinalClass.foo") }
33+
}
34+
35+
public struct MyStruct {
36+
@inline(never)
37+
init(_ c: MyFinalClass) {
38+
c.foo()
39+
}
40+
}
41+
42+
@main
43+
struct Main {
44+
static func test1() {
45+
let o = MySubClass()
46+
// CHECK: MySubClass.init
47+
// CHECK: MyClass.init
48+
o.foo()
49+
// CHECK: MySubClass.foo
50+
bar(o: o)
51+
// CHECK: MySubClass.foo
52+
// CHECK: MySubClass.deinit
53+
// CHECK: MyClass.deinit
54+
}
55+
56+
static func test2() -> MyStruct {
57+
let c = MyFinalClass()
58+
// CHECK: MyFinalClass.init
59+
return MyStruct(c)
60+
// CHECK: MyFinalClass.foo
61+
// CHECK: MyFinalClass.deinit
62+
}
63+
64+
static func main() {
65+
test1()
66+
print("")
67+
test2()
68+
}
69+
}
70+
71+
// Check that stack promotion did really happen
72+
73+
// CHECK-IR: define {{.*}}@"$s4main8MyStructVyAcA0B10FinalClassCcfC"
74+
// CHECK-IR-NEXT: entry:
75+
// CHECK-IR-NEXT: call {{.*}}@"$s4main5print_10terminatorys12StaticStringV_AEtF"
76+
// CHECK-IR-NEXT: call {{.*}}@swift_release
77+
// CHECK-IR-NEXT: ret void
78+
// CHECK-IR-NEXT: }
79+
80+
// CHECK-IR: define {{.*}}@main
81+
// CHECK-IR-NEXT: entry:
82+
// CHECK-IR-NEXT: alloca %T4main10MySubClassC
83+
// CHECK-IR-NEXT: alloca %T4main12MyFinalClassC
84+
// CHECK-IR-NEXT: call {{.*}}@swift_initStackObject
85+
// CHECK-IR-NEXT: call {{.*}}@"$s4main5print_10terminatorys12StaticStringV_AEtF"
86+
// CHECK-IR-NEXT: call {{.*}}@"$s4main5print_10terminatorys12StaticStringV_AEtF"
87+
// CHECK-IR-NEXT: call {{.*}}@"$s4main5print_10terminatorys12StaticStringV_AEtF"
88+
// CHECK-IR-NEXT: call {{.*}}@"$s4main3bar1oyAA7MyClassC_tF"
89+
// CHECK-IR-NEXT: call {{.*}}@swift_setDeallocating
90+
// CHECK-IR-NEXT: call {{.*}}@"$s4main5print_10terminatorys12StaticStringV_AEtF"
91+
// CHECK-IR-NEXT: call {{.*}}@"$s4main5print_10terminatorys12StaticStringV_AEtF"
92+
// CHECK-IR-NEXT: call {{.*}}@llvm.lifetime.end.p0
93+
// CHECK-IR-NEXT: call {{.*}}@"$s4main5print_10terminatorys12StaticStringV_AEtF"
94+
// CHECK-IR-NEXT: call {{.*}}@swift_initStackObject
95+
// CHECK-IR-NEXT: call {{.*}}@"$s4main5print_10terminatorys12StaticStringV_AEtF"
96+
// CHECK-IR-NEXT: call {{.*}}@"$s4main8MyStructVyAcA0B10FinalClassCcfC"
97+
// CHECK-IR-NEXT: call {{.*}}@llvm.lifetime.end.p0
98+
// CHECK-IR-NEXT: ret {{.*}}0
99+
// CHECK-IR-NEXT: }

0 commit comments

Comments
 (0)