Skip to content

Commit 8af1ee5

Browse files
committed
Merge remote-tracking branch 'origin/main' into rebranch
2 parents 7906594 + f766900 commit 8af1ee5

File tree

4 files changed

+91
-14
lines changed

4 files changed

+91
-14
lines changed

lib/IRGen/GenConstant.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,8 @@ llvm::Constant *irgen::emitConstantObject(IRGenModule &IGM, ObjectInst *OI,
466466
if (IGM.canMakeStaticObjectReadOnly(OI->getType())) {
467467
if (!IGM.swiftImmortalRefCount) {
468468
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
469-
// = HeapObject.immortalRefCount
469+
// = HeapObject.immortalRefCount | HeapObject.doNotFreeBit
470+
// 0xffff_ffff on 32-bit, 0xffff_ffff_ffff_ffff on 64-bit
470471
IGM.swiftImmortalRefCount = llvm::ConstantInt::get(IGM.IntPtrTy, -1);
471472
} else {
472473
IGM.swiftImmortalRefCount = llvm::ConstantExpr::getPtrToInt(

stdlib/public/core/EmbeddedRuntime.swift

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,20 +38,21 @@ public struct HeapObject {
3838
// to think about supporting (or banning) weak and/or unowned references.
3939
var refcount: Int
4040

41+
// Note: The immortalRefCount value is also hard-coded in IRGen in `irgen::emitConstantObject`.
4142
#if _pointerBitWidth(_64)
42-
static let doNotFreeBit = Int(bitPattern: 0x8000_0000_0000_0000)
43-
static let refcountMask = Int(bitPattern: 0x7fff_ffff_ffff_ffff)
43+
static let doNotFreeBit = Int(bitPattern: 0x8000_0000_0000_0000)
44+
static let refcountMask = Int(bitPattern: 0x7fff_ffff_ffff_ffff)
45+
static let immortalRefCount = Int(bitPattern: 0x7fff_ffff_ffff_ffff) // Make sure we don't have doNotFreeBit set
4446
#elseif _pointerBitWidth(_32)
45-
static let doNotFreeBit = Int(bitPattern: 0x8000_0000)
46-
static let refcountMask = Int(bitPattern: 0x7fff_ffff)
47+
static let doNotFreeBit = Int(bitPattern: 0x8000_0000)
48+
static let refcountMask = Int(bitPattern: 0x7fff_ffff)
49+
static let immortalRefCount = Int(bitPattern: 0x7fff_ffff) // Make sure we don't have doNotFreeBit set
4750
#elseif _pointerBitWidth(_16)
48-
static let doNotFreeBit = Int(bitPattern: 0x8000)
49-
static let refcountMask = Int(bitPattern: 0x7fff)
51+
static let doNotFreeBit = Int(bitPattern: 0x8000)
52+
static let refcountMask = Int(bitPattern: 0x7fff)
53+
static let immortalRefCount = Int(bitPattern: 0x7fff) // Make sure we don't have doNotFreeBit set
5054
#endif
5155

52-
// Note: The immortalRefCount value of -1 is also hard-coded in IRGen in `irgen::emitConstantObject`.
53-
static let immortalRefCount = -1
54-
5556
#if _pointerBitWidth(_64)
5657
static let immortalObjectPointerBit = UInt(0x8000_0000_0000_0000)
5758
#endif
@@ -158,7 +159,7 @@ public func swift_initStaticObject(metadata: Builtin.RawPointer, object: Builtin
158159

159160
func swift_initStaticObject(metadata: UnsafeMutablePointer<ClassMetadata>, object: UnsafeMutablePointer<HeapObject>) -> UnsafeMutablePointer<HeapObject> {
160161
_swift_embedded_set_heap_object_metadata_pointer(object, metadata)
161-
object.pointee.refcount = HeapObject.immortalRefCount
162+
object.pointee.refcount = HeapObject.immortalRefCount | HeapObject.doNotFreeBit
162163
return object
163164
}
164165

@@ -251,7 +252,7 @@ public func swift_retain_n(object: Builtin.RawPointer, n: UInt32) -> Builtin.Raw
251252

252253
func swift_retain_n_(object: UnsafeMutablePointer<HeapObject>, n: UInt32) -> UnsafeMutablePointer<HeapObject> {
253254
let refcount = refcountPointer(for: object)
254-
if loadRelaxed(refcount) == HeapObject.immortalRefCount {
255+
if loadRelaxed(refcount) & HeapObject.refcountMask == HeapObject.immortalRefCount {
255256
return object
256257
}
257258

@@ -294,7 +295,8 @@ func swift_release_n_(object: UnsafeMutablePointer<HeapObject>?, n: UInt32) {
294295
}
295296

296297
let refcount = refcountPointer(for: object)
297-
if loadRelaxed(refcount) == HeapObject.immortalRefCount {
298+
let loadedRefcount = loadRelaxed(refcount)
299+
if loadedRefcount & HeapObject.refcountMask == HeapObject.immortalRefCount {
298300
return
299301
}
300302

@@ -309,7 +311,8 @@ func swift_release_n_(object: UnsafeMutablePointer<HeapObject>?, n: UInt32) {
309311
// There can only be one thread with a reference at this point (because
310312
// we're releasing the last existing reference), so a relaxed store is
311313
// enough.
312-
storeRelaxed(refcount, newValue: HeapObject.immortalRefCount)
314+
let doNotFree = (loadedRefcount & HeapObject.doNotFreeBit) != 0
315+
storeRelaxed(refcount, newValue: HeapObject.immortalRefCount | (doNotFree ? HeapObject.doNotFreeBit : 0))
313316

314317
_swift_embedded_invoke_heap_object_destroy(object)
315318
} else if resultingRefcount < 0 {

test/embedded/Inputs/debug-malloc.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <stdlib.h>
2+
#include <stdio.h>
3+
4+
#define HEAP_SIZE (8 * 1024)
5+
6+
__attribute__((aligned(16)))
7+
char heap[HEAP_SIZE] = {};
8+
9+
size_t next_heap_index = 0;
10+
11+
void *calloc(size_t count, size_t size) {
12+
printf("malloc(%ld)", count);
13+
14+
if (next_heap_index + count * size > HEAP_SIZE) {
15+
puts("HEAP EXHAUSTED\n");
16+
__builtin_trap();
17+
}
18+
void *p = &heap[next_heap_index];
19+
next_heap_index += count * size;
20+
printf("-> %p\n", p);
21+
return p;
22+
}
23+
24+
void *malloc(size_t count) {
25+
return calloc(count, 1);
26+
}
27+
28+
int posix_memalign(void **memptr, size_t alignment, size_t size) {
29+
*memptr = calloc(size + alignment, 1);
30+
if (((uintptr_t)*memptr) % alignment == 0) return 0;
31+
*(uintptr_t *)memptr += alignment - ((uintptr_t)*memptr % alignment);
32+
return 0;
33+
}
34+
35+
void free(void *ptr) {
36+
// don't actually free
37+
printf("free(%p)\n", ptr);
38+
}

test/embedded/refcounting-leak.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -enable-experimental-feature Embedded -parse-as-library %s -c -o %t/a.o
3+
// RUN: %target-clang -x c -std=c11 -c %S/Inputs/debug-malloc.c -o %t/debug-malloc.o
4+
// RUN: %target-clang %t/a.o %t/debug-malloc.o -o %t/a.out
5+
// RUN: %target-run %t/a.out | %FileCheck %s
6+
7+
// REQUIRES: executable_test
8+
// REQUIRES: swift_in_compiler
9+
// REQUIRES: optimized_stdlib
10+
// REQUIRES: OS=macosx
11+
12+
var x: UInt = 1
13+
func randomNumber() -> UInt {
14+
x = ((x >> 16) ^ x) &* 0x45d9f3b
15+
x = ((x >> 16) ^ x) &* 0x45d9f3b
16+
x = (x >> 16) ^ x
17+
return x
18+
}
19+
20+
@main
21+
struct Main {
22+
static func main() {
23+
for _ in 0 ..< 3 {
24+
_ = randomNumber().description
25+
}
26+
print("OK!")
27+
// CHECK: malloc
28+
// CHECK: free
29+
// CHECK: malloc
30+
// CHECK: free
31+
// CHECK: malloc
32+
// CHECK: free
33+
// CHECK: OK!
34+
}
35+
}

0 commit comments

Comments
 (0)