Skip to main content

squads_multisig_program/
allocator.rs

1/*
2Optimizing Bump Heap Allocation
3
4Objective: Increase available heap memory while maintaining flexibility in program invocation.
5
61. Initial State: Default 32 KiB Heap
7
8Memory Layout:
90x300000000           0x300008000
10      |                    |
11      v                    v
12      [--------------------]
13      ^                    ^
14      |                    |
15 VM Lower              VM Upper
16 Boundary              Boundary
17
18Default Allocator (Allocates Backwards / Top Down) (Default 32 KiB):
190x300000000           0x300008000
20      |                    |
21      [--------------------]
22                           ^
23                           |
24                  Allocation starts here (SAFE)
25
262. Naive Approach: Increase HEAP_LENGTH to 8 * 32 KiB + Default Allocator
27
28Memory Layout with Increased HEAP_LENGTH:
290x300000000           0x300008000                          0x300040000
30      |                    |                                     |
31      v                    v                                     v
32      [--------------------|------------------------------------|]
33      ^                    ^                                     ^
34      |                    |                                     |
35 VM Lower              VM Upper                         Allocation starts here
36 Boundary              Boundary                         (ACCESS VIOLATION!)
37
38Issue: Access violation occurs without requestHeapFrame, requiring it for every transaction.
39
403. Optimized Solution: Forward Allocation with Flexible Heap Usage
41
42Memory Layout (Same as Naive Approach):
430x300000000           0x300008000                          0x300040000
44      |                    |                                     |
45      v                    v                                     v
46      [--------------------|------------------------------------|]
47      ^                    ^                                     ^
48      |                    |                                     |
49 VM Lower              VM Upper                             Allocator & VM
50 Boundary              Boundary                             Heap Limit
51
52Forward Allocator Behavior:
53
54a) Without requestHeapFrame:
550x300000000           0x300008000
56      |                    |
57      [--------------------]
58      ^                    ^
59      |                    |
60 VM Lower               VM Upper
61 Boundary               Boundary
62 Allocation
63 starts here (SAFE)
64
65b) With requestHeapFrame:
660x300000000           0x300008000                          0x300040000
67      |                    |                                     |
68      [--------------------|------------------------------------|]
69      ^                    ^                                     ^
70      |                    |                                     |
71 VM Lower                  |                                VM Upper
72 Boundary                                                   Boundary
73 Allocation        Allocation continues              Maximum allocation
74 starts here       with requestHeapFrame             with requestHeapFrame
75(SAFE)
76
77Key Advantages:
781. Compatibility: Functions without requestHeapFrame for allocations ≤32 KiB.
792. Extensibility: Supports larger allocations when requestHeapFrame is invoked.
803. Efficiency: Eliminates mandatory requestHeapFrame calls for all transactions.
81
82Conclusion:
83The forward allocation strategy offers a robust solution, providing both backward
84compatibility for smaller heap requirements and the flexibility to utilize extended
85heap space when necessary.
86
87The following allocator is a copy of the bump allocator found in
88solana_program::entrypoint and
89https://github.com/solana-labs/solana-program-library/blob/master/examples/rust/custom-heap/src/entrypoint.rs
90
91but with changes to its HEAP_LENGTH and its
92starting allocation address.
93*/
94
95use solana_program::entrypoint::HEAP_START_ADDRESS;
96use std::{alloc::Layout, mem::size_of, ptr::null_mut};
97
98/// Length of the memory region used for program heap.
99pub const HEAP_LENGTH: usize = 8 * 32 * 1024;
100
101struct BumpAllocator;
102
103unsafe impl std::alloc::GlobalAlloc for BumpAllocator {
104    #[inline]
105    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
106        const POS_PTR: *mut usize = HEAP_START_ADDRESS as *mut usize;
107        const TOP_ADDRESS: usize = HEAP_START_ADDRESS as usize + HEAP_LENGTH;
108        const BOTTOM_ADDRESS: usize = HEAP_START_ADDRESS as usize + size_of::<*mut u8>();
109        let mut pos = *POS_PTR;
110        if pos == 0 {
111            // First time, set starting position to bottom address
112            pos = BOTTOM_ADDRESS;
113        }
114        // Align the position upwards
115        pos = (pos + layout.align() - 1) & !(layout.align() - 1);
116        let next_pos = pos.saturating_add(layout.size());
117        if next_pos > TOP_ADDRESS {
118            return null_mut();
119        }
120        *POS_PTR = next_pos;
121        pos as *mut u8
122    }
123
124    #[inline]
125    unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
126        // I'm a bump allocator, I don't free
127    }
128}
129
130// Only use the allocator if we're not in a no-entrypoint context
131#[cfg(not(feature = "no-entrypoint"))]
132#[global_allocator]
133static A: BumpAllocator = BumpAllocator;