Skip to content

Commit 6d08a77

Browse files
lum1n0usclaude
andcommitted
feat: add POSIX-like aligned_alloc()
- a new API: wasm_runtime_aligned_alloc() - gc_alloc_vo_aligned() in ems-alloc - unit test cases Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 601e8fb commit 6d08a77

File tree

8 files changed

+180
-58
lines changed

8 files changed

+180
-58
lines changed

core/iwasm/common/wasm_memory.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,19 +1073,25 @@ wasm_runtime_malloc(unsigned int size)
10731073
void *
10741074
wasm_runtime_aligned_alloc(unsigned int size, unsigned int alignment)
10751075
{
1076-
if (size == 0) {
1077-
LOG_WARNING("warning: wasm_runtime_aligned_alloc with size zero\n");
1076+
if (alignment == 0) {
1077+
LOG_WARNING(
1078+
"warning: wasm_runtime_aligned_alloc with zero alignment\n");
10781079
return NULL;
10791080
}
10801081

1081-
if (alignment == 0) {
1082-
LOG_WARNING("warning: wasm_runtime_aligned_alloc with zero alignment\n");
1083-
return NULL;
1082+
if (size == 0) {
1083+
LOG_WARNING("warning: wasm_runtime_aligned_alloc with size zero\n");
1084+
/* Allocate at least alignment bytes (smallest multiple of alignment) */
1085+
size = alignment;
1086+
#if BH_ENABLE_GC_VERIFY != 0
1087+
exit(-1);
1088+
#endif
10841089
}
10851090

10861091
#if WASM_ENABLE_FUZZ_TEST != 0
10871092
if (size >= WASM_MEM_ALLOC_MAX_SIZE) {
1088-
LOG_WARNING("warning: wasm_runtime_aligned_alloc with too large size\n");
1093+
LOG_WARNING(
1094+
"warning: wasm_runtime_aligned_alloc with too large size\n");
10891095
return NULL;
10901096
}
10911097
#endif

core/iwasm/include/wasm_export.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,8 @@ wasm_runtime_malloc(unsigned int size);
430430
* Note: Allocated memory cannot be reallocated with wasm_runtime_realloc().
431431
*
432432
* @param size bytes need to allocate (must be multiple of alignment)
433-
* @param alignment alignment requirement (must be power of 2, >= 8, <= page size)
433+
* @param alignment alignment requirement (must be power of 2, >= 8, <= page
434+
* size)
434435
*
435436
* @return the pointer to aligned memory allocated, or NULL on failure
436437
*/

core/shared/mem-alloc/ems/ems_alloc.c

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -556,17 +556,15 @@ alloc_hmu_ex(gc_heap_t *heap, gc_size_t size)
556556
MEM_ALLOC_API_INTER hmu_t *
557557
obj_to_hmu(gc_object_t obj)
558558
{
559-
uint32_t *magic_ptr = (uint32_t *)((char *)obj - 4);
560-
561559
/* Check for aligned allocation magic signature */
562-
if ((*magic_ptr & ALIGNED_ALLOC_MAGIC_MASK) == ALIGNED_ALLOC_MAGIC_VALUE) {
560+
if (gc_is_aligned_allocation(obj)) {
563561
/* This is an aligned allocation, read offset */
564562
uint32_t *offset_ptr = (uint32_t *)((char *)obj - 8);
565563
return (hmu_t *)((char *)obj - *offset_ptr);
566564
}
567565

568566
/* Normal allocation: standard offset */
569-
return (hmu_t *)((gc_uint8 *)(obj) - OBJ_PREFIX_SIZE) - 1;
567+
return (hmu_t *)((gc_uint8 *)(obj)-OBJ_PREFIX_SIZE) - 1;
570568
}
571569

572570
#if BH_ENABLE_GC_VERIFY == 0
@@ -671,7 +669,7 @@ gc_alloc_vo_aligned_internal(void *vheap, gc_size_t size, gc_size_t alignment,
671669
}
672670

673671
if (size > SIZE_MAX - alignment - HMU_SIZE - OBJ_PREFIX_SIZE
674-
- OBJ_SUFFIX_SIZE - 8) {
672+
- OBJ_SUFFIX_SIZE - 8) {
675673
/* Would overflow */
676674
return NULL;
677675
}
@@ -684,8 +682,8 @@ gc_alloc_vo_aligned_internal(void *vheap, gc_size_t size, gc_size_t alignment,
684682
#endif
685683

686684
/* Calculate total size needed */
687-
tot_size_unaligned = HMU_SIZE + OBJ_PREFIX_SIZE + size
688-
+ OBJ_SUFFIX_SIZE + alignment - 1 + 8;
685+
tot_size_unaligned =
686+
HMU_SIZE + OBJ_PREFIX_SIZE + size + OBJ_SUFFIX_SIZE + alignment - 1 + 8;
689687
tot_size = GC_ALIGN_8(tot_size_unaligned);
690688

691689
if (tot_size < size) {
@@ -731,7 +729,8 @@ gc_alloc_vo_aligned_internal(void *vheap, gc_size_t size, gc_size_t alignment,
731729
*((uint32_t *)((char *)ret - 8)) = offset;
732730

733731
/* Store magic with encoded alignment */
734-
*((uint32_t *)((char *)ret - 4)) = ALIGNED_ALLOC_MAGIC_VALUE | alignment_log2;
732+
*((uint32_t *)((char *)ret - 4)) =
733+
ALIGNED_ALLOC_MAGIC_VALUE | alignment_log2;
735734

736735
/* Initialize HMU */
737736
hmu_set_ut(hmu, HMU_VO);
@@ -779,13 +778,10 @@ gc_realloc_vo_internal(void *vheap, void *ptr, gc_size_t size, const char *file,
779778
#endif
780779

781780
/* Check if this is an aligned allocation - not supported */
782-
if (obj_old) {
783-
uint32_t *magic_ptr = (uint32_t *)((char *)obj_old - 4);
784-
if ((*magic_ptr & ALIGNED_ALLOC_MAGIC_MASK) == ALIGNED_ALLOC_MAGIC_VALUE) {
785-
LOG_ERROR("[GC_ERROR]gc_realloc_vo does not support aligned "
786-
"allocations\n");
787-
return NULL;
788-
}
781+
if (gc_is_aligned_allocation(obj_old)) {
782+
LOG_ERROR("[GC_ERROR]gc_realloc_vo does not support aligned "
783+
"allocations\n");
784+
return NULL;
789785
}
790786

791787
if (obj_old) {

core/shared/mem-alloc/ems/ems_gc_internal.h

Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,7 @@ extern "C" {
1515

1616
/* Test visibility macro for internal functions */
1717
#ifndef MEM_ALLOC_API_INTER
18-
#ifdef WAMR_BUILD_TEST
19-
#define MEM_ALLOC_API_INTER
20-
#else
21-
#define MEM_ALLOC_API_INTER static
22-
#endif
18+
#define MEM_ALLOC_API_INTER WASM_RUNTIME_API_INTERN
2319
#endif
2420

2521
/* HMU (heap memory unit) basic block type */
@@ -102,22 +98,120 @@ hmu_verify(void *vheap, hmu_t *hmu);
10298
+ (((x) > 8) ? (x) : 8))
10399

104100
/*
105-
* Aligned allocation uses metadata in the header to store the offset
101+
* ============================================================================
102+
* Aligned Memory Allocation
103+
* ============================================================================
106104
*
107-
* ### Memory Layout
105+
* This module implements aligned memory allocation similar to C11
106+
* aligned_alloc() and POSIX posix_memalign() for WAMR's garbage collector.
108107
*
109-
* Aligned allocations use over-allocation with metadata storage:
108+
* POSIX aligned_alloc() Specification:
109+
* ------------------------------------
110+
* From C11 §7.22.3.1 and POSIX.1-2017:
111+
* void *aligned_alloc(size_t alignment, size_t size);
110112
*
111-
* ```
112-
* [HMU][PREFIX][...padding...][METADATA][ALIGNED_OBJ][SUFFIX]
113-
* ^8 bytes ^returned pointer (aligned)
114-
* ```
113+
* Requirements:
114+
* - alignment: Must be a valid alignment supported by the implementation,
115+
* typically a power of 2
116+
* - size: Must be an integral multiple of alignment
117+
* - Returns: Pointer aligned to the specified alignment boundary, or NULL
118+
* - Memory must be freed with free() (not realloc'd)
119+
* - Behavior: If size is 0, may return NULL or unique pointer (impl-defined)
120+
*
121+
* IMPORTANT: POSIX does not require realloc() to preserve alignment.
122+
* Calling realloc() on aligned_alloc() memory has undefined behavior.
123+
*
124+
* WAMR Implementation Strategy:
125+
* -----------------------------
126+
* We implement alignment through over-allocation with metadata tracking:
127+
*
128+
* 1. **Validation Phase**:
129+
* - Check alignment is power-of-2, >= 8 bytes, <= system page size
130+
* - Check size is multiple of alignment
131+
* - Return NULL if validation fails
132+
*
133+
* 2. **Over-Allocation**:
134+
* - Allocate (size + alignment + metadata_overhead) bytes
135+
* - Extra space allows us to find an aligned boundary within the block
136+
* - Calculate log2(alignment) for efficient offset storage
137+
*
138+
* 3. **Alignment Adjustment**:
139+
* - Find next aligned address within allocated block
140+
* - Calculate offset from original allocation to aligned address
141+
* - Store offset in metadata for later free() operation
142+
*
143+
* 4. **Magic Marker Storage**:
144+
* - Store magic marker (0xA11C0000 | offset) in 4 bytes before user pointer
145+
* - Upper 16 bits: 0xA11C identifies aligned allocation
146+
* - Lower 16 bits: offset from HMU to aligned pointer (max 65535 bytes)
147+
* - This marker prevents unsafe realloc() operations
148+
*
149+
* 5. **Realloc Prevention**:
150+
* - gc_realloc_vo_internal() checks for magic marker
151+
* - Returns NULL if realloc attempted on aligned allocation
152+
* - User must manually allocate new memory and copy data
153+
*
154+
* Memory Layout Diagram:
155+
* ----------------------
156+
*
157+
* Low Address High Address
158+
* ┌─────────────┬──────────┬────────────────┬──────────────┬─────────────┐
159+
* │ HMU Header │ Padding │ Magic + Offset │ Aligned Data │ Padding │
160+
* │ (meta) │ (0-align)│ (4 bytes) │ (size) │ (overhead) │
161+
* └─────────────┴──────────┴────────────────┴──────────────┴─────────────┘
162+
* ▲ ▲
163+
* │ │
164+
* magic_ptr user_ptr (returned, aligned)
115165
*
116-
* Magic value for aligned allocation detection
166+
* Constraints and Limitations:
167+
* ----------------------------
168+
* - Minimum alignment: 8 bytes (GC_MIN_ALIGNMENT)
169+
* - Maximum alignment: System page size (os_getpagesize(), typically 4KB)
170+
* - Maximum offset: 65535 bytes (16-bit storage limit)
171+
* - Realloc support: None - returns NULL (prevents alignment loss)
172+
* - Free support: Full - use mem_allocator_free() / wasm_runtime_free()
173+
* - Thread safety: Protected by LOCK_HEAP/UNLOCK_HEAP
174+
*
175+
* Usage Example:
176+
* --------------
177+
* // Allocate 256 bytes aligned to 64-byte boundary (e.g., for SIMD)
178+
* void *ptr = wasm_runtime_aligned_alloc(256, 64);
179+
* assert((uintptr_t)ptr % 64 == 0); // Guaranteed aligned
180+
*
181+
* // Use the memory...
182+
*
183+
* // Free normally (alignment metadata handled automatically)
184+
* wasm_runtime_free(ptr);
185+
*
186+
* // INVALID: Cannot realloc aligned memory
187+
* void *new_ptr = wasm_runtime_realloc(ptr, 512); // Returns NULL!
117188
*/
189+
190+
/* Aligned allocation magic markers */
118191
#define ALIGNED_ALLOC_MAGIC_MASK 0xFFFF0000
119192
#define ALIGNED_ALLOC_MAGIC_VALUE 0xA11C0000
120193

194+
/**
195+
* Check if a gc_object was allocated with alignment requirements.
196+
*
197+
* Aligned allocations store a magic marker (0xA11C0000) in the 4 bytes
198+
* immediately before the object pointer. This marker is used to identify
199+
* aligned allocations to prevent unsafe realloc operations.
200+
*
201+
* @param obj the gc_object to check (user-visible pointer)
202+
* @return true if obj is an aligned allocation, false otherwise
203+
*/
204+
static inline bool
205+
gc_is_aligned_allocation(gc_object_t obj)
206+
{
207+
if (!obj)
208+
return false;
209+
210+
uint32_t *magic_ptr = (uint32_t *)((char *)obj - 4);
211+
return ((*magic_ptr & ALIGNED_ALLOC_MAGIC_MASK)
212+
== ALIGNED_ALLOC_MAGIC_VALUE);
213+
}
214+
121215
/**
122216
* hmu bit operation
123217
*/

core/shared/mem-alloc/mem_alloc.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,15 @@ mem_allocator_free(mem_allocator_t allocator, void *ptr)
6060
#if BH_ENABLE_GC_VERIFY == 0
6161
void *
6262
mem_allocator_malloc_aligned(mem_allocator_t allocator, uint32_t size,
63-
uint32_t alignment)
63+
uint32_t alignment)
6464
{
6565
return gc_alloc_vo_aligned((gc_handle_t)allocator, size, alignment);
6666
}
6767
#else
6868
void *
69-
mem_allocator_malloc_aligned_internal(mem_allocator_t allocator,
70-
uint32_t size, uint32_t alignment,
71-
const char *file, int line)
69+
mem_allocator_malloc_aligned_internal(mem_allocator_t allocator, uint32_t size,
70+
uint32_t alignment, const char *file,
71+
int line)
7272
{
7373
return gc_alloc_vo_aligned_internal((gc_handle_t)allocator, size, alignment,
7474
file, line);

core/shared/mem-alloc/mem_alloc.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,27 +48,27 @@ mem_allocator_free(mem_allocator_t allocator, void *ptr);
4848

4949
/* Aligned allocation support */
5050
#ifndef GC_MIN_ALIGNMENT
51-
#define GC_MIN_ALIGNMENT 8
51+
#define GC_MIN_ALIGNMENT 8
5252
#endif
5353

5454
#if BH_ENABLE_GC_VERIFY == 0
5555

5656
void *
5757
mem_allocator_malloc_aligned(mem_allocator_t allocator, uint32_t size,
58-
uint32_t alignment);
58+
uint32_t alignment);
5959

6060
#define mem_allocator_malloc_aligned_internal(allocator, size, alignment, \
61-
file, line) \
61+
file, line) \
6262
mem_allocator_malloc_aligned(allocator, size, alignment)
6363

6464
#else /* BH_ENABLE_GC_VERIFY != 0 */
6565

6666
void *
67-
mem_allocator_malloc_aligned_internal(mem_allocator_t allocator,
68-
uint32_t size, uint32_t alignment,
69-
const char *file, int line);
67+
mem_allocator_malloc_aligned_internal(mem_allocator_t allocator, uint32_t size,
68+
uint32_t alignment, const char *file,
69+
int line);
7070

71-
#define mem_allocator_malloc_aligned(allocator, size, alignment) \
71+
#define mem_allocator_malloc_aligned(allocator, size, alignment) \
7272
mem_allocator_malloc_aligned_internal(allocator, size, alignment, \
7373
__FILE__, __LINE__)
7474

core/shared/utils/bh_platform.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,24 @@
1818
#include "bh_vector.h"
1919
#include "runtime_timer.h"
2020

21+
/**
22+
* API visibility macros for WAMR internal functions
23+
*
24+
* WASM_RUNTIME_API_EXTERN - Public exported APIs (defined in wasm_export.h)
25+
* WASM_RUNTIME_API_INTERN - Internal APIs visible across WAMR components
26+
*
27+
* In test builds (WAMR_BUILD_TEST=1), internal APIs are exposed for unit
28+
* testing. In production builds, internal APIs are static (file-scoped) for
29+
* encapsulation.
30+
*/
31+
#ifndef WASM_RUNTIME_API_INTERN
32+
#ifdef WAMR_BUILD_TEST
33+
#define WASM_RUNTIME_API_INTERN
34+
#else
35+
#define WASM_RUNTIME_API_INTERN static
36+
#endif
37+
#endif
38+
2139
/**
2240
* WA_MALLOC/WA_FREE need to be redefined for both
2341
* runtime native and WASM app respectively.

0 commit comments

Comments
 (0)