Commit 005854e

kravantokh <kravantokh@noreply.codeberg.org>
2026-07-16 16:44:27
Start arena implementation. Some more platform abstractions.
1 parent deb3606
memory/linux/memory.h
@@ -0,0 +1,54 @@
+#ifndef NOTLIBC_HEADER_MEMORY_LINUX_MEMORY_INCLUDED
+#define NOTLIBC_HEADER_MEMORY_LINUX_MEMORY_INCLUDED
+
+#include "../../platform/platform.h"
+
+Status mem_claim( u32 page_count, Page **ptr_out) {
+	void* res = (void*)linux_mmap( 0, page_count*sizeof(Page), LINUX_PROT_NONE, MMAP_MAP_PRIVATE | MMAP_MAP_ANONYMOUS, -1, 0 );
+
+	if( LINUX_SYSCALL_ERROR(res) )
+		return S_FAILURE | ERR_MEM_MAP_FAILURE;
+
+	*ptr_out = res;
+	return S_SUCCESS;
+}
+
+Status mem_relinquish( Page *start, u32 page_count ) {
+	usize res = linux_munmap( (usize)start, page_count*sizeof(Page) );
+
+	if( LINUX_SYSCALL_ERROR(res) ) {
+		return S_FAILURE | ERR_PLATFORM | ERR_MEM_UNMAP_FAILURE;
+	}
+
+	return S_SUCCESS;
+}
+
+Status mem_occupy( Page *start, u32 page_count ) {
+	usize res = linux_mprotect( (usize)start, page_count*sizeof(Page), LINUX_PROT_READ | LINUX_PROT_WRITE );
+
+	if( LINUX_SYSCALL_ERROR(res) ) {
+		return S_FAILURE | ERR_PLATFORM | ERR_MEM_SETPERM_FAILURE;
+	}
+
+	return S_SUCCESS;
+}
+
+Status mem_vacate( Page *start, u32 page_count ) {
+	usize amount = page_count*sizeof(Page);
+
+	usize res = linux_mprotect( (usize)start, amount, LINUX_PROT_NONE );
+
+	if( LINUX_SYSCALL_ERROR(res) ) {
+		return S_FAILURE | ERR_PLATFORM | ERR_MEM_SETPERM_FAILURE;
+	}
+
+	res = linux_madvise( (usize)start, amount, MADV_DONTNEED);
+
+	if( LINUX_SYSCALL_ERROR(res) ) {
+		return S_FAILURE | ERR_PLATFORM | ERR_MEM_SETHINT_FAILURE;
+	}
+
+	return S_SUCCESS;
+}
+
+#endif
memory/arena.h
@@ -0,0 +1,77 @@
+#ifndef NOTLIBC_HEADER_MEMORY_ARENA_INCLUDED
+#define NOTLIBC_HEADER_MEMORY_ARENA_INCLUDED
+
+#include "../platform/platform.h"
+#include "../util/mathops.h"
+#include "./memory.h"
+
+STRUCT( Arena,
+	Page *start;  /* The address where the arena starts */
+	usize claimed;  /* Total size of the arena */
+	usize occupied; /* The actually occupied (backed) range */
+	usize taken;    /* The top of the arena */
+)
+
+STRUCT( HeapEntry,
+	DEBUG( usize checksum; ) /* for detecting heap corruption and bad pointers */
+	usize size;
+	struct _HeapEntry *next;
+	u8 data[]; /* The above pointer should gives a sane alignment here despite this being u8 */
+)
+
+STRUCT( Heap,
+	Arena arena; /* Heap is built on top of arena. It's just an arena + a freelist with metadata */
+	HeapEntry *freelist;
+)
+
+Status Arena_create( Arena *out, usize size );
+Status Arena_destroy( Arena *a );
+
+Status Arena_take( Arena *a, usize amount, u8 **out );
+Status Arena_give( Arena *a, usize amount );
+Status Arena_align( Arena *a, usize alignment );
+
+
+IMPL(
+
+	Status Arena_create( Arena *out, usize size ) {
+		TRY( u64_round_up( size, 0x1000, &size ) );
+		TRY( mem_claim( size/0x1000, &out->start ) );
+
+		out->claimed = size;
+		out->occupied = 0;
+		out->taken = 0;
+
+		return S_SUCCESS;
+	}
+
+	Status Arena_destroy( Arena *a ) {
+		TRY( mem_relinquish( a->start, a->claimed ) );
+
+		mem_set( a, sizeof(*a), 0 );
+		return S_SUCCESS;
+	}
+
+	Status Arena_align( Arena *a, usize alignment ) {
+		u64 take_amount = 0;
+		TRY( u64_round_up( a->taken, alignment, &take_amount ) );
+		take_amount -= a->taken;
+		
+		if( take_amount != 0 ) {
+			u8* discard;
+			TRY( Arena_take( a, take_amount, &discard ) );
+		}
+
+		return S_SUCCESS;
+	}
+
+
+	Status Arena_give( Arena *a, usize amount ) {
+		if( amount > a->taken ) return S_FAILURE | ERR_OUT_OF_BOUND;
+		a->taken -= amount;
+		return S_SUCCESS;
+	}
+
+)
+
+#endif
memory/memory.h
@@ -0,0 +1,51 @@
+#ifndef NOTLIBC_HEADER_MEMORY_MEMORY_INCLUDED
+#define NOTLIBC_HEADER_MEMORY_MEMORY_INCLUDED
+
+#include "../platform/platform.h"
+
+#define PLATFORM_MEMORY_HEADER \
+	LINUX("./linux/memory.h") \
+	WINDOWS("./windows/memory.h")
+
+#include PLATFORM_MEMORY_HEADER
+
+/* Memory units */
+
+#define KiB(n) ((u64)(n) << 10)
+#define MiB(n) ((u64)(n) << 20)
+#define GiB(n) ((u64)(n) << 30)
+#define TiB(n) ((u64)(n) << 40)
+
+static inline void* mem_copy( void *source, void *destination, u64 len ) {
+	u8 *src = source;
+	u8 *dst = destination;
+	u64 i = 0;
+
+	for( ; i < len; ++i ) {
+		dst[i] = src[i];
+	}
+
+	return &dst[i];
+}
+
+static inline void* mem_set( void *start, u64 len, u8 value ) {
+	assert(start != 0);
+	assert(len != 0);
+	u8 *s = start;
+	u64 i = 0;
+
+	for( ; i < len; ++i ) {
+		s[i] = value;
+	}
+
+	return &s[len];
+}
+
+
+Status mem_claim( u32 page_count, Page **ptr_out); /* Request virtual memory range */
+Status mem_relinquish( Page *start, u32 page_count ); /* Free virtual memory range */
+
+Status mem_occupy( Page *start, u32 page_count ); /* Set up physical backing */
+Status mem_vacate( Page *start, u32 page_count ); /* Allow removal of physical backing */
+
+#endif
platform/common/build.h
@@ -30,6 +30,8 @@
 
 #ifdef NOTLIBC_IMPLEMENT
 #define IMPL(...) __VA_ARGS__
