4040 libc_compat_fns map [string ]bool
4141 tc & types.TypeChecker = unsafe { nil }
4242 has_builtins bool
43+ has_stdatomic_header bool
44+ has_stdatomic_compat_header bool
4345 tmp_count int
4446 line_start bool
4547 field_name_set map [string ]bool // every struct field's C name (lazy) — for const/field collision checks
5355 struct_decl_short_infos map [string ]StructDeclInfo
5456 runtime_inits []string
5557 compiler_vroot string
58+ c99_ mode bool
5659 cur_fn_name string
5760 cur_param_names []string
5861 cur_param_type_values []types.Type
@@ -100,6 +103,11 @@ pub fn (g &FlatGen) c_flags() []string {
100103 return g.c_flags.clone ()
101104}
102105
106+ // set_c99_mode configures whether generated C should support strict C99 builds.
107+ pub fn (mut g FlatGen) set_c99_mode (enabled bool ) {
108+ g.c99_mode = enabled
109+ }
110+
103111// new creates a FlatGen value for c.
104112pub fn FlatGen .new () FlatGen {
105113 return FlatGen{
@@ -208,6 +216,8 @@ pub fn (mut g FlatGen) gen_with_used_options(a &flat.FlatAst, used_fns map[strin
208216 g.c_directives = []CDirective{}
209217 g.c_flags = []string {}
210218 g.libc_compat_fns = map [string ]bool {}
219+ g.has_stdatomic_header = false
220+ g.has_stdatomic_compat_header = false
211221 g.modules = map [string ]string {}
212222 g.fn_ptr_types = map [string ]string {}
213223 g.fixed_array_ret_wrappers = map [string ]bool {}
@@ -499,12 +509,17 @@ fn (mut g FlatGen) collect_c_directive(module_name string, node flat.Node, sourc
499509 if include_arg.len == 0 {
500510 return true
501511 }
502- // The atomic helper headers are superseded by the inline C11 `__atomic_*`
503- // helpers emitted in builtin_abi_decls(); also including them would
504- // redefine `v_prealloc_atomic_*` and the `atomic_*` helpers.
505- if include_arg.contains ('prealloc_atomics.h' ) || include_arg.contains ('stdatomic' ) {
512+ // These helper headers are superseded by the inline compiler helpers emitted in
513+ // builtin_abi_decls(); also including them would redefine the helpers.
514+ if include_arg.contains ('prealloc_atomics.h' ) || include_arg.contains ('filelock_helpers.h' ) {
506515 return true
507516 }
517+ if is_stdatomic_header (include_arg) {
518+ g.has_stdatomic_header = true
519+ }
520+ if is_stdatomic_compat_header (include_arg) {
521+ g.has_stdatomic_compat_header = true
522+ }
508523 g.add_c_directive (module_name, '#include ${include_arg }' , before_import)
509524 return true
510525 }
@@ -517,6 +532,17 @@ fn (mut g FlatGen) collect_c_directive(module_name string, node flat.Node, sourc
517532 return false
518533}
519534
535+ fn is_stdatomic_header (include_arg string ) bool {
536+ normalized := include_arg.replace ('\\ ' , '/' )
537+ return normalized == '<stdatomic.h>' || normalized == '"stdatomic.h"'
538+ || normalized.ends_with ('/stdatomic.h"' ) || is_stdatomic_compat_header (normalized)
539+ }
540+
541+ fn is_stdatomic_compat_header (include_arg string ) bool {
542+ normalized := include_arg.replace ('\\ ' , '/' )
543+ return normalized.contains ('/thirdparty/stdatomic/' ) && normalized.ends_with ('/atomic.h"' )
544+ }
545+
520546fn (mut g FlatGen) add_c_directive (module_name string , text string , before_import bool ) {
521547 if text.len == 0 {
522548 return
@@ -2815,6 +2841,7 @@ fn (g &FlatGen) is_module_qualified_enum(base flat.Node) bool {
28152841}
28162842
28172843fn (mut g FlatGen) preamble () {
2844+ g.c99_feature_test_macros ()
28182845 g.writeln ('#include <stdio.h>' )
28192846 g.writeln ('#include <stdlib.h>' )
28202847 g.writeln ('#include <string.h>' )
@@ -2823,6 +2850,7 @@ fn (mut g FlatGen) preamble() {
28232850 g.writeln ('#include <stdint.h>' ) // guarantees UINTPTR_MAX for the pointer-width atomic helpers
28242851 g.writeln ('#include <math.h>' )
28252852 g.writeln ('#include <unistd.h>' )
2853+ g.c99_atomic_compat_decls ()
28262854 if g.has_builtins {
28272855 g.writeln ('#include <time.h>' )
28282856 g.writeln ('#include <sys/time.h>' )
@@ -2836,7 +2864,6 @@ fn (mut g FlatGen) preamble() {
28362864 g.writeln ('#include <sys/utsname.h>' )
28372865 g.writeln ('#include <pthread.h>' )
28382866 g.writeln ('#include <semaphore.h>' )
2839- g.writeln ('#include <stdatomic.h>' )
28402867 g.writeln ('#include <termios.h>' )
28412868 g.writeln ('#include <unistd.h>' )
28422869 g.writeln ('#include <arpa/inet.h>' )
@@ -2904,6 +2931,36 @@ fn (mut g FlatGen) preamble() {
29042931 g.writeln ('' )
29052932}
29062933
2934+ fn (mut g FlatGen) c99_feature_test_macros () {
2935+ if ! g.c99_ mode {
2936+ return
2937+ }
2938+ g.writeln ('#if defined(__linux__) && !defined(_GNU_SOURCE)' )
2939+ g.writeln ('#define _GNU_SOURCE' )
2940+ g.writeln ('#endif' )
2941+ g.writeln ('#if defined(__linux__) && !defined(_POSIX_C_SOURCE)' )
2942+ g.writeln ('#define _POSIX_C_SOURCE 200809L' )
2943+ g.writeln ('#endif' )
2944+ }
2945+
2946+ fn (mut g FlatGen) c99_atomic_compat_decls () {
2947+ if g.has_stdatomic_header {
2948+ return
2949+ }
2950+ g.writeln ('typedef volatile uintptr_t atomic_uintptr_t;' )
2951+ g.writeln ('#ifndef memory_order_relaxed' )
2952+ g.writeln ('#define memory_order_relaxed 0' )
2953+ g.writeln ('#define memory_order_consume 1' )
2954+ g.writeln ('#define memory_order_acquire 2' )
2955+ g.writeln ('#define memory_order_release 3' )
2956+ g.writeln ('#define memory_order_acq_rel 4' )
2957+ g.writeln ('#define memory_order_seq_cst 5' )
2958+ g.writeln ('#endif' )
2959+ g.writeln ('#ifndef atomic_thread_fence' )
2960+ g.writeln ('#define atomic_thread_fence(order) __sync_synchronize()' )
2961+ g.writeln ('#endif' )
2962+ }
2963+
29072964fn (mut g FlatGen) write_arch_macros () {
29082965 g.writeln ('#ifndef __V_architecture' )
29092966 g.writeln ('#define __V_architecture 0' )
@@ -2954,37 +3011,26 @@ fn (mut g FlatGen) libc_compat_decls() {
29543011 }
29553012}
29563013
2957- fn (mut g FlatGen) builtin_abi_decls () {
2958- if ! g.has_builtins {
3014+ fn (mut g FlatGen) prealloc_atomic_compat_decls () {
3015+ g.writeln ('static inline int v_prealloc_atomic_add_i32(int *ptr, int delta) { return __atomic_add_fetch(ptr, delta, 5); }' )
3016+ g.writeln ('static inline int v_prealloc_atomic_load_i32(int *ptr) { return __atomic_add_fetch(ptr, 0, 5); }' )
3017+ g.writeln ('#ifdef __TINYC__' )
3018+ g.writeln ('static inline int v_prealloc_atomic_store_i32(int *ptr, int val) { return (int)__atomic_exchange_4((u32*)ptr, (u32)val, 5); }' )
3019+ g.writeln ('static inline int v_prealloc_atomic_cas_i32(int *ptr, int expected, int desired) { u32 e = (u32)expected; return __atomic_compare_exchange_4((u32*)ptr, &e, (u32)desired, 5, 5); }' )
3020+ g.writeln ('#else' )
3021+ g.writeln ('static inline int v_prealloc_atomic_store_i32(int *ptr, int val) { return __atomic_exchange_n(ptr, val, 5); }' )
3022+ g.writeln ('static inline int v_prealloc_atomic_cas_i32(int *ptr, int expected, int desired) { return __atomic_compare_exchange_n(ptr, &expected, desired, 0, 5, 5); }' )
3023+ g.writeln ('#endif' )
3024+ }
3025+
3026+ fn (mut g FlatGen) atomic_builtin_compat_decls () {
3027+ if g.has_stdatomic_compat_header {
29593028 return
29603029 }
2961- g.libc_compat_decls ()
2962- g.writeln ('#define array_new(elem_size, len, cap) __new_array((len), (cap), (elem_size))' )
2963- g.writeln ('#define array_push array__push' )
2964- g.writeln ('void array__push_many(array* a, void* val, int size);' )
2965- g.writeln ('#define array_push_many_ptr(a, val, size) array__push_many((a), (void*)(val), (size))' )
2966- g.writeln ('#define array_get array__get' )
2967- g.writeln ('#define array_set(a, i, ...) array__set(&(a), (i), __VA_ARGS__)' )
2968- g.writeln ('array array__clone(array* a);' )
2969- g.writeln ('#define array_slice array__slice' )
2970- g.writeln ('#define array_delete array__delete' )
2971- g.writeln ('#define array_ensure_cap array__ensure_cap' )
2972- g.writeln ('#define map__get_or_set map__get_and_set' )
2973- g.writeln ('#ifndef V_COMMIT_HASH' )
2974- g.writeln ('#define V_COMMIT_HASH ""' )
2975- g.writeln ('#endif' )
2976- // Weak fallbacks for the heap-tracking hooks. A program that provides real
2977- // implementations (e.g. a `vheap_alloc`/`vheap_free` from a linked C file, as
2978- // some projects do) overrides these without a redefinition/static-vs-non-static
2979- // clash against that file's own non-static prototype.
2980- g.writeln ('__attribute__((weak)) void vheap_alloc(void* p, u64 n) { (void)p; (void)n; }' )
2981- g.writeln ('__attribute__((weak)) void vheap_free(void* p) { (void)p; }' )
2982- // Atomic helpers. We use the C11 __atomic_* builtins (memory order 5 == __ATOMIC_SEQ_CST).
3030+ // Atomic helpers. We use compiler __atomic_* builtins (memory order 5 == __ATOMIC_SEQ_CST).
29833031 // clang/gcc inline the generic _n / RMW builtins. tcc only implements the inline
29843032 // __atomic_{add,sub,fetch}_* RMW builtins; for load/store/exchange/cas it has no generic
29853033 // _n form, so we route those to the sized __atomic_*_N libcalls (resolved from libc).
2986- g.writeln ('static inline int v_prealloc_atomic_add_i32(int *ptr, int delta) { return __atomic_add_fetch(ptr, delta, 5); }' )
2987- g.writeln ('static inline int v_prealloc_atomic_load_i32(int *ptr) { return __atomic_add_fetch(ptr, 0, 5); }' )
29883034 g.writeln ('static inline u32 atomic_fetch_add_u32(void* ptr, u32 delta) { return __atomic_fetch_add((u32*)ptr, delta, 5); }' )
29893035 g.writeln ('static inline u64 atomic_fetch_add_u64(void* ptr, u64 delta) { return __atomic_fetch_add((u64*)ptr, delta, 5); }' )
29903036 g.writeln ('static inline u64 atomic_fetch_sub_u64(void* ptr, u64 delta) { return __atomic_fetch_sub((u64*)ptr, delta, 5); }' )
@@ -2994,8 +3040,6 @@ fn (mut g FlatGen) builtin_abi_decls() {
29943040 g.writeln ('static inline u64 atomic_load_u64(void* ptr) { return __atomic_fetch_add((u64*)ptr, 0, 5); }' )
29953041 g.writeln ('static inline void* atomic_load_ptr(void* ptr) { return *(void* volatile*)ptr; }' )
29963042 g.writeln ('#ifdef __TINYC__' )
2997- g.writeln ('static inline int v_prealloc_atomic_store_i32(int *ptr, int val) { return (int)__atomic_exchange_4((u32*)ptr, (u32)val, 5); }' )
2998- g.writeln ('static inline int v_prealloc_atomic_cas_i32(int *ptr, int expected, int desired) { u32 e = (u32)expected; return __atomic_compare_exchange_4((u32*)ptr, &e, (u32)desired, 5, 5); }' )
29993043 g.writeln ('static inline void atomic_store_byte(void* ptr, byte val) { __atomic_store_1((byte*)ptr, val, 5); }' )
30003044 g.writeln ('static inline void atomic_store_u16(void* ptr, u16 val) { __atomic_store_2((u16*)ptr, val, 5); }' )
30013045 g.writeln ('static inline void atomic_store_u32(void* ptr, u32 val) { __atomic_store_4((u32*)ptr, val, 5); }' )
@@ -3017,8 +3061,6 @@ fn (mut g FlatGen) builtin_abi_decls() {
30173061 g.writeln ('static inline bool atomic_compare_exchange_weak_u32(void* ptr, u32* expected, u32 desired) { return __atomic_compare_exchange_4((u32*)ptr, expected, desired, 5, 5); }' )
30183062 g.writeln ('static inline bool atomic_compare_exchange_weak_u64(void* ptr, u64* expected, u64 desired) { return __atomic_compare_exchange_8((u64*)ptr, expected, desired, 5, 5); }' )
30193063 g.writeln ('#else' )
3020- g.writeln ('static inline int v_prealloc_atomic_store_i32(int *ptr, int val) { return __atomic_exchange_n(ptr, val, 5); }' )
3021- g.writeln ('static inline int v_prealloc_atomic_cas_i32(int *ptr, int expected, int desired) { return __atomic_compare_exchange_n(ptr, &expected, desired, 0, 5, 5); }' )
30223064 g.writeln ('static inline void atomic_store_byte(void* ptr, byte val) { __atomic_store_n((byte*)ptr, val, 5); }' )
30233065 g.writeln ('static inline void atomic_store_u16(void* ptr, u16 val) { __atomic_store_n((u16*)ptr, val, 5); }' )
30243066 g.writeln ('static inline void atomic_store_u32(void* ptr, u32 val) { __atomic_store_n((u32*)ptr, val, 5); }' )
@@ -3034,6 +3076,36 @@ fn (mut g FlatGen) builtin_abi_decls() {
30343076 g.writeln ('#endif' )
30353077 g.writeln ('static inline bool atomic_compare_exchange_weak_ptr(void* ptr, void* expected, ptrdiff_t desired) { return atomic_compare_exchange_strong_ptr(ptr, expected, desired); }' )
30363078 g.writeln ('static inline void cpu_relax(void) { __asm__ __volatile__("" ::: "memory"); }' )
3079+ }
3080+
3081+ fn (mut g FlatGen) builtin_abi_decls () {
3082+ if ! g.has_builtins {
3083+ return
3084+ }
3085+ g.libc_compat_decls ()
3086+ g.writeln ('#define array_new(elem_size, len, cap) __new_array((len), (cap), (elem_size))' )
3087+ g.writeln ('#define array_push array__push' )
3088+ g.writeln ('void array__push_many(array* a, void* val, int size);' )
3089+ g.writeln ('#define array_push_many_ptr(a, val, size) array__push_many((a), (void*)(val), (size))' )
3090+ g.writeln ('#define array_get array__get' )
3091+ g.writeln ('#define array_set(a, i, ...) array__set(&(a), (i), __VA_ARGS__)' )
3092+ g.writeln ('array array__clone(array* a);' )
3093+ g.writeln ('#define array_slice array__slice' )
3094+ g.writeln ('#define array_delete array__delete' )
3095+ g.writeln ('#define array_ensure_cap array__ensure_cap' )
3096+ g.writeln ('#define map__get_or_set map__get_and_set' )
3097+ g.writeln ('#ifndef V_COMMIT_HASH' )
3098+ g.writeln ('#define V_COMMIT_HASH ""' )
3099+ g.writeln ('#endif' )
3100+ // Weak fallbacks for the heap-tracking hooks. A program that provides real
3101+ // implementations (e.g. a `vheap_alloc`/`vheap_free` from a linked C file, as
3102+ // some projects do) overrides these without a redefinition/static-vs-non-static
3103+ // clash against that file's own non-static prototype.
3104+ g.writeln ('__attribute__((weak)) void vheap_alloc(void* p, u64 n) { (void)p; (void)n; }' )
3105+ g.writeln ('__attribute__((weak)) void vheap_free(void* p) { (void)p; }' )
3106+ g.filelock_compat_decls ()
3107+ g.prealloc_atomic_compat_decls ()
3108+ g.atomic_builtin_compat_decls ()
30373109 g.writeln ('static inline double math__abs(double a) { return a < 0 ? -a : a; }' )
30383110 g.writeln ('static inline double math__min(double a, double b) { return a < b ? a : b; }' )
30393111 g.writeln ('static const u64 _wyp[4] = {0x2d358dccaa6c78a5ull, 0x8bb84b93962eacc9ull, 0x4b33a62ed433d4a3ull, 0x4d5a2da51de1aa47ull};' )
@@ -3064,6 +3136,51 @@ fn (mut g FlatGen) builtin_abi_decls() {
30643136 g.writeln ('' )
30653137}
30663138
3139+ fn (mut g FlatGen) filelock_compat_decls () {
3140+ g.writeln ('#ifdef _WIN32' )
3141+ g.writeln ('#include <windows.h>' )
3142+ g.writeln ('int v_filelock_lock(void* handle, int exclusive, int immediate, u64 start, u64 len) {' )
3143+ g.writeln ('\t OVERLAPPED overlap;' )
3144+ g.writeln ('\t memset(&overlap, 0, sizeof(overlap));' )
3145+ g.writeln ('\t overlap.Offset = (DWORD)(start & 0xffffffffULL);' )
3146+ g.writeln ('\t overlap.OffsetHigh = (DWORD)(start >> 32);' )
3147+ g.writeln ('\t DWORD flags = immediate ? LOCKFILE_FAIL_IMMEDIATELY : 0;' )
3148+ g.writeln ('\t if (exclusive) { flags |= LOCKFILE_EXCLUSIVE_LOCK; }' )
3149+ g.writeln ('\t DWORD low = len == 0 ? MAXDWORD : (DWORD)(len & 0xffffffffULL);' )
3150+ g.writeln ('\t DWORD high = len == 0 ? MAXDWORD : (DWORD)(len >> 32);' )
3151+ g.writeln ('\t return LockFileEx((HANDLE)handle, flags, 0, low, high, &overlap) ? 0 : -1;' )
3152+ g.writeln ('}' )
3153+ g.writeln ('int v_filelock_unlock(void* handle, u64 start, u64 len) {' )
3154+ g.writeln ('\t OVERLAPPED overlap;' )
3155+ g.writeln ('\t memset(&overlap, 0, sizeof(overlap));' )
3156+ g.writeln ('\t overlap.Offset = (DWORD)(start & 0xffffffffULL);' )
3157+ g.writeln ('\t overlap.OffsetHigh = (DWORD)(start >> 32);' )
3158+ g.writeln ('\t DWORD low = len == 0 ? MAXDWORD : (DWORD)(len & 0xffffffffULL);' )
3159+ g.writeln ('\t DWORD high = len == 0 ? MAXDWORD : (DWORD)(len >> 32);' )
3160+ g.writeln ('\t return UnlockFileEx((HANDLE)handle, 0, low, high, &overlap) ? 0 : -1;' )
3161+ g.writeln ('}' )
3162+ g.writeln ('#else' )
3163+ g.writeln ('int v_filelock_lock(i32 fd, i32 exclusive, i32 immediate, u64 start, u64 len) {' )
3164+ g.writeln ('\t struct flock fl;' )
3165+ g.writeln ('\t memset(&fl, 0, sizeof(fl));' )
3166+ g.writeln ('\t fl.l_type = exclusive ? F_WRLCK : F_RDLCK;' )
3167+ g.writeln ('\t fl.l_whence = SEEK_SET;' )
3168+ g.writeln ('\t fl.l_start = (off_t)start;' )
3169+ g.writeln ('\t fl.l_len = len == 0 ? 0 : (off_t)len;' )
3170+ g.writeln ('\t return fcntl(fd, immediate ? F_SETLK : F_SETLKW, &fl);' )
3171+ g.writeln ('}' )
3172+ g.writeln ('int v_filelock_unlock(i32 fd, u64 start, u64 len) {' )
3173+ g.writeln ('\t struct flock fl;' )
3174+ g.writeln ('\t memset(&fl, 0, sizeof(fl));' )
3175+ g.writeln ('\t fl.l_type = F_UNLCK;' )
3176+ g.writeln ('\t fl.l_whence = SEEK_SET;' )
3177+ g.writeln ('\t fl.l_start = (off_t)start;' )
3178+ g.writeln ('\t fl.l_len = len == 0 ? 0 : (off_t)len;' )
3179+ g.writeln ('\t return fcntl(fd, F_SETLK, &fl);' )
3180+ g.writeln ('}' )
3181+ g.writeln ('#endif' )
3182+ }
3183+
30673184fn (mut g FlatGen) collect_fixed_array_typedefs_needed () map [string ]FixedArrayTypedefInfo {
30683185 mut needed := map [string ]FixedArrayTypedefInfo{}
30693186 old_module := g.tc.cur_module
0 commit comments