Added cwasm_arena.c and cwasm_debug.c. Arena API hasn't been tested well yet. There's also something wrong with our DebugOutputLineBuffer usage for splitting lines to multiple Javascript output calls. Also added an int level to jsStdPrint and a bunch of macros to cwasm.h

This commit is contained in:
2025-08-31 17:33:48 -07:00
parent d0aa7a1d0e
commit cca61ea163
9 changed files with 566 additions and 11 deletions

182
cwasm_arena.c Normal file
View File

@@ -0,0 +1,182 @@
/*
File: cwasm_arena.c
Author: Taylor Robbins
Date: 08\28\2025
Description:
** Defines a basic memory Arena structure and API
** Also defines a few global memory arenas: MainWasmMemory and 2x ScratchArenas
*/
// +--------------------------------------------------------------+
// | Arena Types |
// +--------------------------------------------------------------+
typedef enum ArenaFlag ArenaFlag;
enum ArenaFlag
{
ArenaFlag_None = 0x00000000,
ArenaFlag_Debug = 0x00000001,
ArenaFlag_IsMainWasmMemory = 0x00000002,
ArenaFlag_DefaultAligned4 = 0x00000004,
ArenaFlag_DefaultAligned8 = 0x00000008,
ArenaFlag_DefaultAligned16 = 0x00000010,
ArenaFlag_IsScratch = 0x00000020,
ArenaFlag_All = 0x0000003F,
ArenaFlag_Count = 6,
};
typedef struct Arena Arena;
struct Arena
{
u32 flags; //ArenaFlags
void* base;
u32 size;
u32 used;
};
typedef struct ArenaMark ArenaMark;
struct ArenaMark
{
Arena* arena;
u32 mark;
};
// +--------------------------------------------------------------+
// | Global Arenas |
// +--------------------------------------------------------------+
static Arena MainWasmMemory = ZEROED;
static Arena ScratchArenas[2] = ZEROED;
// +--------------------------------------------------------------+
// | Arena API |
// +--------------------------------------------------------------+
__attribute__((always_inline)) Arena NewArena(u32 flags, u32 size, void* basePntr)
{
Arena result = ZEROED;
result.flags = flags;
result.size = size;
result.base = basePntr;
return result;
}
static inline ArenaMark GetArenaMark(Arena* arena)
{
ArenaMark result = ZEROED;
result.arena = arena;
result.mark = arena->used;
return result;
}
static inline void ResetToMark(Arena* arena, u32 mark)
{
arena->used = mark;
}
static inline void ResetToArenaMark(ArenaMark arenaMark) { ResetToMark(arenaMark.arena, arenaMark.mark); }
void* AllocMemAligned(Arena* arena, u32 numBytes, u32 alignment)
{
Assert(arena != nullptr && arena->base != nullptr);
u32 realAlignment = alignment;
if (realAlignment == UINT32_MAX) { realAlignment = 0; }
else if (realAlignment == 0 && (arena->flags & ArenaFlag_DefaultAligned16) != 0) { realAlignment = 16; }
else if (realAlignment == 0 && (arena->flags & ArenaFlag_DefaultAligned8) != 0) { realAlignment = 8; }
else if (realAlignment == 0 && (arena->flags & ArenaFlag_DefaultAligned4) != 0) { realAlignment = 4; }
u32 alignOffset = AlignOffset((u8*)arena->base + arena->used, realAlignment);
u32 newSize = arena->used + alignOffset + numBytes;
if (newSize > arena->size)
{
if ((arena->flags & ArenaFlag_IsMainWasmMemory) != 0)
{
void* newMemory = grow_mem(newSize - arena->size);
Assert(newMemory == (u8*)arena->base + arena->size);
arena->size = newSize;
}
else { return nullptr; }
}
void* result = (u8*)arena->base + arena->used;
arena->used += alignOffset + numBytes;
return result;
}
static inline void* AllocMemUnaligned(Arena* arena, u32 numBytes) { return AllocMemAligned(arena, numBytes, UINT32_MAX); }
static inline void* AllocMem(Arena* arena, u32 numBytes) { return AllocMemAligned(arena, numBytes, 0); }
static inline Arena* AllocArena(Arena* sourceArena, u32 newArenaFlags, u32 newArenaSize, u32 newArenaAlignment)
{
//TODO: Do we need to make sure the Arena structure is aligned to it's own struct alignment?
#if LANGUAGE_IS_C
u32 arenaStructAlignment = (u32)_Alignof(Arena);
#else
u32 arenaStructAlignment = (u32)std::alignment_of<Arena>();
#endif
u32 offsetAfterStruct = AlignOffset(sizeof(Arena), newArenaAlignment);
Arena* result = (Arena*)AllocMemAligned(sourceArena, sizeof(Arena) + offsetAfterStruct + newArenaSize, (arenaStructAlignment >= newArenaAlignment) ? arenaStructAlignment : newArenaAlignment);
if (result == nullptr) { return nullptr; }
*result = NewArena(newArenaFlags, newArenaSize, ((u8*)result) + sizeof(Arena) + offsetAfterStruct);
return result;
}
#if LANGUAGE_IS_C
#define AllocStruct(type, arenaPntr) (type*)AllocMemAligned((arenaPntr), sizeof(type), (u32)_Alignof(type))
#define AllocArray(type, arenaPntr, count) (type*)AllocMemAligned((arenaPntr), sizeof(type) * (count), (u32)_Alignof(type))
#else
#define AllocStruct(type, arenaPntr) (type*)AllocMemAligned((arenaPntr), sizeof(type), (u32)std::alignment_of<type>())
#define AllocArray(type, arenaPntr, count) (type*)AllocMemAligned((arenaPntr), sizeof(type) * (count), (u32)std::alignment_of<type>())
#endif
void* ReallocMemAligned(Arena* arena, void* oldPntr, u32 oldSize, u32 newSize, u32 newAlignment)
{
if (oldSize == newSize) { return oldPntr; }
if (oldPntr == nullptr) { return AllocMemAligned(arena, newSize, newAlignment); }
Assert(arena != nullptr && arena->base != nullptr);
u32 realAlignment = newAlignment;
if (realAlignment == UINT32_MAX) { realAlignment = 0; }
else if (realAlignment == 0 && (arena->flags & ArenaFlag_DefaultAligned16) != 0) { realAlignment = 16; }
else if (realAlignment == 0 && (arena->flags & ArenaFlag_DefaultAligned8) != 0) { realAlignment = 8; }
else if (realAlignment == 0 && (arena->flags & ArenaFlag_DefaultAligned4) != 0) { realAlignment = 4; }
if ((u8*)oldPntr + oldSize == (u8*)arena->base + arena->used && IsAlignedTo(oldPntr, realAlignment))
{
if (newSize > oldSize)
{
void* newSpace = AllocMemUnaligned(arena, newSize - oldSize);
if (newSpace == nullptr) { return nullptr; }
Assert(newSpace == (u8*)oldPntr + oldSize);
}
else { arena->used -= (oldSize - newSize); }
return oldPntr;
}
else
{
void* newPntr = AllocMemAligned(arena, newSize, newAlignment);
if (oldSize > 0 && newPntr != nullptr) { memcpy(newPntr, oldPntr, oldSize); }
return newPntr;
}
}
static inline void* ReallocMemUnaligned(Arena* arena, void* oldPntr, u32 oldSize, u32 newSize) { return ReallocMemAligned(arena, oldPntr, oldSize, newSize, UINT32_MAX); }
static inline void* ReallocMem(Arena* arena, void* oldPntr, u32 oldSize, u32 newSize) { return ReallocMemAligned(arena, oldPntr, oldSize, newSize, 0); }
void InitGlobalArenas(u32 scratchArenasSize)
{
u32 mainInitialSize = 1024;
void* mainPntr = grow_mem(mainInitialSize);
MainWasmMemory = NewArena(ArenaFlag_IsMainWasmMemory|ArenaFlag_DefaultAligned16, mainInitialSize, mainPntr);
for (u32 aIndex = 0; aIndex < ArrayCount(ScratchArenas); aIndex++)
{
void* scratchMemory = AllocMem(&MainWasmMemory, scratchArenasSize);
ScratchArenas[aIndex] = NewArena(ArenaFlag_IsScratch, scratchArenasSize, scratchMemory);
}
}
// +==============================+
// | Scratch API |
// +==============================+
ArenaMark GetScratch1(Arena* conflictArena)
{
return GetArenaMark((conflictArena == &ScratchArenas[0]) ? &ScratchArenas[1] : &ScratchArenas[0]);
}
static inline ArenaMark GetScratch() { return GetScratch1(nullptr); }
#define ScratchBegin1(scratchName, conflictArenaPntr) ArenaMark scratchName##Mark = GetScratch1(conflictArenaPntr); Arena* scratchName = scratchName##Mark.arena
#define ScratchBegin(scratchName) ScratchBegin(scratchName, nullptr)
#define ScratchEnd(scratchName) ResetToArenaMark(scratchName##Mark)