Skip to content

Commit 3070538

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

12 files changed

Lines changed: 484 additions & 74 deletions

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: 117 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,7 @@ fn (mut g FlatGen) gen_node(id flat.NodeId) {
289289
&& !g.type_can_wrap_as_sum(expr_value_type, base)
290290
&& !g.types_numeric_compatible(expr_value_type, base)
291291
&& !g.call_constructs_type(ret_id, base)
292+
&& !g.clone_call_matches_base(ret_node, base)
292293
&& expr_value_type !is types.Primitive
293294
&& expr_value_type !is types.Unknown {
294295
g.writeln('return (${ct}){.ok = false};')
@@ -586,8 +587,8 @@ fn (mut g FlatGen) return_expr_string(node flat.Node, ret_id flat.NodeId, ret_no
586587
&& !g.expr_is_nil_pointer_payload(ret_id, base)
587588
&& !g.type_can_wrap_as_sum(expr_value_type, base)
588589
&& !g.types_numeric_compatible(expr_value_type, base)
589-
&& !g.call_constructs_type(ret_id, base) && expr_value_type !is types.Primitive
590-
&& expr_value_type !is types.Unknown {
590+
&& !g.call_constructs_type(ret_id, base) && !g.clone_call_matches_base(ret_node, base)
591+
&& expr_value_type !is types.Primitive && expr_value_type !is types.Unknown {
591592
return '(${ct}){.ok = false}'
592593
}
593594
return '(${ct}){.ok = true, .value = ${g.expr_to_string_with_expected_type(ret_id, base)}}'
@@ -737,8 +738,16 @@ fn (g &FlatGen) selector_call_return_type(fn_node flat.Node) ?types.Type {
737738
}
738739
}
739740
}
740-
base_type := g.tc.resolve_type(base_id)
741+
base_type0 := g.usable_expr_type(base_id)
742+
base_type := if base_type0 is types.Unknown || base_type0 is types.Void {
743+
g.tc.resolve_type(base_id)
744+
} else {
745+
base_type0
746+
}
741747
clean_type := types.unwrap_pointer(base_type)
748+
if fn_node.value == 'clone' && (clean_type is types.Array || clean_type is types.Map) {
749+
return base_type
750+
}
742751
mut receiver_name := clean_type.name()
743752
if clean_type is types.Struct {
744753
receiver_name = clean_type.name
@@ -848,6 +857,67 @@ fn (g &FlatGen) optional_result_matches_base(expr_type types.Type, base types.Ty
848857
return g.option_c_name_for_base(inner) == g.option_c_name_for_base(base)
849858
}
850859

860+
fn (g &FlatGen) clone_call_matches_base(call_node flat.Node, base types.Type) bool {
861+
mut node := call_node
862+
for node.kind in [.expr_stmt, .paren] && node.children_count > 0 {
863+
node = *g.a.child_node(&node, 0)
864+
}
865+
if node.kind != .call || node.children_count == 0 {
866+
return false
867+
}
868+
fn_node := g.a.child_node(&node, 0)
869+
if fn_node.kind == .ident {
870+
base_ct := g.tc.c_type(types.unwrap_pointer(base))
871+
return (fn_node.value == 'array__clone' && base_ct == 'Array')
872+
|| (fn_node.value == 'map__clone' && base_ct == 'map')
873+
}
874+
if fn_node.kind != .selector || fn_node.value != 'clone' || fn_node.children_count == 0 {
875+
return false
876+
}
877+
base_id := g.a.child(fn_node, 0)
878+
receiver_type0 := g.usable_expr_type(base_id)
879+
receiver_type := if receiver_type0 is types.Unknown || receiver_type0 is types.Void {
880+
g.tc.resolve_type(base_id)
881+
} else {
882+
receiver_type0
883+
}
884+
clean_receiver := types.unwrap_pointer(receiver_type)
885+
clean_base := types.unwrap_pointer(base)
886+
if g.type_names_match(clean_receiver, clean_base) {
887+
return true
888+
}
889+
receiver_ct0 := g.tc.c_type(clean_receiver)
890+
base_ct0 := g.tc.c_type(clean_base)
891+
if receiver_ct0.len > 0 && base_ct0.len > 0 && receiver_ct0 == base_ct0 {
892+
return true
893+
}
894+
receiver := if clean_receiver is types.Alias {
895+
clean_receiver.base_type
896+
} else {
897+
clean_receiver
898+
}
899+
expected := if clean_base is types.Alias {
900+
clean_base.base_type
901+
} else {
902+
clean_base
903+
}
904+
if expected is types.Array || expected is types.Map {
905+
receiver_ct := g.tc.c_type(receiver)
906+
expected_ct := g.tc.c_type(expected)
907+
if receiver_ct.len > 0 && receiver_ct == expected_ct {
908+
return true
909+
}
910+
}
911+
if receiver is types.Array && expected is types.Array {
912+
return g.type_names_match(receiver.elem_type, expected.elem_type)
913+
}
914+
if receiver is types.Map && expected is types.Map {
915+
return g.type_names_match(receiver.key_type, expected.key_type)
916+
&& g.type_names_match(receiver.value_type, expected.value_type)
917+
}
918+
return false
919+
}
920+
851921
// option_c_name_for_base returns the C optional type name used for a `?base`/`!base`
852922
// value, mirroring optional_type_name without its side effects.
853923
fn (g &FlatGen) option_c_name_for_base(base types.Type) string {
@@ -911,6 +981,9 @@ fn (g &FlatGen) expr_is_nil_value(id flat.NodeId) bool {
911981
fn (g &FlatGen) usable_expr_type(id flat.NodeId) types.Type {
912982
if int(id) >= 0 && int(id) < g.a.nodes.len {
913983
node := g.a.nodes[int(id)]
984+
if node.kind in [.expr_stmt, .paren] && node.children_count > 0 {
985+
return g.usable_expr_type(g.a.child(&node, 0))
986+
}
914987
if node.kind == .ident {
915988
if typ := g.cur_param_types[node.value] {
916989
return typ
@@ -952,6 +1025,22 @@ fn (g &FlatGen) usable_expr_type(id flat.NodeId) types.Type {
9521025
return types.Type(types.u8_)
9531026
}
9541027
}
1028+
if node.kind == .call && node.children_count > 0 {
1029+
fn_node := g.a.child_node(&node, 0)
1030+
if fn_node.kind == .ident {
1031+
if typ := g.tc.cur_scope.lookup(fn_node.value) {
1032+
ret := fn_type_return_type(typ)
1033+
if ret !is types.Unknown && ret !is types.Void {
1034+
return ret
1035+
}
1036+
}
1037+
if ret := g.fn_decl_return_type_for_call_name(fn_node.value) {
1038+
if ret !is types.Unknown && ret !is types.Void {
1039+
return ret
1040+
}
1041+
}
1042+
}
1043+
}
9551044
}
9561045
if typ := g.tc.expr_type(id) {
9571046
if typ !is types.Unknown && typ !is types.Void {
@@ -1041,11 +1130,22 @@ fn (g &FlatGen) is_runtime_array_flags_stmt(id flat.NodeId) bool {
10411130
return owner_type is types.Array || owner_type.name() == 'strings.Builder'
10421131
}
10431132

1133+
fn (g &FlatGen) multi_return_expr_type(id flat.NodeId) ?types.MultiReturn {
1134+
rtype := g.tc.resolve_type(id)
1135+
if rtype is types.MultiReturn {
1136+
return rtype
1137+
}
1138+
utype := g.usable_expr_type(id)
1139+
if utype is types.MultiReturn {
1140+
return utype
1141+
}
1142+
return none
1143+
}
1144+
10441145
// gen_decl_assign emits decl assign output for c.
10451146
fn (mut g FlatGen) gen_decl_assign(node flat.Node) {
10461147
if node.children_count >= 3 {
1047-
rhs_type := g.tc.resolve_type(g.a.child(&node, 1))
1048-
if rhs_type is types.MultiReturn {
1148+
if _ := g.multi_return_expr_type(g.a.child(&node, 1)) {
10491149
g.gen_multi_return_decl(node)
10501150
return
10511151
}
@@ -1325,17 +1425,15 @@ fn (mut g FlatGen) gen_fixed_array_copy_from_node(dst string, rhs_id flat.NodeId
13251425

13261426
fn (mut g FlatGen) gen_multi_return_decl(node flat.Node) {
13271427
rhs_id := g.a.child(&node, 1)
1328-
rhs_type := g.tc.resolve_type(rhs_id)
1428+
rhs_multi := g.multi_return_expr_type(rhs_id) or { return }
1429+
rhs_type := types.Type(rhs_multi)
13291430
ct := g.tc.c_type(rhs_type)
13301431
tmp := g.tmp_name()
13311432
g.write('${ct} ${tmp} = ')
13321433
g.gen_expr_with_expected_type(rhs_id, rhs_type)
13331434
g.writeln(';')
13341435
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-
}
1436+
multi_types := rhs_multi.types.clone()
13391437
for j in 0 .. num_lhs {
13401438
lhs_idx := if j == 0 { 0 } else { j + 1 }
13411439
lhs_id := g.a.child(&node, lhs_idx)
@@ -1376,8 +1474,7 @@ fn (mut g FlatGen) gen_multi_return_decl(node flat.Node) {
13761474
fn (mut g FlatGen) gen_assign(node flat.Node) {
13771475
if node.children_count >= 3 {
13781476
rhs_id := g.a.child(&node, 1)
1379-
rhs_type := g.tc.resolve_type(rhs_id)
1380-
if rhs_type is types.MultiReturn {
1477+
if _ := g.multi_return_expr_type(rhs_id) {
13811478
g.gen_multi_return_assign(node)
13821479
return
13831480
}
@@ -1427,6 +1524,11 @@ fn (mut g FlatGen) gen_assign(node flat.Node) {
14271524
g.gen_expr(g.a.child(&node, i + 1))
14281525
g.writeln(';')
14291526
}
1527+
} else {
1528+
g.gen_expr(g.a.child(&node, i))
1529+
g.write(' <<= ')
1530+
g.gen_expr(g.a.child(&node, i + 1))
1531+
g.writeln(';')
14301532
}
14311533
} else {
14321534
rhs_id := g.a.child(&node, i + 1)
@@ -1601,17 +1703,15 @@ fn (g &FlatGen) assign_lhs_needs_deref(lhs_id flat.NodeId, lhs_type types.Type,
16011703
// gen_multi_return_assign emits multi return assign output for c.
16021704
fn (mut g FlatGen) gen_multi_return_assign(node flat.Node) {
16031705
rhs_id := g.a.child(&node, 1)
1604-
rhs_type := g.tc.resolve_type(rhs_id)
1706+
rhs_multi := g.multi_return_expr_type(rhs_id) or { return }
1707+
rhs_type := types.Type(rhs_multi)
16051708
ct := g.tc.c_type(rhs_type)
16061709
tmp := g.tmp_name()
16071710
g.write('${ct} ${tmp} = ')
16081711
g.gen_expr_with_expected_type(rhs_id, rhs_type)
16091712
g.writeln(';')
16101713
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-
}
1714+
multi_types := rhs_multi.types.clone()
16151715
for j in 0 .. num_lhs {
16161716
lhs_idx := if j == 0 { 0 } else { j + 1 }
16171717
lhs_id := g.a.child(&node, lhs_idx)

0 commit comments

Comments
 (0)