Skip to content

Commit 91bafd4

Browse files
committed
Reintegrates BumpPtrAllocator in AutoDiffLinearMapContext
This commit builds on top of a previous commit that fixed memory leak issues in the autodiff loop context allocation builtins. The previous commit allocated loop contexts in a box, which is known to have memory scaling issues. This commit modifies AutoDiffLinearMapContext to perform memory allocation using the BumpPtrAllocator, like it used to, while introducing new bookkeeping logic that enables AutoDiffLinearMapContext to prevent memory leaks, unlike before.
1 parent 2ccc0b3 commit 91bafd4

File tree

2 files changed

+79
-80
lines changed

2 files changed

+79
-80
lines changed

stdlib/public/runtime/AutoDiffSupport.cpp

Lines changed: 31 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,7 @@ using namespace llvm;
2121

2222
SWIFT_CC(swift)
2323
static void destroyLinearMapContext(SWIFT_CONTEXT HeapObject *obj) {
24-
auto *linearMapContext = static_cast<AutoDiffLinearMapContext *>(obj);
25-
26-
for (auto *heapObjectPtr : linearMapContext->getAllocatedHeapObjects()) {
27-
swift_release(heapObjectPtr);
28-
}
29-
30-
linearMapContext->~AutoDiffLinearMapContext();
24+
static_cast<AutoDiffLinearMapContext *>(obj)->~AutoDiffLinearMapContext();
3125
free(obj);
3226
}
3327

@@ -50,53 +44,48 @@ static FullMetadata<HeapMetadata> linearMapContextHeapMetadata = {
5044
};
5145

5246
AutoDiffLinearMapContext::AutoDiffLinearMapContext(
53-
OpaqueValue *const topLevelLinearMapContextProjection)
47+
const Metadata *topLevelLinearMapContextMetadata)
5448
: HeapObject(&linearMapContextHeapMetadata) {
55-
this->topLevelLinearMapContextProjection = topLevelLinearMapContextProjection;
49+
allocatedContextObjects.push_back(AllocatedContextObjectRecord{
50+
topLevelLinearMapContextMetadata, projectTopLevelSubcontext()});
5651
}
5752

58-
AutoDiffLinearMapContext *swift::swift_autoDiffCreateLinearMapContext(
59-
const Metadata *topLevelLinearMapContextMetadata) {
60-
// Linear map context metadata must have non-null value witnesses
61-
assert(topLevelLinearMapContextMetadata->getValueWitnesses());
62-
63-
// Allocate a box for the top-level linear map context
64-
auto [topLevelContextHeapObjectPtr, toplevelContextProjection] =
65-
swift_allocBox(topLevelLinearMapContextMetadata);
66-
67-
// Create a linear map context object that stores the projection
68-
// for the top level context
69-
auto linearMapContext =
70-
new AutoDiffLinearMapContext(toplevelContextProjection);
53+
void *AutoDiffLinearMapContext::projectTopLevelSubcontext() const {
54+
auto offset = alignTo(sizeof(AutoDiffLinearMapContext),
55+
alignof(AutoDiffLinearMapContext));
56+
return const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(this) +
57+
offset);
58+
}
7159

72-
// Stash away the `HeapObject` pointer for the allocated context
73-
// for proper "release" during clean up.
74-
linearMapContext->storeAllocatedHeapObjectPtr(topLevelContextHeapObjectPtr);
60+
void *AutoDiffLinearMapContext::allocateSubcontext(
61+
const Metadata *contextObjectMetadata) {
62+
auto size = contextObjectMetadata->vw_size();
63+
auto align = contextObjectMetadata->vw_alignment();
64+
auto *contextObjectPtr = allocator.Allocate(size, align);
65+
allocatedContextObjects.push_back(
66+
AllocatedContextObjectRecord{contextObjectMetadata, contextObjectPtr});
67+
return contextObjectPtr;
68+
}
7569

76-
// Return the newly created linear map context object
77-
return linearMapContext;
70+
AutoDiffLinearMapContext *swift::swift_autoDiffCreateLinearMapContext(
71+
const Metadata *topLevelLinearMapContextMetadata) {
72+
auto topLevelLinearMapContextSize =
73+
topLevelLinearMapContextMetadata->vw_size();
74+
auto allocationSize = alignTo(sizeof(AutoDiffLinearMapContext),
75+
alignof(AutoDiffLinearMapContext)) +
76+
topLevelLinearMapContextSize;
77+
auto *buffer = (AutoDiffLinearMapContext *)malloc(allocationSize);
78+
return ::new (buffer)
79+
AutoDiffLinearMapContext(topLevelLinearMapContextMetadata);
7880
}
7981

8082
void *swift::swift_autoDiffProjectTopLevelSubcontext(
8183
AutoDiffLinearMapContext *linearMapContext) {
82-
return static_cast<void *>(
83-
linearMapContext->getTopLevelLinearMapContextProjection());
84+
return static_cast<void *>(linearMapContext->projectTopLevelSubcontext());
8485
}
8586

8687
void *swift::swift_autoDiffAllocateSubcontext(
8788
AutoDiffLinearMapContext *linearMapContext,
8889
const Metadata *linearMapSubcontextMetadata) {
89-
// Linear map context metadata must have non-null value witnesses
90-
assert(linearMapSubcontextMetadata->getValueWitnesses());
91-
92-
// Allocate a box for the linear map subcontext
93-
auto [subcontextHeapObjectPtr, subcontextProjection] =
94-
swift_allocBox(linearMapSubcontextMetadata);
95-
96-
// Stash away the `HeapObject` pointer for the allocated context
97-
// for proper "release" during clean up.
98-
linearMapContext->storeAllocatedHeapObjectPtr(subcontextHeapObjectPtr);
99-
100-
// Return the subcontext projection
101-
return static_cast<void *>(subcontextProjection);
90+
return linearMapContext->allocateSubcontext(linearMapSubcontextMetadata);
10291
}

