Skip to content

Commit 5ad0331

Browse files
authored
Adopt malloc_type for allocating Swift objects from runtime (#63226)
Adopt malloc_type for allocating Swift objects from runtime Radar-Id: rdar://98998492
1 parent 2b542cb commit 5ad0331

File tree

4 files changed

+149
-4
lines changed

4 files changed

+149
-4
lines changed

include/swift/Runtime/Config.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,16 @@
129129
#error Masking ISAs are incompatible with opaque ISAs
130130
#endif
131131

132+
#if defined(__APPLE__) && defined(__LP64__) && __has_include(<malloc_type_private.h>) && SWIFT_STDLIB_HAS_DARWIN_LIBMALLOC
133+
# include <TargetConditionals.h>
134+
# if TARGET_OS_IOS && !TARGET_OS_SIMULATOR
135+
# define SWIFT_STDLIB_HAS_MALLOC_TYPE 1
136+
# endif
137+
#endif
138+
#ifndef SWIFT_STDLIB_HAS_MALLOC_TYPE
139+
# define SWIFT_STDLIB_HAS_MALLOC_TYPE 0
140+
#endif
141+
132142
/// Which bits in the class metadata are used to distinguish Swift classes
133143
/// from ObjC classes?
134144
#ifndef SWIFT_CLASS_IS_SWIFT_MASK

include/swift/Runtime/Heap.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ namespace swift {
3232
SWIFT_EXTERN_C SWIFT_RETURNS_NONNULL SWIFT_NODISCARD SWIFT_RUNTIME_EXPORT_ATTRIBUTE
3333
void *swift_slowAlloc(size_t bytes, size_t alignMask);
3434

35+
using MallocTypeId = unsigned long long;
36+
37+
SWIFT_RETURNS_NONNULL SWIFT_NODISCARD
38+
void *swift_slowAllocTyped(size_t bytes, size_t alignMask, MallocTypeId typeId);
39+
3540
// If the caller cannot promise to zero the object during destruction,
3641
// then call these corresponding APIs:
3742
SWIFT_RUNTIME_EXPORT

stdlib/public/runtime/Heap.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,11 @@ static inline malloc_zone_t *DEFAULT_ZONE() {
7373
// the use of AlignedAlloc. This allows manually allocated to memory to always
7474
// be deallocated with AlignedFree without knowledge of its original allocation
7575
// alignment.
76-
//
76+
static size_t computeAlignment(size_t alignMask) {
77+
return (alignMask == ~(size_t(0))) ? _swift_MinAllocationAlignment
78+
: alignMask + 1;
79+
}
80+
7781
// For alignMask > (_minAllocationAlignment-1)
7882
// i.e. alignment == 0 || alignment > _minAllocationAlignment:
7983
// The runtime must use AlignedAlloc, and the standard library must
@@ -93,15 +97,32 @@ void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
9397
p = malloc(size);
9498
#endif
9599
} else {
96-
size_t alignment = (alignMask == ~(size_t(0)))
97-
? _swift_MinAllocationAlignment
98-
: alignMask + 1;
100+
size_t alignment = computeAlignment(alignMask);
99101
p = AlignedAlloc(size, alignment);
100102
}
101103
if (!p) swift::crash("Could not allocate memory.");
102104
return p;
103105
}
104106

107+
void *swift::swift_slowAllocTyped(size_t size, size_t alignMask,
108+
MallocTypeId typeId) {
109+
#if SWIFT_STDLIB_HAS_MALLOC_TYPE
110+
if (__builtin_available(macOS 9998, iOS 9998, tvOS 9998, watchOS 9998, *)) {
111+
void *p;
112+
// This check also forces "default" alignment to use malloc_memalign().
113+
if (alignMask <= MALLOC_ALIGN_MASK) {
114+
p = malloc_type_zone_malloc(DEFAULT_ZONE(), size, typeId);
115+
} else {
116+
size_t alignment = computeAlignment(alignMask);
117+
p = malloc_type_zone_memalign(DEFAULT_ZONE(), alignment, size, typeId);
118+
}
119+
if (!p) swift::crash("Could not allocate memory.");
120+
return p;
121+
}
122+
#endif
123+
return swift_slowAlloc(size, alignMask);
124+
}
125+
105126
// Unknown alignment is specified by passing alignMask == ~(size_t(0)), forcing
106127
// the AlignedFree deallocation path for unknown alignment. The memory
107128
// deallocated with unknown alignment must have been allocated with either

stdlib/public/runtime/HeapObject.cpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
# include "swift/Runtime/ObjCBridge.h"
4545
# include <dlfcn.h>
4646
#endif
47+
#if SWIFT_STDLIB_HAS_MALLOC_TYPE
48+
# include <malloc_type_private.h>
49+
#endif
4750
#include "Leaks.h"
4851

4952
using namespace swift;
@@ -115,12 +118,118 @@ static HeapObject *_swift_tryRetain_(HeapObject *object)
115118
return _ ## name ## _ args; \
116119
} while(0)
117120

