use compiler static assert when possible

This commit is contained in:
jacob 2025-07-03 13:02:16 -05:00
parent 05ce4f32a1
commit fdf35c698d
13 changed files with 46 additions and 54 deletions

View File

@ -4,7 +4,7 @@
#define SH_DECL(t, n) struct CAT(sh_, t) n #define SH_DECL(t, n) struct CAT(sh_, t) n
#define SH_DECLS(t, n) SH_DECL(t, n) #define SH_DECLS(t, n) SH_DECL(t, n)
#define SH_ENTRY(rootsig) static #define SH_ENTRY(rootsig) static
#define SH_ASSERT_32BIT(s, n) CT_ASSERT((sizeof(s) / 4) == n) #define SH_ASSERT_32BIT(s, n) STATIC_ASSERT((sizeof(s) / 4) == n)
struct sh_uint { u32 v; }; struct sh_uint { u32 v; };
INLINE struct sh_uint sh_uint_from_u32(u32 v) INLINE struct sh_uint sh_uint_from_u32(u32 v)
@ -34,7 +34,7 @@ struct sh_float4x4 { f32 v[4][4]; };
INLINE struct sh_float4x4 sh_float4x4_from_mat4x4(struct mat4x4 v) INLINE struct sh_float4x4 sh_float4x4_from_mat4x4(struct mat4x4 v)
{ {
struct sh_float4x4 res; struct sh_float4x4 res;
CT_ASSERT(sizeof(res) == sizeof(v)); STATIC_ASSERT(sizeof(res) == sizeof(v));
MEMCPY(&res, v.e, sizeof(res)); MEMCPY(&res, v.e, sizeof(res));
return res; return res;
} }
@ -43,7 +43,7 @@ struct sh_float2x3 { f32 v[2][3]; };
INLINE struct sh_float2x3 sh_float2x3_from_xform(struct xform v) INLINE struct sh_float2x3 sh_float2x3_from_xform(struct xform v)
{ {
struct sh_float2x3 res; struct sh_float2x3 res;
CT_ASSERT(sizeof(res) == sizeof(v)); STATIC_ASSERT(sizeof(res) == sizeof(v));
MEMCPY(&res, &v, sizeof(res)); MEMCPY(&res, &v, sizeof(res));
return res; return res;
} }

View File

@ -219,8 +219,8 @@ void sys_app_entry(struct string args_str)
#if !RTC #if !RTC
/* Verify test modes aren't left on by accident in release mode */ /* Verify test modes aren't left on by accident in release mode */
CT_ASSERT(BITBUFF_DEBUG == 0); STATIC_ASSERT(BITBUFF_DEBUG == 0);
CT_ASSERT(BITBUFF_TEST == 0); STATIC_ASSERT(BITBUFF_TEST == 0);
#endif #endif
#if BITBUFF_TEST #if BITBUFF_TEST

View File

@ -33,8 +33,8 @@ struct arena *arena_alloc(u64 reserve)
} }
ASSERT(((u64)base & 0xFFF) == 0); /* Base should be 4k aligned */ ASSERT(((u64)base & 0xFFF) == 0); /* Base should be 4k aligned */
CT_ASSERT(ARENA_HEADER_SIZE <= ARENA_BLOCK_SIZE); /* Header must fit in first block */ STATIC_ASSERT(ARENA_HEADER_SIZE <= ARENA_BLOCK_SIZE); /* Header must fit in first block */
CT_ASSERT(sizeof(struct arena) <= ARENA_HEADER_SIZE); /* Arena struct must fit in header */ STATIC_ASSERT(sizeof(struct arena) <= ARENA_HEADER_SIZE); /* Arena struct must fit in header */
__profalloc(base, ARENA_BLOCK_SIZE); __profalloc(base, ARENA_BLOCK_SIZE);
ASAN_POISON(base + sizeof(struct arena), ARENA_BLOCK_SIZE - sizeof(struct arena)); ASAN_POISON(base + sizeof(struct arena), ARENA_BLOCK_SIZE - sizeof(struct arena));