stdlib/public/runtime/AutoDiffSupport.h

Lines changed: 48 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,58 +13,68 @@
1313
#ifndef SWIFT_RUNTIME_AUTODIFF_SUPPORT_H
1414
#define SWIFT_RUNTIME_AUTODIFF_SUPPORT_H
1515

16+
#include "swift/ABI/Metadata.h"
1617
#include "swift/Runtime/Config.h"
1718
#include "swift/Runtime/HeapObject.h"
1819
#include "llvm/ADT/ArrayRef.h"
1920
#include "llvm/ADT/SmallVector.h"
21+
#include "llvm/Support/Allocator.h"
2022

2123
namespace swift {
2224
/// A data structure responsible for efficiently allocating closure contexts for
2325
/// linear maps such as pullbacks, including recursive branching trace enum
2426
/// case payloads.
2527
class AutoDiffLinearMapContext : public HeapObject {
28+
/// A simple wrapper around a context object allocated by the
29+
/// `AutoDiffLinearMapContext` type. This type knows all the "physical"
30+
/// properties and behavior of the allocated context object by way of
31+
/// storing the allocated type's `TypeMetadata`. It uses this information
32+
/// to ensure that the allocated context object is destroyed/deinitialized
33+
/// properly, upon its own destruction.
34+
class [[nodiscard]] AllocatedContextObjectRecord final {
35+
const Metadata *contextObjectMetadata;
36+
OpaqueValue *contextObjectPtr;
37+
38+
public:
39+
AllocatedContextObjectRecord(const Metadata *contextObjectMetadata,
40+
OpaqueValue *contextObjectPtr)
41+
: contextObjectMetadata(contextObjectMetadata),
42+
contextObjectPtr(contextObjectPtr) {}
43+
44+
AllocatedContextObjectRecord(const Metadata *contextObjectMetadata,
45+
void *contextObjectPtr)
46+
: AllocatedContextObjectRecord(
47+
contextObjectMetadata,
48+
static_cast<OpaqueValue *>(contextObjectPtr)) {}
49+
50+
~AllocatedContextObjectRecord() {
51+
contextObjectMetadata->vw_destroy(contextObjectPtr);
52+
}
53+
54+
size_t size() const { return contextObjectMetadata->vw_size(); }
55+
56+
size_t align() const { return contextObjectMetadata->vw_alignment(); }
57+
};
58+
2659
private:
27-
// TODO: Commenting out BumpPtrAllocator temporarily
28-
// until we move away from the interim solution of allocating
29-
// boxes for linear map contexts/subcontexts.
30-
//
31-
// /// The underlying allocator.
32-
// // TODO: Use a custom allocator so that the initial slab can be
33-
// // tail-allocated.
34-
// llvm::BumpPtrAllocator allocator;
35-
36-
/// A projection/pointer to the memory storing the
37-
/// top-level linear map context object.
38-
OpaqueValue *topLevelLinearMapContextProjection;
39-
40-
/// Storage for `HeapObject` pointers to the linear map
41-
/// context and subcontexts allocated for derivatives with
42-
/// loops.
43-
llvm::SmallVector<HeapObject *, 4> allocatedHeapObjects;
60+
/// The underlying allocator.
61+
// TODO: Use a custom allocator so that the initial slab can be
62+
// tail-allocated.
63+
llvm::BumpPtrAllocator allocator;
64+
65+
/// Storage for `AllocatedContextObjectRecord`s, corresponding to the
66+
/// subcontext allocations performed by the type.
67+
llvm::SmallVector<AllocatedContextObjectRecord, 4> allocatedContextObjects;
4468

4569
public:
4670
/// Creates a linear map context.
47-
AutoDiffLinearMapContext(OpaqueValue *const);
48-
49-
// TODO: Commenting out BumpPtrAllocator temporarily
50-
// until we move away from the interim solution of allocating
51-
// boxes for linear map contexts/subcontexts.
52-
//
53-
// llvm::BumpPtrAllocator& getAllocator() const {
54-
// return const_cast<llvm::BumpPtrAllocator&>(this->allocator);
55-
// }
56-
57-
OpaqueValue *getTopLevelLinearMapContextProjection() const {
58-
return this->topLevelLinearMapContextProjection;
59-
}
60-
61-
llvm::ArrayRef<HeapObject *> getAllocatedHeapObjects() const {
62-
return this->allocatedHeapObjects;
63-
}
64-
65-
void storeAllocatedHeapObjectPtr(HeapObject *allocatedHeapObjectPtr) {
66-
this->allocatedHeapObjects.push_back(allocatedHeapObjectPtr);
67-
}
71+
AutoDiffLinearMapContext(const Metadata *topLevelLinearMapContextMetadata);
72+
73+
/// Returns the address of the tail-allocated top-level subcontext.
74+
void *projectTopLevelSubcontext() const;
75+
76+
/// Allocates memory for a new subcontext.
77+
void *allocateSubcontext(const Metadata *contextObjectMetadata);
6878
};
6979

7080
/// Creates a linear map context with a tail-allocated top-level subcontext.

0 commit comments

Comments
 (0)