Source tree reorganization: - Move StarForth v3 engine to v3/ (src/, include/, Makefile) - Move kernel to kernel/ (src/, include/, linker/, Makefile) - Create v4/ skeleton for F18-ISA golden model (DECOMPOSITION.md, JUSTIFICATION.md) - Move FABRIC-0..4.md to docs/fabric/ - Move ONTOLOGY.md and ROADMAP.md to docs/ Board infrastructure: - Add boards/ser5/, boards/raspi/, boards/milkv/, boards/zynq7020/ - Each board has board.mk (ISA, CPU flags, boot recipe) and README.md - Root Makefile becomes thin dispatcher: boot_image, all, clean, docs take TARGET - make boot_image TARGET=SER5|RASPI|MILKV builds one GPT/MBR image per board - ZYNQ7020 target exists but stops with clear error (ARMv7 port not built yet) - scripts/mkdiskimage.sh builds disk images for all boards Docs pipeline: - docs/book/ with LaTeX master (main.tex) and Makefile - pandoc converts Markdown to LaTeX at build time - Two Lua filters: table-widths.lua (wide tables wrap), code-breaks.lua (inline code breaks) - make docs builds single PDF (754 pages, 0 missing characters) - make docs TARGET=<board> adds board appendix - build/docs/<book|board>/meta.tex stamps git commit into PDF Bug fixes: - 42 include paths that only worked by accident now use correct relative paths - clang-18 hardcode replaced with configurable CC variable (fixed aarch64 build) - Pi 5: kernel_2712.img linked at 0x80000, .bss zeroed, memory reserved - Doxyfile, .clang-tidy, README.md, Kconfig paths updated Verified: - Hosted v3 build passes 1012 tests, 0 failures - SER5 image boots in QEMU (OVMF), POST passes, K exact (65536 = Q48_ONE) - Milk-V image boots in QEMU (OpenSBI + U-Boot + bootefi), POST passes - make clean TARGET=<board> removes only that board and its ISA objects - make all builds all boards, hosted v3, and docs in one run Co-authored-by: Junie <junie@jetbrains.com>
165 lines
5.0 KiB
C
165 lines
5.0 KiB
C
/*
|
||
StarForth — Steady-State Virtual Machine Runtime
|
||
|
||
Copyright (c) 2023–2025 Robert A. James
|
||
All rights reserved.
|
||
|
||
This file is part of the StarForth project.
|
||
|
||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
|
||
You may obtain a copy of the License at:
|
||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||
|
||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
express or implied, including but not limited to the warranties of
|
||
merchantability, fitness for a particular purpose, and noninfringement.
|
||
|
||
See the License for the specific language governing permissions and
|
||
limitations under the License.
|
||
*/
|
||
|
||
/*
|
||
* platform/alloc_host.c - Host (Linux/POSIX) memory allocator
|
||
*
|
||
* Simple wrapper around standard malloc/free with statistics tracking.
|
||
*/
|
||
|
||
#include "../../include/platform_alloc.h"
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
/* Statistics tracking */
|
||
static sf_alloc_stats_t g_stats = {0};
|
||
|
||
/**
|
||
* @brief Initialise the host allocator statistics.
|
||
*
|
||
* Resets the @c g_stats struct to zero. Safe to call multiple times;
|
||
* subsequent calls simply reset the counters. No heap memory is allocated
|
||
* by this function — the underlying allocator is the system @c malloc.
|
||
*
|
||
* @return 0 always (cannot fail on the hosted path)
|
||
*/
|
||
int sf_alloc_init(void)
|
||
{
|
||
memset(&g_stats, 0, sizeof(g_stats));
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief Allocate @c size bytes from the system heap.
|
||
*
|
||
* Delegates to @c malloc(). On success, increments @c g_stats.used_bytes
|
||
* and @c g_stats.alloc_count, and updates @c g_stats.peak_bytes if the
|
||
* new used total exceeds the recorded peak.
|
||
*
|
||
* @param size Number of bytes to allocate; 0 returns NULL
|
||
* @return Pointer to allocated memory, or NULL on allocation failure or zero size
|
||
*/
|
||
void* sf_malloc(size_t size)
|
||
{
|
||
if (size == 0) return NULL;
|
||
|
||
void* ptr = malloc(size);
|
||
if (ptr)
|
||
{
|
||
g_stats.used_bytes += size;
|
||
g_stats.alloc_count++;
|
||
if (g_stats.used_bytes > g_stats.peak_bytes)
|
||
{
|
||
g_stats.peak_bytes = g_stats.used_bytes;
|
||
}
|
||
}
|
||
return ptr;
|
||
}
|
||
|
||
/**
|
||
* @brief Allocate a zero-initialised array of @c count elements of @c size bytes each.
|
||
*
|
||
* Delegates to @c calloc(). Updates statistics the same way as @c sf_malloc():
|
||
* adds @c count*size to @c used_bytes, increments @c alloc_count, and updates
|
||
* @c peak_bytes.
|
||
*
|
||
* @param count Number of elements; 0 returns NULL
|
||
* @param size Size of each element in bytes; 0 returns NULL
|
||
* @return Pointer to zero-filled memory, or NULL on allocation failure or zero arguments
|
||
*/
|
||
void* sf_calloc(size_t count, size_t size)
|
||
{
|
||
if (count == 0 || size == 0) return NULL;
|
||
|
||
void* ptr = calloc(count, size);
|
||
if (ptr)
|
||
{
|
||
size_t total = count * size;
|
||
g_stats.used_bytes += total;
|
||
g_stats.alloc_count++;
|
||
if (g_stats.used_bytes > g_stats.peak_bytes)
|
||
{
|
||
g_stats.peak_bytes = g_stats.used_bytes;
|
||
}
|
||
}
|
||
return ptr;
|
||
}
|
||
|
||
/**
|
||
* @brief Resize a previously allocated block to @c new_size bytes.
|
||
*
|
||
* Delegates to @c realloc(). On success, increments @c g_stats.alloc_count.
|
||
* The exact byte delta is not tracked because the original allocation size is
|
||
* not stored alongside the pointer — @c used_bytes is therefore approximate
|
||
* after any @c sf_realloc() call. For accurate accounting, wrap allocations
|
||
* with size-tracking headers.
|
||
*
|
||
* @param ptr Pointer to an existing allocation, or NULL (behaves like @c sf_malloc)
|
||
* @param new_size Desired new size in bytes; 0 may free the block (platform-defined)
|
||
* @return Pointer to resized memory, or NULL on failure (original block unchanged)
|
||
*/
|
||
void* sf_realloc(void* ptr, size_t new_size)
|
||
{
|
||
void* new_ptr = realloc(ptr, new_size);
|
||
if (new_ptr)
|
||
{
|
||
/* Note: Can't track exact delta without size tracking */
|
||
g_stats.alloc_count++;
|
||
}
|
||
return new_ptr;
|
||
}
|
||
|
||
/**
|
||
* @brief Release a previously allocated block back to the system heap.
|
||
*
|
||
* Delegates to @c free(). Increments @c g_stats.free_count on non-NULL @c ptr.
|
||
* The freed byte count is not subtracted from @c used_bytes because the
|
||
* original size is not tracked — see @c sf_realloc() note.
|
||
*
|
||
* @param ptr Pointer to free; NULL is a safe no-op
|
||
*/
|
||
void sf_free(void* ptr)
|
||
{
|
||
if (!ptr) return;
|
||
|
||
/* Note: We can't track exact bytes freed without size tracking.
|
||
* For accurate stats, would need to wrap allocations with headers.
|
||
* For now, just count frees. */
|
||
g_stats.free_count++;
|
||
free(ptr);
|
||
}
|
||
|
||
/**
|
||
* @brief Copy the current allocator statistics into @c stats.
|
||
*
|
||
* Fills @c *stats with a snapshot of @c g_stats. Note that @c used_bytes
|
||
* overstates the live total when blocks have been freed or reallocated,
|
||
* because freed sizes are not tracked. @c free_count is accurate.
|
||
*
|
||
* @param stats Output buffer to populate; no-op if NULL
|
||
*/
|
||
void sf_alloc_get_stats(sf_alloc_stats_t* stats)
|
||
{
|
||
if (!stats) return;
|
||
*stats = g_stats;
|
||
}
|