121+
#if SWIFT_STDLIB_HAS_MALLOC_TYPE
122+
static malloc_type_summary_t
123+
computeMallocTypeSummary(const HeapMetadata *heapMetadata) {
124+
assert(isHeapMetadataKind(heapMetadata->getKind()));
125+
auto *classMetadata = heapMetadata->getClassObject();
126+
auto *typeDesc = heapMetadata->getTypeContextDescriptor();
127+
128+
malloc_type_summary_t summary = {};
129+
130+
// Objc
131+
if (classMetadata && classMetadata->isPureObjC()) {
132+
summary.type_kind = MALLOC_TYPE_KIND_OBJC;
133+
return summary;
134+
}
135+
136+
// Runtime internal and unclassified
137+
if (!typeDesc) {
138+
summary.type_kind = MALLOC_TYPE_KIND_CXX;
139+
return summary;
140+
}
141+
142+
// Swift
143+
summary.type_kind = MALLOC_TYPE_KIND_SWIFT;
144+
145+
bool isGenericData = true;
146+
for (auto &field : *typeDesc->Fields.get()) {
147+
if (field.isIndirectCase()) {
148+
isGenericData = false;
149+
if (field.isVar())
150+
summary.layout_semantics.data_pointer = true;
151+
else
152+
summary.layout_semantics.immutable_pointer = true;
153+
}
154+
}
155+
156+
if (classMetadata->Flags & ClassFlags::UsesSwiftRefcounting) {
157+
summary.layout_semantics.reference_count = true;
158+
} else {
159+
summary.layout_semantics.generic_data = isGenericData;
160+
}
161+
162+
return summary;
163+
164+
// FIXME: these are all the things we are potentially interested in
165+
// typedef struct {
166+
// bool data_pointer : 1;
167+
// bool struct_pointer : 1;
168+
// bool immutable_pointer : 1;
169+
// bool anonymous_pointer : 1;
170+
// bool reference_count : 1;
171+
// bool resource_handle : 1;
172+
// bool spatial_bounds : 1;
173+
// bool tainted_data : 1;
174+
// bool generic_data : 1;
175+
// uint16_t unused : 7;
176+
// } malloc_type_layout_semantics_t;
177+
}
178+
179+
struct MallocTypeCacheEntry {
180+
// union malloc_type_descriptor_t {
181+
// struct {
182+
// uint32_t hash;
183+
// malloc_type_summary_t summary;
184+
// };
185+
// malloc_type_id_t type_id;
186+
// };
187+
malloc_type_descriptor_t desc;
188+
189+
friend llvm::hash_code hash_value(const MallocTypeCacheEntry &entry) {
190+
return hash_value(entry.desc.hash);
191+
}
192+
bool matchesKey(uint32_t key) const { return desc.hash == key; }
193+
};
194+
static ConcurrentReadableHashMap<MallocTypeCacheEntry> MallocTypes;
195+
196+
static malloc_type_id_t getMallocTypeId(const HeapMetadata *heapMetadata) {
197+
uint64_t metadataPtrBits = reinterpret_cast<uint64_t>(heapMetadata);
198+
uint32_t key = (metadataPtrBits >> 32) ^ (metadataPtrBits >> 0);
199+
200+
{
201+
auto snapshot = MallocTypes.snapshot();
202+
if (auto *entry = snapshot.find(key))
203+
return entry->desc.type_id;
204+
}
205+
206+
malloc_type_descriptor_t desc = {
207+
.hash = key,
208+
.summary = computeMallocTypeSummary(heapMetadata)
209+
};
210+
211+
MallocTypes.getOrInsert(
212+
key, [desc](MallocTypeCacheEntry *entry, bool created) {
213+
if (created)
214+
entry->desc = desc;
215+
return true;
216+
});
217+
218+
return desc.type_id;
219+
}
220+
#endif // SWIFT_STDLIB_HAS_MALLOC_TYPE
221+
118222
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
119223
size_t requiredSize,
120224
size_t requiredAlignmentMask) {
121225
assert(isAlignmentMask(requiredAlignmentMask));
226+
#if SWIFT_STDLIB_HAS_MALLOC_TYPE
227+
auto object = reinterpret_cast<HeapObject *>(swift_slowAllocTyped(
228+
requiredSize, requiredAlignmentMask, getMallocTypeId(metadata)));
229+
#else
122230
auto object = reinterpret_cast<HeapObject *>(
123231
swift_slowAlloc(requiredSize, requiredAlignmentMask));
232+
#endif
124233

125234
// NOTE: this relies on the C++17 guaranteed semantics of no null-pointer
126235
// check on the placement new allocator which we have observed on Windows,

0 commit comments

Comments
 (0)