From fdf35c698d913d6e35efe0d1ccf65dcfe03a4839 Mon Sep 17 00:00:00 2001 From: jacob Date: Thu, 3 Jul 2025 13:02:16 -0500 Subject: [PATCH] use compiler static assert when possible --- res/sh/sh_common.h | 6 +++--- src/app.c | 4 ++-- src/arena.c | 4 ++-- src/arena.h | 2 +- src/collider.c | 2 +- src/common.h | 14 +++++++++++--- src/job.c | 9 +-------- src/phys.c | 4 ++-- src/sim.h | 2 +- src/sock_win32.c | 4 ++-- src/sprite.c | 4 ++-- src/sys.h | 6 +++--- src/sys_win32.c | 39 +++++++++++++++------------------------ 13 files changed, 46 insertions(+), 54 deletions(-) diff --git a/res/sh/sh_common.h b/res/sh/sh_common.h index c6d5f3c8..09448a08 100644 --- a/res/sh/sh_common.h +++ b/res/sh/sh_common.h @@ -4,7 +4,7 @@ #define SH_DECL(t, n) struct CAT(sh_, t) n #define SH_DECLS(t, n) SH_DECL(t, n) #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; }; 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) { struct sh_float4x4 res; - CT_ASSERT(sizeof(res) == sizeof(v)); + STATIC_ASSERT(sizeof(res) == sizeof(v)); MEMCPY(&res, v.e, sizeof(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) { struct sh_float2x3 res; - CT_ASSERT(sizeof(res) == sizeof(v)); + STATIC_ASSERT(sizeof(res) == sizeof(v)); MEMCPY(&res, &v, sizeof(res)); return res; } diff --git a/src/app.c b/src/app.c index aa7ebca9..623b7d69 100644 --- a/src/app.c +++ b/src/app.c @@ -219,8 +219,8 @@ void sys_app_entry(struct string args_str) #if !RTC /* Verify test modes aren't left on by accident in release mode */ - CT_ASSERT(BITBUFF_DEBUG == 0); - CT_ASSERT(BITBUFF_TEST == 0); + STATIC_ASSERT(BITBUFF_DEBUG == 0); + STATIC_ASSERT(BITBUFF_TEST == 0); #endif #if BITBUFF_TEST diff --git a/src/arena.c b/src/arena.c index bb2370a6..3bab294c 100644 --- a/src/arena.c +++ b/src/arena.c @@ -33,8 +33,8 @@ struct arena *arena_alloc(u64 reserve) } ASSERT(((u64)base & 0xFFF) == 0); /* Base should be 4k aligned */ - CT_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(ARENA_HEADER_SIZE <= ARENA_BLOCK_SIZE); /* Header must fit in first block */ + STATIC_ASSERT(sizeof(struct arena) <= ARENA_HEADER_SIZE); /* Arena struct must fit in header */ __profalloc(base, ARENA_BLOCK_SIZE); ASAN_POISON(base + sizeof(struct arena), ARENA_BLOCK_SIZE - sizeof(struct arena)); diff --git a/src/arena.h b/src/arena.h index 43a44eaa..2ced94fd 100644 --- a/src/arena.h +++ b/src/arena.h @@ -141,7 +141,7 @@ INLINE void *_arena_push_dry(struct arena *arena, u64 align) INLINE struct arena_temp _scratch_begin(struct arena *potential_conflict) { /* 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 */ ASSERT(potential_conflict != NULL); diff --git a/src/collider.c b/src/collider.c index d0ad00ac..95a598e2 100644 --- a/src/collider.c +++ b/src/collider.c @@ -600,7 +600,7 @@ struct collider_collision_points_result collider_collision_points(struct collide /* Clip to determine final points */ if (colliding) { /* 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; diff --git a/src/common.h b/src/common.h index cda732be..26155e3e 100644 --- a/src/common.h +++ b/src/common.h @@ -119,9 +119,17 @@ extern "C" { * ========================== */ /* Compile time assert */ -#define CT_ASSERT3(cond, line) struct CT_ASSERT_____##line {int foo[(cond) ? 1 : -1];} -#define CT_ASSERT2(cond, line) CT_ASSERT3(cond, line) -#define CT_ASSERT(cond) CT_ASSERT2(cond, __LINE__) +#if LANGUAGE_C && (__STDC_VERSION__ < 202311L) +# if COMPILER_MSVC +# 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 DEBINFO diff --git a/src/job.c b/src/job.c index 9a6559df..7c01ea7e 100644 --- a/src/job.c +++ b/src/job.c @@ -38,18 +38,11 @@ struct worker_job_queue { }; struct alignas(64) worker_ctx { - /* 4 bytes */ i32 worker_id; /* Will be -1 if thread is not a worker */ - /* 4 bytes */ i32 pin_depth; - /* 4 bytes */ b32 initialized; - /* 52 bytes */ - u8 padding[52]; }; -/* One ctx per cache line (prevent false sharing) */ -CT_ASSERT(alignof(struct worker_ctx) == 64); -CT_ASSERT(sizeof(struct worker_ctx) == 64); +STATIC_ASSERT(alignof(struct worker_ctx) == 64); /* To avoid false sharing */ /* ========================== * * Global state diff --git a/src/phys.c b/src/phys.c index 06cb5a5b..b33b5da0 100644 --- a/src/phys.c +++ b/src/phys.c @@ -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); /* Parts of algorithm are hard-coded to support 2 contact points */ - CT_ASSERT(countof(constraint_ent->contact_constraint_data.points) == 2); - CT_ASSERT(countof(collider_res.points) == 2); + STATIC_ASSERT(countof(constraint_ent->contact_constraint_data.points) == 2); + STATIC_ASSERT(countof(collider_res.points) == 2); struct phys_contact_constraint *constraint = NULL; if (collider_res.num_points > 0) { diff --git a/src/sim.h b/src/sim.h index 79c304bf..5ebc692f 100644 --- a/src/sim.h +++ b/src/sim.h @@ -172,7 +172,7 @@ enum sim_tile_kind { 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; diff --git a/src/sock_win32.c b/src/sock_win32.c index 9f1d0faf..ba8cd67d 100644 --- a/src/sock_win32.c +++ b/src/sock_win32.c @@ -79,7 +79,7 @@ INTERNAL struct sock_address sock_address_from_ip_port_cstr(char *ip_cstr, char res.valid = true; res.family = SOCK_ADDRESS_FAMILY_IPV4; 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); break; } 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.family = SOCK_ADDRESS_FAMILY_IPV6; 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); break; #endif diff --git a/src/sprite.c b/src/sprite.c index 0fbe105f..7bc162ba 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -16,7 +16,7 @@ * It will entries until the budget has shrunk < target. */ #define CACHE_MEMORY_BUDGET_THRESHOLD (MEGABYTE(256)) #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 @@ -57,7 +57,7 @@ struct cache_refcount { i32 count; /* Number of scopes currently holding a reference to this entry */ 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 { u64 v; diff --git a/src/sys.h b/src/sys.h index 968b9b63..f11698a9 100644 --- a/src/sys.h +++ b/src/sys.h @@ -485,7 +485,7 @@ b32 sys_run_command(struct string cmd); * Fiber * ========================== */ -#define SYS_MAX_FIBERS 65536 +#define SYS_MAX_FIBERS 4096 i32 sys_current_fiber_id(void); @@ -493,10 +493,10 @@ i32 sys_current_fiber_id(void); * Scratch * ========================== */ -#define SYS_SCRATCH_ARENAS_PER_FIBER 2 +#define SYS_SCRATCH_ARENAS_PER_CTX 2 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); diff --git a/src/sys_win32.c b/src/sys_win32.c index a2aaadd7..92de36ea 100644 --- a/src/sys_win32.c +++ b/src/sys_win32.c @@ -114,18 +114,14 @@ struct win32_window { - +#define FIBER_CTX_SLEEP_TIMER_INIT_MAGIC ((HANDLE)0x48e87857650169c8) struct alignas(64) fiber_ctx { - i32 id; /* 4 bytes */ - u8 pad0[4]; /* 4 bytes */ HANDLE sleep_timer; /* 8 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); -CT_ASSERT(alignof(struct fiber_ctx) == 64); - - +STATIC_ASSERT(sizeof(struct fiber_ctx) == 64); /* Assume ctx fits in one cache line (increase if necessary) */ +STATIC_ASSERT(alignof(struct fiber_ctx) == 64); /* To avoid false sharing */ @@ -140,7 +136,6 @@ GLOBAL struct { i64 qpc_per_second; i64 ns_per_qpc; i32 scheduler_period_ms; - DWORD thread_tls_index; u32 main_thread_id; wchar_t cmdline_args_wstr[8192]; @@ -206,8 +201,7 @@ INTERNAL i32 fiber_ctx_init(void) } struct fiber_ctx *ctx = &G.fiber_contexts[id]; { - ctx->id = id; - ctx->sleep_timer = CreateWaitableTimerExW(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); + ctx->sleep_timer = FIBER_CTX_SLEEP_TIMER_INIT_MAGIC; } 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) @@ -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 sys_scratch_ctx *scratch_ctx = &fiber_ctx->scratch_ctx; if (!scratch_ctx->arenas[0]) { - __profscope(Initialize scratch ctx); + __profscope(Initialize scratch context); for (u32 i = 0; i < countof(scratch_ctx->arenas); ++i) { scratch_ctx->arenas[i] = arena_alloc(GIGABYTE(64)); } @@ -2261,7 +2255,7 @@ void sys_panic(struct string msg) WRITE_BARRIER(); SetEvent(G.panic_event); - /* Wait for thread to be terminated */ + /* Wait for process termination */ if (GetCurrentThreadId() != G.main_thread_id) { Sleep(INFINITE); } @@ -2274,7 +2268,7 @@ void sys_panic(struct string msg) /* 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; @@ -2359,9 +2353,14 @@ void sys_sleep_precise(f64 seconds) __prof; struct fiber_ctx *ctx = fiber_ctx_from_id(sys_current_fiber_id()); 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) { /* Use newer sleeping method */ - win32_precise_sleep_timer(seconds, timer); + win32_precise_sleep_timer(timer, seconds); } else { /* Fall back to older sleep method if CREATE_WAITABLE_TIMER_HIGH_RESOLUTION * 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_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 */ win32_init_vk_btn_table();