+#else
+#define IMPL(...)
 #endif
 
 #endif
platform/linux/corefunc.h
@@ -16,6 +16,6 @@ static inline void terminate_forced(void) {
 #include "./syscalls.h"
 
 static inline void terminate(u32 code) {
-	linux_exit(*(i32*)&code);
+	linux_exit((s32)code);
 	UNREACHABLE;
 }
platform/linux/coretypes.h
@@ -8,21 +8,31 @@ typedef unsigned short     u16;
 typedef unsigned int       u32;
 typedef unsigned long long u64;
 
-typedef signed char      i8;
-typedef signed short     i16;
-typedef signed int       i32;
-typedef signed long long i64;
+typedef signed char      s8;
+typedef signed short     s16;
+typedef signed int       s32;
+typedef signed long long s64;
 
 typedef float  f32;
 typedef double f64;
 
 X86_64(
 	typedef u64 usize;
+	typedef s64 isize;
+
+	typedef struct _Page {
+		u8 data[4096];
+	} Page;
 )
 
 
 I386(
 	typedef u32 usize;
+	typedef s32 isize;
+
+	typedef struct _Page {
+		u8 data[4096];
+	} Page;
 )
 
 #endif
platform/linux/syscall_wrappers_gcclike.h
@@ -6,7 +6,7 @@
 X86_64(
 
 	/* All this mess, just so *maybe* the compiler can optimize register usage when inlining. Check the generic wrappers for a more typical way of going about this. This here is unusual, from what I have seen. */
-	static inline usize linux_syscall1(usize num, usize arg1) {
+	static inline isize linux_syscall1(usize num, usize arg1) {
 			register usize ret __asm__("rax") = num;
 
 			register usize a1 __asm__("rdi") = arg1;
@@ -19,7 +19,7 @@ X86_64(
 			);
 		return ret;
 	}
-	static inline usize linux_syscall2(usize num, usize arg1, usize arg2) {
+	static inline isize linux_syscall2(usize num, usize arg1, usize arg2) {
 			register usize ret __asm__("rax") = num;
 
 			register usize a1 __asm__("rdi") = arg1;
@@ -33,7 +33,7 @@ X86_64(
 			);
 		return ret;
 	}
-	static inline usize linux_syscall3(usize num, usize arg1, usize arg2, usize arg3) {
+	static inline isize linux_syscall3(usize num, usize arg1, usize arg2, usize arg3) {
 			register usize ret __asm__("rax") = num;
 
 			register usize a1 __asm__("rdi") = arg1;
@@ -48,7 +48,7 @@ X86_64(
 			);
 		return ret;
 	}
-	static inline usize linux_syscall4(usize num, usize arg1, usize arg2, usize arg3, usize arg4) {
+	static inline isize linux_syscall4(usize num, usize arg1, usize arg2, usize arg3, usize arg4) {
 			register usize ret __asm__("rax") = num;
 
 			register usize a1 __asm__("rdi") = arg1;
@@ -65,7 +65,7 @@ X86_64(
 		return ret;
 	}
 
-	static inline usize linux_syscall5(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5) {
+	static inline isize linux_syscall5(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5) {
 			register usize ret __asm__("rax") = num;
 
 			register usize a1 __asm__("rdi") = arg1;
@@ -83,7 +83,7 @@ X86_64(
 		return ret;
 	}
 
-	static inline usize linux_syscall6(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5, usize arg6) {
+	static inline isize linux_syscall6(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5, usize arg6) {
 			register usize ret __asm__("rax") = num;
 
 			register usize a1 __asm__("rdi") = arg1;
@@ -103,7 +103,6 @@ X86_64(
 	}
 )
 
-
 I386(
 	ERROR("Linux syscalls not implemented for I386.");
 )
platform/linux/syscall_wrappers_generic.h
@@ -6,7 +6,7 @@
 
 X86_64(
 
-	static inline usize linux_syscall1(usize num, usize arg1) {
+	static inline isize linux_syscall1(usize num, usize arg1) {
 		__asm__ volatile (
 			"mov %[a1], %%rdi\n"
 			"syscall\n"
@@ -17,7 +17,7 @@ X86_64(
 		return num;
 	}
 
-	static inline usize linux_syscall2(usize num, usize arg1, usize arg2) {
+	static inline isize linux_syscall2(usize num, usize arg1, usize arg2) {
 		__asm__ volatile (
 			"mov %[a1], %%rdi\n"
 			"mov %[a2], %%rsi\n"
@@ -28,7 +28,7 @@ X86_64(
 		);
 		return num;
 	}
-	static inline usize linux_syscall3(usize num, usize arg1, usize arg2, usize arg3) {
+	static inline isize linux_syscall3(usize num, usize arg1, usize arg2, usize arg3) {
 		__asm__ volatile (
 			"mov %[a1], %%rdi\n"
 			"mov %[a2], %%rsi\n"
@@ -40,7 +40,7 @@ X86_64(
 		);
 		return num;
 	}
-	static inline usize linux_syscall4(usize num, usize arg1, usize arg2, usize arg3, usize arg4) {
+	static inline isize linux_syscall4(usize num, usize arg1, usize arg2, usize arg3, usize arg4) {
 		__asm__ volatile (
 			"mov %[a1], %%rdi\n"
 			"mov %[a2], %%rsi\n"
@@ -53,13 +53,14 @@ X86_64(
 		);
 		return num;
 	}
-	static inline usize linux_syscall5(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5) {
+	static inline isize linux_syscall5(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5) {
 		__asm__ volatile (
 			"mov %[a1], %%rdi\n"
 			"mov %[a2], %%rsi\n"
 			"mov %[a3], %%rdx\n"
 			"mov %[a4], %%r10\n"
 			"mov %[a5], %%r8\n"
+			"syscall\n"
 			: [num]"+a"(num)
 			: [a1]"r"(arg1), [a2]"r"(arg2), [a3]"r"(arg3), [a4]"r"(arg4), [a5]"r"(arg5)
 			: "rcx", "r11", "memory"
@@ -67,7 +68,7 @@ X86_64(
 		return num;
 	}
 
-	static inline usize linux_syscall6(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5, usize arg6) {
+	static inline isize linux_syscall6(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5, usize arg6) {
 		__asm__ volatile (
 			"mov %[a1], %%rdi\n"
 			"mov %[a2], %%rsi\n"
platform/linux/syscalls.h
@@ -5,12 +5,12 @@
 #include "../common/compiler.h"
 #include "./coretypes.h"
 
-static inline usize linux_syscall1(usize num, usize arg1);
-static inline usize linux_syscall2(usize num, usize arg1, usize arg2);
-static inline usize linux_syscall3(usize num, usize arg1, usize arg2, usize arg3);
-static inline usize linux_syscall4(usize num, usize arg1, usize arg2, usize arg3, usize arg4);
-static inline usize linux_syscall5(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5);
-static inline usize linux_syscall6(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5, usize arg6);
+static inline isize linux_syscall1(usize num, usize arg1);
+static inline isize linux_syscall2(usize num, usize arg1, usize arg2);
+static inline isize linux_syscall3(usize num, usize arg1, usize arg2, usize arg3);
+static inline isize linux_syscall4(usize num, usize arg1, usize arg2, usize arg3, usize arg4);
+static inline isize linux_syscall5(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5);
+static inline isize linux_syscall6(usize num, usize arg1, usize arg2, usize arg3, usize arg4, usize arg5, usize arg6);
 
 #if COMPILER == COMPILER_GCC || COMPILER == COMPILER_CLANG
 	#include "./syscall_wrappers_gcclike.h"
@@ -18,28 +18,85 @@ static inline usize linux_syscall6(usize num, usize arg1, usize arg2, usize arg3
 	#include "./syscall_wrappers_generic.h"
 #endif
 
+#define LINUX_SYSCALL_ERROR_START ((usize)-4095)
+#define LINUX_SYSCALL_ERROR(value) ((usize)value >= LINUX_SYSCALL_ERROR_START)
 
-
-static inline usize linux_read( u32 fd, u8* buf, usize count ) {
+static inline isize linux_read( u32 fd, u8* buf, usize count ) {
 	return linux_syscall3( 0, fd, (usize)buf, count );
 }
 
-static inline usize linux_write( u32 fd, const u8* buf, usize count ) {
+static inline isize linux_write( u32 fd, const u8* buf, usize count ) {
 	return linux_syscall3( 1, fd, (usize)buf, count );
 }
 
-static inline usize linux_open( u8* path, i32 flags, i32 mode ) {
-	return linux_syscall3( 2, (usize)path, *(u32*)&flags, *(u32*)&mode);
+static inline isize linux_open( const u8* path, s32 flags, s32 mode ) {
+	return linux_syscall3( 2, (usize)path, (usize)flags, (usize)&mode);
 }
 
-static inline usize linux_close( u32 fd ) {
+static inline isize linux_close( u32 fd ) {
 	return linux_syscall1( 3, fd );
 }
 
-static inline void linux_exit( i32 exitcode ) {
+static inline void linux_exit( s32 exitcode ) {
 	(void)linux_syscall1( 60, exitcode );
 	UNREACHABLE;
 }
 
+#define LINUX_PROT_NONE (0x0)
+#define LINUX_PROT_READ (0x1)
+#define LINUX_PROT_WRITE (0x2)
+#define LINUX_PROT_EXEC (0x4)
+
+#define MMAP_MAP_SHARED 0x1 
+#define MMAP_MAP_PRIVATE 0x2
+#define MMAP_MAP_SHARED_VALIDATE 0x3
+#define MMAP_MAP_SHARED_DROPPABLE 0x08
+#define MMAP_MAP_ANONYMOUS 0x20
+
+#define MMAP_MAP_FAILED ((void *)-1)
+
+static inline isize linux_mmap( usize addr, usize len, usize prot, usize flags, usize fd, usize offset ) {
+	return linux_syscall6( 9, addr, len, prot, flags, fd, offset );
+}
+
+static inline isize linux_munmap( usize start, usize len ) {
+	return linux_syscall2( 11, start, len );
+}
+
+static inline isize linux_mprotect( usize start, usize len, usize prot ) {
+	return linux_syscall3( 11, start, len, prot );
+}
+
+
+#define MADV_NORMAL 0
+#define MADV_RANDOM 1
+#define MADV_SEQUENTIAL 2
+#define MADV_WILLNEED 3
+#define MADV_DONTNEED 4
+#define MADV_FREE 8
+#define MADV_REMOVE 9
+#define MADV_DONTFORK 10
+#define MADV_DOFORK 11
+#define MADV_MERGEABLE 12
+#define MADV_UNMERGEABLE 13
+#define MADV_HUGEPAGE 14
+#define MADV_NOHUGEPAGE 15
+#define MADV_DONTDUMP 16
+#define MADV_DODUMP 17
+#define MADV_WIPEONFORK 18
+#define MADV_KEEPONFORK 19
+#define MADV_COLD 20
+#define MADV_PAGEOUT 21
+#define MADV_POPULATE_READ 22
+#define MADV_POPULATE_WRITE 23
+#define MADV_DONTNEED_LOCKED 24
+#define MADV_COLLAPSE 25
+#define MADV_HWPOISON 100
+#define MADV_GUARD_INSTALL 102
+#define MADV_GUARD_REMOVE 103
+
+static inline isize linux_madvise( usize start, usize len, s32 behaviour ) {
+	return linux_syscall3( 11, start, len, (usize)(u32)behaviour );
+}
 
 #endif
platform/platform.h
@@ -1,3 +1,6 @@
+#ifndef NOTLIBC_HEADER_PLATFORM_PLATFORM_INCLUDED
+#define NOTLIBC_HEADER_PLATFORM_PLATFORM_INCLUDED
+
 #include "./common/build.h"
 #include "./common/compiler.h"
 #include "./common/arch.h"
@@ -15,14 +18,75 @@ ASSERT( sizeof( u16 ) == 2 );
 ASSERT( sizeof( u32 ) == 4 );
 ASSERT( sizeof( u64 ) == 8 );
 
-ASSERT( sizeof( i8 ) == 1 );
-ASSERT( sizeof( i16 ) == 2 );
-ASSERT( sizeof( i32 ) == 4 );
-ASSERT( sizeof( i64 ) == 8 );
+ASSERT( sizeof( s8 ) == 1 );
+ASSERT( sizeof( s16 ) == 2 );
+ASSERT( sizeof( s32 ) == 4 );
+ASSERT( sizeof( s64 ) == 8 );
 
 ASSERT( sizeof( f32 ) == 4 );
 ASSERT( sizeof( f64 ) == 8 );
 
+/* Error handling types and macros. */
+
+typedef u32 Status;
+
+#define S_MASK (3 << 30)
+
+#define S_SUCCESS (1 << 30) /* Requested action performed. */
+#define S_COMPLETION (0 << 30) /* Action didn't need to be performed - desired state is already present/active. */
+#define S_FAILURE (2 << 30) /* Action failed. */
+
+#define S_SOURCE_MASK (0x3ff << 20)
+enum _ErrorSources {
+
+	ERR_ARG_1 =  (0x1 << 20),
+	ERR_ARG_2 =  (0x2 << 20),
+	ERR_ARG_3 =  (0x3 << 20),
+	ERR_ARG_4 =  (0x4 << 20),
+	ERR_ARG_5 =  (0x5 << 20),
+	ERR_ARG_6 =  (0x6 << 20),
+	ERR_ARG_7 =  (0x7 << 20),
+	ERR_ARG_8 =  (0x8 << 20),
+	ERR_ARG_9 =  (0x9 << 20),
+	ERR_ARG_10 = (0xa << 20),
+	ERR_ARG_11 = (0xb << 20),
+	ERR_ARG_12 = (0xc << 20),
+
+	ERR_PLATFORM = (0x10 << 20), /* The error resulted inside platform code. */
+};
+
+#define S_REASON_MASK (0xfffff)
+enum _ErrorReasons {
+	ERR_GENERIC = 0, /* Even numbers are for the implementations, odd are for user-defined errors */
+
+	ERR_INVALID_ARGUMENT = 2,
+
+	ERR_OVERFLOW = 4,
+
+	ERR_OUT_OF_BOUND = 6, /* a value is out of bounds or the operation would violate certain bounds with the current arguments */
+
+	ERR_MEM_MAP_FAILURE = 0x100,
+	ERR_MEM_UNMAP_FAILURE = 0x102,
+	ERR_MEM_SETPERM_FAILURE = 0x104,
+	ERR_MEM_SETHINT_FAILURE = 0x106,
+
+};
+
+
+#define S_OK(...) ( ((__VA_ARGS__) & S_MASK) != (Status)S_FAILURE ) /* != FAILURE, cause otherwise the expression will appear twice, which is a great footgun when it's stateful... we are having none of that here */
+#define S_NOK(...) ( ((__VA_ARGS__) & S_MASK) == (Status)S_FAILURE )
+
+#define S_SUCCESSFUL(...) ( ((__VA_ARGS__) & S_MASK) == (Status)S_SUCCESS )
+#define S_COMPLETED(...) ( ((__VA_ARGS__) & S_MASK) == (Status)S_COMPLETION )
+#define S_FAILED(...) ( ((__VA_ARGS__) & S_MASK) == (Status)S_FAILURE )
+
+#define TRY(...) do {             \
+	Status _res = (__VA_ARGS__); \
+	if( S_NOK(_res) ) {          \
+		return _res;             \
+	}                            \
+} while(0)
+
 /* Core platform functions */
 
 static inline void terminate(u32 code); /* aka exit */
@@ -30,9 +94,16 @@ static inline void terminate_forced(void); /* force exit, fault, anything that t
 
 static inline void breakpoint(void); /* raise a debug breakpoint, if possible on the target */
 
+static inline void assert( usize condition ) {
+	if( !condition ) {
+		breakpoint();
+	}
+}
 
 #define HEADER_PLATFORM_COREFUNC   \
 	LINUX("linux/corefunc.h")     \
 	WINDOWS("windows/corefunc.h")
 
 #include HEADER_PLATFORM_COREFUNC
+
+#endif
util/mathops.h
@@ -0,0 +1,31 @@
+#ifndef NOTLIBC_HEADER_UTIL_MATHOPS_INCLUDED
+#define NOTLIBC_HEADER_UTIL_MATHOPS_INCLUDED
+
+#include "../platform/platform.h"
+#include "./safemath.h"
+
+
+// TODO: Add overflow and underflow checks for both signed and unsigned, as well as for gcc-likes and generic ones with portable C checks.
+
+
+#define GEN_ROUND_UP(T) \
+	static inline Status T##_round_up( T number, T multiple, T *out ) {           \
+		if( multiple == 0 ) return S_FAILURE | ERR_ARG_3 | ERR_INVALID_ARGUMENT; \
+		T rem = number%multiple;                                                 \
+		                                                                         \
+		if( rem == 0 ) {                                                         \
+			*out = number;                                                      \
+		} else {                                                                 \
+			TRY( T##_add( number, multiple - rem, out ) );                      \
+		}                                                                        \
+		return S_SUCCESS;                                                        \
+	}
+
+
+
+GEN_ROUND_UP(u8)
+GEN_ROUND_UP(u16)
+GEN_ROUND_UP(u32)
+GEN_ROUND_UP(u64)
+
+#endif
util/safemath.h
@@ -0,0 +1,13 @@
+#ifndef NOTLIBC_HEADER_UTIL_SAFEMATH_INCLUDED
+#define NOTLIBC_HEADER_UTIL_SAFEMATH_INCLUDED
+
+#include "../platform/common/compiler.h"
+
+#define SAFEMATH_HEADER \
+	GCCLIKE("./safemath_gcclike.h") \
+	TCC("./safemath_generic.h") \
+	MSVC("./safemath_generic.h")
+
+#include SAFEMATH_HEADER
+
+#endif
util/safemath_gcclike.h
@@ -0,0 +1,25 @@
+#ifndef NOTLIBC_HEADER_UTIL_SAFEMATH_GCCLIKE_INCLUDED
+#define NOTLIBC_HEADER_UTIL_SAFEMATH_GCCLIKE_INCLUDED
+
+#include "../platform/platform.h"
+
+#define GEN_ADD(T) \
+	static inline Status T##_add( T n1, T n2, T*out ) { \
+		if( __builtin_add_overflow(n1, n2, out) ) { \
+			return S_FAILURE | ERR_OVERFLOW;\
+		} \
+		return S_SUCCESS; \
+	} \
+
+
+GEN_ADD(u8)
+GEN_ADD(u16)
+GEN_ADD(u32)
+GEN_ADD(u64)
+
+GEN_ADD(s8)
+GEN_ADD(s16)
+GEN_ADD(s32)
+GEN_ADD(s64)
+
+#endif