Skip to content

Commit 41b91cb

Browse files
committed
v3: add c99 mode
1 parent 410a66c commit 41b91cb

10 files changed

Lines changed: 395 additions & 58 deletions

File tree

vlib/v3/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ The table uses the first v3-generated C stage, `./v3 -parallel -o v4 v3.v`.
218218
Bundled TCC currently rejects the atomic helper shims on macOS and the driver
219219
falls back to `cc`.
220220

221+
Pass `-c99` to the v3 C backend to compile generated C and support objects as
222+
C99 (`cc -std=c99`) instead of the default GNU11 mode. `test_all.vsh -c99`
223+
validates the C backend and self-host chain in that mode, and skips the ARM64
224+
native backend step because `-c99` only applies to generated C.
225+
221226
| Phase | Time | Peak RSS |
222227
|----------------|----------:|---------:|
223228
| parse | 59.47 ms | 73 MB |

vlib/v3/gen/c/cleanc.v

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -499,10 +499,11 @@ fn (mut g FlatGen) collect_c_directive(module_name string, node flat.Node, sourc
499499
if include_arg.len == 0 {
500500
return true
501501
}
502-
// The atomic helper headers are superseded by the inline C11 `__atomic_*`
502+
// The atomic helper headers are superseded by the inline compiler `__atomic_*`
503503
// helpers emitted in builtin_abi_decls(); also including them would
504504
// redefine `v_prealloc_atomic_*` and the `atomic_*` helpers.
505-
if include_arg.contains('prealloc_atomics.h') || include_arg.contains('stdatomic') {
505+
if include_arg.contains('prealloc_atomics.h') || include_arg.contains('stdatomic')
506+
|| include_arg.contains('filelock_helpers.h') {
506507
return true
507508
}
508509
g.add_c_directive(module_name, '#include ${include_arg}', before_import)
@@ -2823,6 +2824,7 @@ fn (mut g FlatGen) preamble() {
28232824
g.writeln('#include <stdint.h>') // guarantees UINTPTR_MAX for the pointer-width atomic helpers
28242825
g.writeln('#include <math.h>')
28252826
g.writeln('#include <unistd.h>')
2827+
g.c99_atomic_compat_decls()
28262828
if g.has_builtins {
28272829
g.writeln('#include <time.h>')
28282830
g.writeln('#include <sys/time.h>')
@@ -2836,7 +2838,6 @@ fn (mut g FlatGen) preamble() {
28362838
g.writeln('#include <sys/utsname.h>')
28372839
g.writeln('#include <pthread.h>')
28382840
g.writeln('#include <semaphore.h>')
2839-
g.writeln('#include <stdatomic.h>')
28402841
g.writeln('#include <termios.h>')
28412842
g.writeln('#include <unistd.h>')
28422843
g.writeln('#include <arpa/inet.h>')
@@ -2904,6 +2905,21 @@ fn (mut g FlatGen) preamble() {
29042905
g.writeln('')
29052906
}
29062907

2908+
fn (mut g FlatGen) c99_atomic_compat_decls() {
2909+
g.writeln('typedef volatile uintptr_t atomic_uintptr_t;')
2910+
g.writeln('#ifndef memory_order_relaxed')
2911+
g.writeln('#define memory_order_relaxed 0')
2912+
g.writeln('#define memory_order_consume 1')
2913+
g.writeln('#define memory_order_acquire 2')
2914+
g.writeln('#define memory_order_release 3')
2915+
g.writeln('#define memory_order_acq_rel 4')
2916+
g.writeln('#define memory_order_seq_cst 5')
2917+
g.writeln('#endif')
2918+
g.writeln('#ifndef atomic_thread_fence')
2919+
g.writeln('#define atomic_thread_fence(order) __sync_synchronize()')
2920+
g.writeln('#endif')
2921+
}
2922+
29072923
fn (mut g FlatGen) write_arch_macros() {
29082924
g.writeln('#ifndef __V_architecture')
29092925
g.writeln('#define __V_architecture 0')
@@ -2979,7 +2995,8 @@ fn (mut g FlatGen) builtin_abi_decls() {
29792995
// clash against that file's own non-static prototype.
29802996
g.writeln('__attribute__((weak)) void vheap_alloc(void* p, u64 n) { (void)p; (void)n; }')
29812997
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).
2998+
g.filelock_compat_decls()
2999+
// Atomic helpers. We use compiler __atomic_* builtins (memory order 5 == __ATOMIC_SEQ_CST).
29833000
// clang/gcc inline the generic _n / RMW builtins. tcc only implements the inline
29843001
// __atomic_{add,sub,fetch}_* RMW builtins; for load/store/exchange/cas it has no generic
29853002
// _n form, so we route those to the sized __atomic_*_N libcalls (resolved from libc).
@@ -3064,6 +3081,51 @@ fn (mut g FlatGen) builtin_abi_decls() {
30643081
g.writeln('')
30653082
}
30663083

3084+
fn (mut g FlatGen) filelock_compat_decls() {
3085+
g.writeln('#ifdef _WIN32')
3086+
g.writeln('#include <windows.h>')
3087+
g.writeln('int v_filelock_lock(void* handle, int exclusive, int immediate, u64 start, u64 len) {')
3088+
g.writeln('\tOVERLAPPED overlap;')
3089+
g.writeln('\tmemset(&overlap, 0, sizeof(overlap));')
3090+
g.writeln('\toverlap.Offset = (DWORD)(start & 0xffffffffULL);')
3091+
g.writeln('\toverlap.OffsetHigh = (DWORD)(start >> 32);')
3092+
g.writeln('\tDWORD flags = immediate ? LOCKFILE_FAIL_IMMEDIATELY : 0;')
3093+
g.writeln('\tif (exclusive) { flags |= LOCKFILE_EXCLUSIVE_LOCK; }')
3094+
g.writeln('\tDWORD low = len == 0 ? MAXDWORD : (DWORD)(len & 0xffffffffULL);')
3095+
g.writeln('\tDWORD high = len == 0 ? MAXDWORD : (DWORD)(len >> 32);')
3096+
g.writeln('\treturn LockFileEx((HANDLE)handle, flags, 0, low, high, &overlap) ? 0 : -1;')
3097+
g.writeln('}')
3098+
g.writeln('int v_filelock_unlock(void* handle, u64 start, u64 len) {')
3099+
g.writeln('\tOVERLAPPED overlap;')
3100+
g.writeln('\tmemset(&overlap, 0, sizeof(overlap));')
3101+
g.writeln('\toverlap.Offset = (DWORD)(start & 0xffffffffULL);')
3102+
g.writeln('\toverlap.OffsetHigh = (DWORD)(start >> 32);')
3103+
g.writeln('\tDWORD low = len == 0 ? MAXDWORD : (DWORD)(len & 0xffffffffULL);')
3104+
g.writeln('\tDWORD high = len == 0 ? MAXDWORD : (DWORD)(len >> 32);')
3105+
g.writeln('\treturn UnlockFileEx((HANDLE)handle, 0, low, high, &overlap) ? 0 : -1;')
3106+
g.writeln('}')
3107+
g.writeln('#else')
3108+
g.writeln('int v_filelock_lock(i32 fd, i32 exclusive, i32 immediate, u64 start, u64 len) {')
3109+
g.writeln('\tstruct flock fl;')
3110+
g.writeln('\tmemset(&fl, 0, sizeof(fl));')
3111+
g.writeln('\tfl.l_type = exclusive ? F_WRLCK : F_RDLCK;')
3112+
g.writeln('\tfl.l_whence = SEEK_SET;')
3113+
g.writeln('\tfl.l_start = (off_t)start;')
3114+
g.writeln('\tfl.l_len = len == 0 ? 0 : (off_t)len;')
3115+
g.writeln('\treturn fcntl(fd, immediate ? F_SETLK : F_SETLKW, &fl);')
3116+
g.writeln('}')
3117+
g.writeln('int v_filelock_unlock(i32 fd, u64 start, u64 len) {')
3118+
g.writeln('\tstruct flock fl;')
3119+
g.writeln('\tmemset(&fl, 0, sizeof(fl));')
3120+
g.writeln('\tfl.l_type = F_UNLCK;')
3121+
g.writeln('\tfl.l_whence = SEEK_SET;')
3122+
g.writeln('\tfl.l_start = (off_t)start;')
3123+
g.writeln('\tfl.l_len = len == 0 ? 0 : (off_t)len;')
3124+
g.writeln('\treturn fcntl(fd, F_SETLK, &fl);')
3125+
g.writeln('}')
3126+
g.writeln('#endif')
3127+
}
3128+
30673129
fn (mut g FlatGen) collect_fixed_array_typedefs_needed() map[string]FixedArrayTypedefInfo {
30683130
mut needed := map[string]FixedArrayTypedefInfo{}
30693131
old_module := g.tc.cur_module

vlib/v3/gen/c/stmt.v

Lines changed: 78 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,8 @@ fn (mut g FlatGen) gen_node(id flat.NodeId) {
265265
if g.optional_result_matches_base(raw_expr_type, base)
266266
|| g.optional_result_matches_base(expr_type, base)
267267
|| g.optional_result_matches_base(call_ret_type, base)
268-
|| g.optional_result_matches_base(decl_ret_type, base) {
268+
|| g.optional_result_matches_base(decl_ret_type, base)
269+
|| g.clone_call_matches_base(ret_node, base) {
269270
g.write('return ')
270271
g.gen_expr(ret_id)
271272
g.writeln(';')
@@ -565,7 +566,8 @@ fn (mut g FlatGen) return_expr_string(node flat.Node, ret_id flat.NodeId, ret_no
565566
if g.optional_result_matches_base(raw_expr_type, base)
566567
|| g.optional_result_matches_base(expr_type, base)
567568
|| g.optional_result_matches_base(call_ret_type, base)
568-
|| g.optional_result_matches_base(decl_ret_type, base) {
569+
|| g.optional_result_matches_base(decl_ret_type, base)
570+
|| g.clone_call_matches_base(ret_node, base) {
569571
return g.expr_to_string(ret_id)
570572
}
571573
mut expr_value_type := expr_type
@@ -737,8 +739,16 @@ fn (g &FlatGen) selector_call_return_type(fn_node flat.Node) ?types.Type {
737739
}
738740
}
739741
}
740-
base_type := g.tc.resolve_type(base_id)
742+
base_type0 := g.usable_expr_type(base_id)
743+
base_type := if base_type0 is types.Unknown || base_type0 is types.Void {
744+
g.tc.resolve_type(base_id)
745+
} else {
746+
base_type0
747+
}
741748
clean_type := types.unwrap_pointer(base_type)
749+
if fn_node.value == 'clone' && (clean_type is types.Array || clean_type is types.Map) {
750+
return base_type
751+
}
742752
mut receiver_name := clean_type.name()
743753
if clean_type is types.Struct {
744754
receiver_name = clean_type.name
@@ -848,6 +858,27 @@ fn (g &FlatGen) optional_result_matches_base(expr_type types.Type, base types.Ty
848858
return g.option_c_name_for_base(inner) == g.option_c_name_for_base(base)
849859
}
850860

861+
fn (g &FlatGen) clone_call_matches_base(call_node flat.Node, base types.Type) bool {
862+
if call_node.kind != .call || call_node.children_count == 0 {
863+
return false
864+
}
865+
fn_node := g.a.child_node(&call_node, 0)
866+
if fn_node.kind != .selector || fn_node.value != 'clone' || fn_node.children_count == 0 {
867+
return false
868+
}
869+
base_id := g.a.child(fn_node, 0)
870+
clean_receiver := types.unwrap_pointer(g.tc.resolve_type(base_id))
871+
clean_base := types.unwrap_pointer(base)
872+
if clean_receiver is types.Array && clean_base is types.Array {
873+
return g.type_names_match(clean_receiver.elem_type, clean_base.elem_type)
874+
}
875+
if clean_receiver is types.Map && clean_base is types.Map {
876+
return g.type_names_match(clean_receiver.key_type, clean_base.key_type)
877+
&& g.type_names_match(clean_receiver.value_type, clean_base.value_type)
878+
}
879+
return false
880+
}
881+
851882
// option_c_name_for_base returns the C optional type name used for a `?base`/`!base`
852883
// value, mirroring optional_type_name without its side effects.
853884
fn (g &FlatGen) option_c_name_for_base(base types.Type) string {
@@ -911,6 +942,9 @@ fn (g &FlatGen) expr_is_nil_value(id flat.NodeId) bool {
911942
fn (g &FlatGen) usable_expr_type(id flat.NodeId) types.Type {
912943
if int(id) >= 0 && int(id) < g.a.nodes.len {
913944
node := g.a.nodes[int(id)]
945+
if node.kind in [.expr_stmt, .paren] && node.children_count > 0 {
946+
return g.usable_expr_type(g.a.child(&node, 0))
947+
}
914948
if node.kind == .ident {
915949
if typ := g.cur_param_types[node.value] {
916950
return typ
@@ -952,6 +986,22 @@ fn (g &FlatGen) usable_expr_type(id flat.NodeId) types.Type {
952986
return types.Type(types.u8_)
953987
}
954988
}
989+
if node.kind == .call && node.children_count > 0 {
990+
fn_node := g.a.child_node(&node, 0)
991+
if fn_node.kind == .ident {
992+
if typ := g.tc.cur_scope.lookup(fn_node.value) {
993+
ret := fn_type_return_type(typ)
994+
if ret !is types.Unknown && ret !is types.Void {
995+
return ret
996+
}
997+
}
998+
if ret := g.fn_decl_return_type_for_call_name(fn_node.value) {
999+
if ret !is types.Unknown && ret !is types.Void {
1000+
return ret
1001+
}
1002+
}
1003+
}
1004+
}
9551005
}
9561006
if typ := g.tc.expr_type(id) {
9571007
if typ !is types.Unknown && typ !is types.Void {
@@ -1041,11 +1091,22 @@ fn (g &FlatGen) is_runtime_array_flags_stmt(id flat.NodeId) bool {
10411091
return owner_type is types.Array || owner_type.name() == 'strings.Builder'
10421092
}
10431093

1094+
fn (g &FlatGen) multi_return_expr_type(id flat.NodeId) ?types.MultiReturn {
1095+
rtype := g.tc.resolve_type(id)
1096+
if rtype is types.MultiReturn {
1097+
return rtype
1098+
}
1099+
utype := g.usable_expr_type(id)
1100+
if utype is types.MultiReturn {
1101+
return utype
1102+
}
1103+
return none
1104+
}
1105+
10441106
// gen_decl_assign emits decl assign output for c.
10451107
fn (mut g FlatGen) gen_decl_assign(node flat.Node) {
10461108
if node.children_count >= 3 {
1047-
rhs_type := g.tc.resolve_type(g.a.child(&node, 1))
1048-
if rhs_type is types.MultiReturn {
1109+
if _ := g.multi_return_expr_type(g.a.child(&node, 1)) {
10491110
g.gen_multi_return_decl(node)
10501111
return
10511112
}
@@ -1325,17 +1386,15 @@ fn (mut g FlatGen) gen_fixed_array_copy_from_node(dst string, rhs_id flat.NodeId
13251386

13261387
fn (mut g FlatGen) gen_multi_return_decl(node flat.Node) {
13271388
rhs_id := g.a.child(&node, 1)
1328-
rhs_type := g.tc.resolve_type(rhs_id)
1389+
rhs_multi := g.multi_return_expr_type(rhs_id) or { return }
1390+
rhs_type := types.Type(rhs_multi)
13291391
ct := g.tc.c_type(rhs_type)
13301392
tmp := g.tmp_name()
13311393
g.write('${ct} ${tmp} = ')
13321394
g.gen_expr_with_expected_type(rhs_id, rhs_type)
13331395
g.writeln(';')
13341396
num_lhs := node.children_count - 1
1335-
mut multi_types := []types.Type{}
1336-
if rhs_type is types.MultiReturn {
1337-
multi_types = rhs_type.types.clone()
1338-
}
1397+
multi_types := rhs_multi.types.clone()
13391398
for j in 0 .. num_lhs {
13401399
lhs_idx := if j == 0 { 0 } else { j + 1 }
13411400
lhs_id := g.a.child(&node, lhs_idx)
@@ -1376,8 +1435,7 @@ fn (mut g FlatGen) gen_multi_return_decl(node flat.Node) {
13761435
fn (mut g FlatGen) gen_assign(node flat.Node) {
13771436
if node.children_count >= 3 {
13781437
rhs_id := g.a.child(&node, 1)
1379-
rhs_type := g.tc.resolve_type(rhs_id)
1380-
if rhs_type is types.MultiReturn {
1438+
if _ := g.multi_return_expr_type(rhs_id) {
13811439
g.gen_multi_return_assign(node)
13821440
return
13831441
}
@@ -1427,6 +1485,11 @@ fn (mut g FlatGen) gen_assign(node flat.Node) {
14271485
g.gen_expr(g.a.child(&node, i + 1))
14281486
g.writeln(';')
14291487
}
1488+
} else {
1489+
g.gen_expr(g.a.child(&node, i))
1490+
g.write(' <<= ')
1491+
g.gen_expr(g.a.child(&node, i + 1))
1492+
g.writeln(';')
14301493
}
14311494
} else {
14321495
rhs_id := g.a.child(&node, i + 1)
@@ -1601,17 +1664,15 @@ fn (g &FlatGen) assign_lhs_needs_deref(lhs_id flat.NodeId, lhs_type types.Type,
16011664
// gen_multi_return_assign emits multi return assign output for c.
16021665
fn (mut g FlatGen) gen_multi_return_assign(node flat.Node) {
16031666
rhs_id := g.a.child(&node, 1)
1604-
rhs_type := g.tc.resolve_type(rhs_id)
1667+
rhs_multi := g.multi_return_expr_type(rhs_id) or { return }
1668+
rhs_type := types.Type(rhs_multi)
16051669
ct := g.tc.c_type(rhs_type)
16061670
tmp := g.tmp_name()
16071671
g.write('${ct} ${tmp} = ')
16081672
g.gen_expr_with_expected_type(rhs_id, rhs_type)
16091673
g.writeln(';')
16101674
num_lhs := node.children_count - 1
1611-
mut multi_types := []types.Type{}
1612-
if rhs_type is types.MultiReturn {
1613-
multi_types = rhs_type.types.clone()
1614-
}
1675+
multi_types := rhs_multi.types.clone()
16151676
for j in 0 .. num_lhs {
16161677
lhs_idx := if j == 0 { 0 } else { j + 1 }
16171678
lhs_id := g.a.child(&node, lhs_idx)

0 commit comments

Comments
 (0)