View File

@ -141,7 +141,7 @@ INLINE void *_arena_push_dry(struct arena *arena, u64 align)
INLINE struct arena_temp _scratch_begin(struct arena *potential_conflict) INLINE struct arena_temp _scratch_begin(struct arena *potential_conflict)
{ {
/* This function is currently hard-coded to support 2 scratch arenas */ /* This function is currently hard-coded to support 2 scratch arenas */
CT_ASSERT(SYS_SCRATCH_ARENAS_PER_FIBER == 2); STATIC_ASSERT(SYS_SCRATCH_ARENAS_PER_CTX == 2);
/* Use `scratch_begin_no_conflict` if no conflicts are present */ /* Use `scratch_begin_no_conflict` if no conflicts are present */
ASSERT(potential_conflict != NULL); ASSERT(potential_conflict != NULL);

View File

@ -600,7 +600,7 @@ struct collider_collision_points_result collider_collision_points(struct collide
/* Clip to determine final points */ /* Clip to determine final points */
if (colliding) { if (colliding) {
/* Max vertices must be < 16 to fit in 4 bit ids */ /* Max vertices must be < 16 to fit in 4 bit ids */
CT_ASSERT(countof(shape0->points) <= 16); STATIC_ASSERT(countof(shape0->points) <= 16);
struct collider_menkowski_feature f = epa_res.closest_feature; struct collider_menkowski_feature f = epa_res.closest_feature;

View File

@ -119,9 +119,17 @@ extern "C" {
* ========================== */ * ========================== */
/* Compile time assert */ /* Compile time assert */
#define CT_ASSERT3(cond, line) struct CT_ASSERT_____##line {int foo[(cond) ? 1 : -1];} #if LANGUAGE_C && (__STDC_VERSION__ < 202311L)
#define CT_ASSERT2(cond, line) CT_ASSERT3(cond, line) # if COMPILER_MSVC
#define CT_ASSERT(cond) CT_ASSERT2(cond, __LINE__) # define STATIC_ASSERT3(cond, line) struct STATIC_ASSERT_____##line {int foo[(cond) ? 1 : -1];}
# define STATIC_ASSERT2(cond, line) STATIC_ASSERT3(cond, line)
# define STATIC_ASSERT(cond) STATIC_ASSERT2(cond, __LINE__)
# else
# define STATIC_ASSERT(cond) _Static_assert(cond, "")
# endif
#else
# define STATIC_ASSERT(c) static_assert(c, "")
#endif
#if COMPILER_MSVC #if COMPILER_MSVC
# if DEBINFO # if DEBINFO

View File

@ -38,18 +38,11 @@ struct worker_job_queue {
}; };
struct alignas(64) worker_ctx { struct alignas(64) worker_ctx {
/* 4 bytes */
i32 worker_id; /* Will be -1 if thread is not a worker */ i32 worker_id; /* Will be -1 if thread is not a worker */
/* 4 bytes */
i32 pin_depth; i32 pin_depth;
/* 4 bytes */
b32 initialized; b32 initialized;
/* 52 bytes */
u8 padding[52];
}; };
/* One ctx per cache line (prevent false sharing) */ STATIC_ASSERT(alignof(struct worker_ctx) == 64); /* To avoid false sharing */
CT_ASSERT(alignof(struct worker_ctx) == 64);
CT_ASSERT(sizeof(struct worker_ctx) == 64);
/* ========================== * /* ========================== *
* Global state * Global state

View File

@ -92,8 +92,8 @@ void phys_create_and_update_contacts(struct phys_step_ctx *ctx, f32 elapsed_dt,
struct collider_collision_points_result collider_res = collider_collision_points(&e0_collider, &e1_collider, e0_xf, e1_xf); struct collider_collision_points_result collider_res = collider_collision_points(&e0_collider, &e1_collider, e0_xf, e1_xf);
/* Parts of algorithm are hard-coded to support 2 contact points */ /* Parts of algorithm are hard-coded to support 2 contact points */
CT_ASSERT(countof(constraint_ent->contact_constraint_data.points) == 2); STATIC_ASSERT(countof(constraint_ent->contact_constraint_data.points) == 2);
CT_ASSERT(countof(collider_res.points) == 2); STATIC_ASSERT(countof(collider_res.points) == 2);
struct phys_contact_constraint *constraint = NULL; struct phys_contact_constraint *constraint = NULL;
if (collider_res.num_points > 0) { if (collider_res.num_points > 0) {

View File

@ -172,7 +172,7 @@ enum sim_tile_kind {
NUM_SIM_TILE_KINDS NUM_SIM_TILE_KINDS
}; };
CT_ASSERT(NUM_SIM_TILE_KINDS < 256); /* Tile kind must fit in 8 bits */ STATIC_ASSERT(NUM_SIM_TILE_KINDS < 256); /* Tile kind must fit in 8 bits */
struct sim_ent_bin; struct sim_ent_bin;

View File

@ -79,7 +79,7 @@ INTERNAL struct sock_address sock_address_from_ip_port_cstr(char *ip_cstr, char
res.valid = true; res.valid = true;
res.family = SOCK_ADDRESS_FAMILY_IPV4; res.family = SOCK_ADDRESS_FAMILY_IPV4;
res.portnb = sockaddr->sin_port; res.portnb = sockaddr->sin_port;
CT_ASSERT(sizeof(sockaddr->sin_addr) == 4); STATIC_ASSERT(sizeof(sockaddr->sin_addr) == 4);
MEMCPY(res.ipnb, (void *)&sockaddr->sin_addr, 4); MEMCPY(res.ipnb, (void *)&sockaddr->sin_addr, 4);
break; break;
} else if (ai_res->ai_family == AF_INET6) { } else if (ai_res->ai_family == AF_INET6) {
@ -89,7 +89,7 @@ INTERNAL struct sock_address sock_address_from_ip_port_cstr(char *ip_cstr, char
res.valid = true; res.valid = true;
res.family = SOCK_ADDRESS_FAMILY_IPV6; res.family = SOCK_ADDRESS_FAMILY_IPV6;
res.portnb = sockaddr->sin6_port; res.portnb = sockaddr->sin6_port;
CT_ASSERT(sizeof(sockaddr->sin6_addr) == 16); STATIC_ASSERT(sizeof(sockaddr->sin6_addr) == 16);
MEMCPY(res.ipnb, (void *)&sockaddr->sin6_addr, 16); MEMCPY(res.ipnb, (void *)&sockaddr->sin6_addr, 16);
break; break;
#endif #endif

View File

@ -16,7 +16,7 @@
* It will entries until the budget has shrunk < target. */ * It will entries until the budget has shrunk < target. */
#define CACHE_MEMORY_BUDGET_THRESHOLD (MEGABYTE(256)) #define CACHE_MEMORY_BUDGET_THRESHOLD (MEGABYTE(256))
#define CACHE_MEMORY_BUDGET_TARGET (MEGABYTE(128)) #define CACHE_MEMORY_BUDGET_TARGET (MEGABYTE(128))
CT_ASSERT(CACHE_MEMORY_BUDGET_THRESHOLD >= CACHE_MEMORY_BUDGET_TARGET); STATIC_ASSERT(CACHE_MEMORY_BUDGET_THRESHOLD >= CACHE_MEMORY_BUDGET_TARGET);
#define CACHE_BINS_COUNT 1024 #define CACHE_BINS_COUNT 1024
@ -57,7 +57,7 @@ struct cache_refcount {
i32 count; /* Number of scopes currently holding a reference to this entry */ i32 count; /* Number of scopes currently holding a reference to this entry */
i32 last_ref_cycle; /* Last evictor cycle that the refcount was modified */ i32 last_ref_cycle; /* Last evictor cycle that the refcount was modified */
}; };
CT_ASSERT(sizeof(struct cache_refcount) == 8); /* Must fit into 64 bit atomic */ STATIC_ASSERT(sizeof(struct cache_refcount) == 8); /* Must fit into 64 bit atomic */
struct cache_entry_hash { struct cache_entry_hash {
u64 v; u64 v;

View File

@ -485,7 +485,7 @@ b32 sys_run_command(struct string cmd);
* Fiber * Fiber
* ========================== */ * ========================== */
#define SYS_MAX_FIBERS 65536 #define SYS_MAX_FIBERS 4096
i32 sys_current_fiber_id(void); i32 sys_current_fiber_id(void);
@ -493,10 +493,10 @@ i32 sys_current_fiber_id(void);
* Scratch * Scratch
* ========================== */ * ========================== */
#define SYS_SCRATCH_ARENAS_PER_FIBER 2 #define SYS_SCRATCH_ARENAS_PER_CTX 2
struct sys_scratch_ctx { struct sys_scratch_ctx {
struct arena *arenas[SYS_SCRATCH_ARENAS_PER_FIBER]; struct arena *arenas[SYS_SCRATCH_ARENAS_PER_CTX];
}; };
struct sys_scratch_ctx *sys_scratch_ctx_from_fiber_id(i32 fiber_id); struct sys_scratch_ctx *sys_scratch_ctx_from_fiber_id(i32 fiber_id);

View File

@ -114,18 +114,14 @@ struct win32_window {
#define FIBER_CTX_SLEEP_TIMER_INIT_MAGIC ((HANDLE)0x48e87857650169c8)
struct alignas(64) fiber_ctx { struct alignas(64) fiber_ctx {
i32 id; /* 4 bytes */
u8 pad0[4]; /* 4 bytes */
HANDLE sleep_timer; /* 8 bytes */ HANDLE sleep_timer; /* 8 bytes */
struct sys_scratch_ctx scratch_ctx; /* 16 bytes */ struct sys_scratch_ctx scratch_ctx; /* 16 bytes */
u8 pad1[16]; /* 32 bytes */ u8 pad[40]; /* 40 bytes */
}; };
CT_ASSERT(sizeof(struct fiber_ctx) == 64); STATIC_ASSERT(sizeof(struct fiber_ctx) == 64); /* Assume ctx fits in one cache line (increase if necessary) */
CT_ASSERT(alignof(struct fiber_ctx) == 64); STATIC_ASSERT(alignof(struct fiber_ctx) == 64); /* To avoid false sharing */
@ -140,7 +136,6 @@ GLOBAL struct {
i64 qpc_per_second; i64 qpc_per_second;
i64 ns_per_qpc; i64 ns_per_qpc;
i32 scheduler_period_ms; i32 scheduler_period_ms;
DWORD thread_tls_index;
u32 main_thread_id; u32 main_thread_id;
wchar_t cmdline_args_wstr[8192]; wchar_t cmdline_args_wstr[8192];
@ -206,8 +201,7 @@ INTERNAL i32 fiber_ctx_init(void)
} }
struct fiber_ctx *ctx = &G.fiber_contexts[id]; struct fiber_ctx *ctx = &G.fiber_contexts[id];
{ {
ctx->id = id; ctx->sleep_timer = FIBER_CTX_SLEEP_TIMER_INIT_MAGIC;
ctx->sleep_timer = CreateWaitableTimerExW(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
} }
return id; return id;
} }
@ -223,7 +217,7 @@ i32 sys_current_fiber_id(void)
} }
/* ========================== * /* ========================== *
* Scratch * Scratch ctx
* ========================== */ * ========================== */
struct sys_scratch_ctx *sys_scratch_ctx_from_fiber_id(i32 id) struct sys_scratch_ctx *sys_scratch_ctx_from_fiber_id(i32 id)
@ -231,7 +225,7 @@ struct sys_scratch_ctx *sys_scratch_ctx_from_fiber_id(i32 id)
struct fiber_ctx *fiber_ctx = fiber_ctx_from_id(id); struct fiber_ctx *fiber_ctx = fiber_ctx_from_id(id);
struct sys_scratch_ctx *scratch_ctx = &fiber_ctx->scratch_ctx; struct sys_scratch_ctx *scratch_ctx = &fiber_ctx->scratch_ctx;
if (!scratch_ctx->arenas[0]) { if (!scratch_ctx->arenas[0]) {
__profscope(Initialize scratch ctx); __profscope(Initialize scratch context);
for (u32 i = 0; i < countof(scratch_ctx->arenas); ++i) { for (u32 i = 0; i < countof(scratch_ctx->arenas); ++i) {
scratch_ctx->arenas[i] = arena_alloc(GIGABYTE(64)); scratch_ctx->arenas[i] = arena_alloc(GIGABYTE(64));
} }
@ -2261,7 +2255,7 @@ void sys_panic(struct string msg)
WRITE_BARRIER(); WRITE_BARRIER();
SetEvent(G.panic_event); SetEvent(G.panic_event);
/* Wait for thread to be terminated */ /* Wait for process termination */
if (GetCurrentThreadId() != G.main_thread_id) { if (GetCurrentThreadId() != G.main_thread_id) {
Sleep(INFINITE); Sleep(INFINITE);
} }
@ -2274,7 +2268,7 @@ void sys_panic(struct string msg)
/* https://blog.bearcats.nl/perfect-sleep-function/ */ /* https://blog.bearcats.nl/perfect-sleep-function/ */
INTERNAL void win32_precise_sleep_timer(f64 seconds, HANDLE timer) INTERNAL void win32_precise_sleep_timer(HANDLE timer, f64 seconds)
{ {
__prof; __prof;
@ -2359,9 +2353,14 @@ void sys_sleep_precise(f64 seconds)
__prof; __prof;
struct fiber_ctx *ctx = fiber_ctx_from_id(sys_current_fiber_id()); struct fiber_ctx *ctx = fiber_ctx_from_id(sys_current_fiber_id());
HANDLE timer = ctx->sleep_timer; HANDLE timer = ctx->sleep_timer;
if (timer == FIBER_CTX_SLEEP_TIMER_INIT_MAGIC) {
__profscope(Create high resolution timer);
timer = CreateWaitableTimerExW(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
ctx->sleep_timer = timer;
}
if (timer) { if (timer) {
/* Use newer sleeping method */ /* Use newer sleeping method */
win32_precise_sleep_timer(seconds, timer); win32_precise_sleep_timer(timer, seconds);
} else { } else {
/* Fall back to older sleep method if CREATE_WAITABLE_TIMER_HIGH_RESOLUTION /* Fall back to older sleep method if CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
* is not available due to older windows version */ * is not available due to older windows version */
@ -2476,14 +2475,6 @@ int CALLBACK wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev_instance,
G.windows_mutex = sys_mutex_alloc(); G.windows_mutex = sys_mutex_alloc();
G.windows_arena = arena_alloc(GIGABYTE(64)); G.windows_arena = arena_alloc(GIGABYTE(64));
/* Set up TLS index */
G.thread_tls_index = TlsAlloc();
if (G.thread_tls_index == TLS_OUT_OF_INDEXES) {
/* TODO: GetLastError */
error_msg = L"Platform initialization error: TLS_OUT_OF_INDEXES";
goto abort;
}
/* Initialize vk table */ /* Initialize vk table */
win32_init_vk_btn_table(); win32_init_vk_btn_table();