diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5434d54419..29d90b6e8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -247,6 +247,8 @@ jobs: ./v2 -o v3 -usecache cmd/v ./v3 version ./v3 -o tetris -usecache examples/tetris/tetris.v + - name: V self compilation with -parallel-cc + ./v -o v2 -parallel-cc cmd/v - name: Test password input run: | ./v examples/password @@ -317,6 +319,8 @@ jobs: run: ./v -autofree -experimental -o tetris examples/tetris/tetris.v - name: Build option_test.v with -autofree run: ./v -autofree vlib/v/tests/option_test.v + - name: V self compilation with -parallel-cc + ./v -o v2 -parallel-cc cmd/v - name: Build modules run: | ./v build-module vlib/os diff --git a/vlib/encoding/csv/writer_test.v b/vlib/encoding/csv/writer_test.v index ffc3626538..4063785f33 100644 --- a/vlib/encoding/csv/writer_test.v +++ b/vlib/encoding/csv/writer_test.v @@ -8,6 +8,12 @@ fn test_encoding_csv_writer() { csv_writer.write(['sam', 'sam@likesham.com', '0433000000', 'needs, quoting']) or {} assert csv_writer.str() == 'name,email,phone,other\njoe,joe@blow.com,0400000000,test\nsam,sam@likesham.com,0433000000,"needs, quoting"\n' + + /* + mut csv_writer2 := csv.new_writer(delimiter:':') + csv_writer.write(['foo', 'bar', '2']) or {} + assert csv_writer.str() == 'foo:bar:2' + */ } fn test_encoding_csv_writer_delimiter() { diff --git a/vlib/v/builder/cbuilder/cbuilder.v b/vlib/v/builder/cbuilder/cbuilder.v index 48a551e942..580c98e5e6 100644 --- a/vlib/v/builder/cbuilder/cbuilder.v +++ b/vlib/v/builder/cbuilder/cbuilder.v @@ -31,7 +31,9 @@ pub fn compile_c(mut b builder.Builder) { out_name_c = b.get_vtmp_filename(b.pref.out_name, '.tmp.so.c') } build_c(mut b, files, out_name_c) - b.cc() + if !b.pref.parallel_cc { + b.cc() + } } pub fn gen_c(mut b builder.Builder, v_files []string) string { @@ -43,8 +45,15 @@ pub fn gen_c(mut b builder.Builder, v_files []string) string { } util.timing_start('C GEN') - res := c.gen(b.parsed_files, b.table, b.pref) + header, res, out_str, out_fn_start_pos := c.gen(b.parsed_files, b.table, b.pref) util.timing_measure('C GEN') + + if b.pref.parallel_cc { + util.timing_start('Parallel C compilation') + parallel_cc(mut b, header, res, out_str, out_fn_start_pos) + util.timing_measure('Parallel C compilation') + } + return res } diff --git a/vlib/v/builder/cbuilder/parallel_cc.v b/vlib/v/builder/cbuilder/parallel_cc.v new file mode 100644 index 0000000000..61c3b32f05 --- /dev/null +++ b/vlib/v/builder/cbuilder/parallel_cc.v @@ -0,0 +1,86 @@ +module cbuilder + +import os +import time +import sync +import v.util +import v.builder + +fn parallel_cc(mut b builder.Builder, header string, res string, out_str string, out_fn_start_pos []int) { + nr_jobs := util.nr_jobs + println('len=$nr_jobs') + out_h := header.replace_once('static char * v_typeof_interface_IError', 'char * v_typeof_interface_IError') + os.write_file('out.h', out_h) or { panic(err) } + // Write generated stuff in `g.out` before and after the `out_fn_start_pos` locations, + // like the `int main()` to "out_0.c" and "out_x.c" + out0 := out_str[..out_fn_start_pos[0]].replace_once('static char * v_typeof_interface_IError', + 'char * v_typeof_interface_IError') + os.write_file('out_0.c', '#include "out.h"\n' + out0) or { panic(err) } + os.write_file('out_x.c', '#include "out.h"\n' + out_str[out_fn_start_pos.last()..]) or { + panic(err) + } + + mut prev_fn_pos := 0 + mut out_files := []os.File{len: nr_jobs} + mut fnames := []string{} + for i in 0 .. nr_jobs { + fname := 'out_${i + 1}.c' + fnames << fname + out_files[i] = os.create(fname) or { panic(err) } + out_files[i].writeln('#include "out.h"\n') or { panic(err) } + } + // out_fn_start_pos.sort() + for i, fn_pos in out_fn_start_pos { + if prev_fn_pos >= out_str.len || fn_pos >= out_str.len || prev_fn_pos > fn_pos { + println('EXITING i=$i out of $out_fn_start_pos.len prev_pos=$prev_fn_pos fn_pos=$fn_pos') + break + } + if i == 0 { + // Skip typeof etc stuff that's been added to out_0.c + prev_fn_pos = fn_pos + continue + } + fn_text := out_str[prev_fn_pos..fn_pos] + out_files[i % nr_jobs].writeln(fn_text + '\n//////////////////////////////////////\n\n') or { + panic(err) + } + prev_fn_pos = fn_pos + } + for i in 0 .. nr_jobs { + out_files[i].close() + } + t := time.now() + mut wg := sync.new_waitgroup() + for i in ['0', 'x'] { + wg.add(1) + go build_o(i, mut wg) + } + for i in 0 .. nr_jobs { + wg.add(1) + go build_o((i + 1).str(), mut wg) + } + wg.wait() + println(time.now() - t) + link_cmd := '${os.quoted_path(cbuilder.cc_compiler)} -o v_parallel out_0.o ${fnames.map(it.replace('.c', + '.o')).join(' ')} out_x.o -lpthread $cbuilder.cc_ldflags' + link_res := os.execute(link_cmd) + println('> link_cmd: $link_cmd => $link_res.exit_code') + println(time.now() - t) + if link_res.exit_code != 0 { + println(link_res.output) + } +} + +fn build_o(postfix string, mut wg sync.WaitGroup) { + sw := time.new_stopwatch() + cmd := '${os.quoted_path(cbuilder.cc_compiler)} $cbuilder.cc_cflags -c -w -o out_${postfix}.o out_${postfix}.c' + res := os.execute(cmd) + wg.done() + println('cmd: `$cmd` => $res.exit_code , $sw.elapsed().milliseconds() ms') +} + +const cc_compiler = os.getenv_opt('CC') or { 'cc' } + +const cc_ldflags = os.getenv_opt('LDFLAGS') or { '' } + +const cc_cflags = os.getenv_opt('CFLAGS') or { '' } diff --git a/vlib/v/gen/c/array.v b/vlib/v/gen/c/array.v index 3528970072..76a85fd518 100644 --- a/vlib/v/gen/c/array.v +++ b/vlib/v/gen/c/array.v @@ -13,7 +13,7 @@ fn (mut g Gen) array_init(node ast.ArrayInit, var_name string) { is_amp := g.is_amp g.is_amp = false if is_amp { - g.out.go_back(1) // delete the `&` already generated in `prefix_expr() + g.go_back(1) // delete the `&` already generated in `prefix_expr() } if g.is_shared { shared_styp = g.typ(array_type.typ.set_flag(.shared_f)) @@ -481,7 +481,7 @@ fn (mut g Gen) gen_array_sort(node ast.CallExpr) { } stype_arg := g.typ(info.elem_type) - g.definitions.writeln('VV_LOCAL_SYMBOL int ${compare_fn}($stype_arg* a, $stype_arg* b) {') + g.definitions.writeln('VV_LOCAL_SYMBOL $g.static_modifier int ${compare_fn}($stype_arg* a, $stype_arg* b) {') c_condition := if comparison_type.sym.has_method('<') { '${g.typ(comparison_type.typ)}__lt($left_expr, $right_expr)' } else if comparison_type.unaliased_sym.has_method('<') { @@ -771,7 +771,7 @@ fn (mut g Gen) gen_array_contains(typ ast.Type, left ast.Expr, right ast.Expr) { g.write('${fn_name}(') g.write(strings.repeat(`*`, typ.nr_muls())) if typ.share() == .shared_t { - g.out.go_back(1) + g.go_back(1) } g.expr(left) if typ.share() == .shared_t { diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 8854ff62e0..15f4f11949 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -235,12 +235,12 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { g.expr(left) if g.is_arraymap_set && g.arraymap_set_pos > 0 { - g.out.go_back_to(g.arraymap_set_pos) + g.go_back_to(g.arraymap_set_pos) g.write(', &$v_var)') g.is_arraymap_set = false g.arraymap_set_pos = 0 } else { - g.out.go_back_to(pos) + g.go_back_to(pos) is_var_mut := !is_decl && left.is_auto_deref_var() addr_left := if is_var_mut { '' } else { '&' } g.writeln('') diff --git a/vlib/v/gen/c/auto_free_methods.v b/vlib/v/gen/c/auto_free_methods.v index da54f7cb65..a9dd14c9e4 100644 --- a/vlib/v/gen/c/auto_free_methods.v +++ b/vlib/v/gen/c/auto_free_methods.v @@ -65,12 +65,12 @@ fn (mut g Gen) gen_free_method(typ ast.Type) string { } fn (mut g Gen) gen_free_for_struct(info ast.Struct, styp string, fn_name string) { - g.definitions.writeln('void ${fn_name}($styp* it); // auto') + g.definitions.writeln('$g.static_modifier void ${fn_name}($styp* it); // auto') mut fn_builder := strings.new_builder(128) defer { g.auto_fn_definitions << fn_builder.str() } - fn_builder.writeln('void ${fn_name}($styp* it) {') + fn_builder.writeln('$g.static_modifier void ${fn_name}($styp* it) {') for field in info.fields { field_name := c_name(field.name) sym := g.table.sym(g.unwrap_generic(field.typ)) @@ -98,12 +98,12 @@ fn (mut g Gen) gen_free_for_struct(info ast.Struct, styp string, fn_name string) } fn (mut g Gen) gen_free_for_array(info ast.Array, styp string, fn_name string) { - g.definitions.writeln('void ${fn_name}($styp* it); // auto') + g.definitions.writeln('$g.static_modifier void ${fn_name}($styp* it); // auto') mut fn_builder := strings.new_builder(128) defer { g.auto_fn_definitions << fn_builder.str() } - fn_builder.writeln('void ${fn_name}($styp* it) {') + fn_builder.writeln('$g.static_modifier void ${fn_name}($styp* it) {') sym := g.table.sym(g.unwrap_generic(info.elem_type)) if sym.kind in [.string, .array, .map, .struct_] { @@ -123,12 +123,12 @@ fn (mut g Gen) gen_free_for_array(info ast.Array, styp string, fn_name string) { } fn (mut g Gen) gen_free_for_map(info ast.Map, styp string, fn_name string) { - g.definitions.writeln('void ${fn_name}($styp* it); // auto') + g.definitions.writeln('$g.static_modifier void ${fn_name}($styp* it); // auto') mut fn_builder := strings.new_builder(128) defer { g.auto_fn_definitions << fn_builder.str() } - fn_builder.writeln('void ${fn_name}($styp* it) {') + fn_builder.writeln('$g.static_modifier void ${fn_name}($styp* it) {') fn_builder.writeln('\tmap_free(it);') fn_builder.writeln('}') diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 6d9a3b92e0..3fedcc53c6 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -216,18 +216,24 @@ mut: autofree_scope_stmts []string use_segfault_handler bool = true test_function_names []string + ///////// + // out_parallel []strings.Builder + // out_idx int + out_fn_start_pos []int // for generating multiple .c files, stores locations of all fn positions in `out` string builder + static_modifier string // for parallel_cc } // global or const variable definition string struct GlobalConstDef { - mod string // module name - def string // definition - init string // init later (in _vinit) - dep_names []string // the names of all the consts, that this const depends on - order int // -1 for simple defines, string literals, anonymous function names, extern declarations etc + mod string // module name + def string // definition + init string // init later (in _vinit) + dep_names []string // the names of all the consts, that this const depends on + order int // -1 for simple defines, string literals, anonymous function names, extern declarations etc + is_precomputed bool // can be declared as a const in C: primitive, and a simple definition } -pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { +pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) (string, string, string, []int) { // println('start cgen2') mut module_built := '' if pref.build_mode == .build_module { @@ -282,7 +288,17 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { is_cc_msvc: pref.ccompiler == 'msvc' use_segfault_handler: !('no_segfault_handler' in pref.compile_defines || pref.os in [.wasm32, .wasm32_emscripten]) + static_modifier: if pref.parallel_cc { 'static' } else { '' } } + /* + global_g.out_parallel = []strings.Builder{len: nr_cpus} + for i in 0 .. nr_cpus { + global_g.out_parallel[i] = strings.new_builder(100000) + global_g.out_parallel[i].writeln('#include "out.h"\n') + } + println('LEN=') + println(global_g.out_parallel.len) + */ // anon fn may include assert and thus this needs // to be included before any test contents are written if pref.is_test { @@ -519,6 +535,7 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { b.write_string(g.dump_funcs.str()) } if g.auto_fn_definitions.len > 0 { + b.writeln('\n// V auto functions:') for fn_def in g.auto_fn_definitions { b.writeln(fn_def) } @@ -528,12 +545,17 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { b.writeln('\n// V closure helpers') b.writeln(c_closure_helpers(g.pref)) } + b.writeln('\n// V anon functions:') for fn_def in g.anon_fn_definitions { b.writeln(fn_def) } } - b.writeln('\n// V out') - b.write_string(g.out.str()) + b.writeln('\n// end of V out') + mut header := b.last_n(b.len) + header = '#ifndef V_HEADER_FILE\n#define V_HEADER_FILE' + header + header += '\n#endif\n' + out_str := g.out.str() + b.write_string(out_str) // g.out.str()) b.writeln('\n// THE END.') g.timers.show('cgen common') res := b.str() @@ -543,9 +565,11 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { eprintln('>> g.table.fn_generic_types key: $gkey') } } + out_fn_start_pos := g.out_fn_start_pos.clone() unsafe { b.free() } unsafe { g.free_builders() } - return res + + return header, res, out_str, out_fn_start_pos } fn cgen_process_one_file_cb(p &pool.PoolProcessor, idx int, wid int) &Gen { @@ -818,7 +842,8 @@ pub fn (mut g Gen) write_typeof_functions() { if sum_info.is_generic { continue } - g.writeln('static char * v_typeof_sumtype_${sym.cname}(int sidx) { /* $sym.name */ ') + g.writeln('char * v_typeof_sumtype_${sym.cname}(int sidx) { /* $sym.name */ ') + // g.writeln('static char * v_typeof_sumtype_${sym.cname}(int sidx) { /* $sym.name */ ') if g.pref.build_mode == .build_module { g.writeln('\t\tif( sidx == _v_type_idx_${sym.cname}() ) return "${util.strip_main_name(sym.name)}";') for v in sum_info.variants { @@ -839,7 +864,8 @@ pub fn (mut g Gen) write_typeof_functions() { } g.writeln('}') g.writeln('') - g.writeln('static int v_typeof_sumtype_idx_${sym.cname}(int sidx) { /* $sym.name */ ') + // g.writeln('static int v_typeof_sumtype_idx_${sym.cname}(int sidx) { /* $sym.name */ ') + g.writeln('int v_typeof_sumtype_idx_${sym.cname}(int sidx) { /* $sym.name */ ') if g.pref.build_mode == .build_module { g.writeln('\t\tif( sidx == _v_type_idx_${sym.cname}() ) return ${int(ityp)};') for v in sum_info.variants { @@ -939,13 +965,17 @@ fn (mut g Gen) generic_fn_name(types []ast.Type, before string) string { fn (mut g Gen) expr_string(expr ast.Expr) string { pos := g.out.len + // pos2 := g.out_parallel[g.out_idx].len g.expr(expr) + // g.out_parallel[g.out_idx].cut_to(pos2) return g.out.cut_to(pos).trim_space() } fn (mut g Gen) expr_string_with_cast(expr ast.Expr, typ ast.Type, exp ast.Type) string { pos := g.out.len + // pos2 := g.out_parallel[g.out_idx].len g.expr_with_cast(expr, typ, exp) + // g.out_parallel[g.out_idx].cut_to(pos2) return g.out.cut_to(pos).trim_space() } @@ -953,6 +983,7 @@ fn (mut g Gen) expr_string_with_cast(expr ast.Expr, typ ast.Type, exp ast.Type) // (and create a statement) fn (mut g Gen) expr_string_surround(prepend string, expr ast.Expr, append string) string { pos := g.out.len + // pos2 := g.out_parallel[g.out_idx].len g.stmt_path_pos << pos defer { g.stmt_path_pos.delete_last() @@ -960,6 +991,7 @@ fn (mut g Gen) expr_string_surround(prepend string, expr ast.Expr, append string g.write(prepend) g.expr(expr) g.write(append) + // g.out_parallel[g.out_idx].cut_to(pos2) return g.out.cut_to(pos) } @@ -1261,8 +1293,10 @@ pub fn (mut g Gen) write_typedef_types() { mut fixed := g.typ(info.elem_type) if elem_sym.info is ast.FnType { pos := g.out.len + // pos2:=g.out_parallel[g.out_idx].len g.write_fn_ptr_decl(&elem_sym.info, '') fixed = g.out.cut_to(pos) + // g.out_parallel[g.out_idx].cut_to(pos2) mut def_str := 'typedef $fixed;' def_str = def_str.replace_once('(*)', '(*$styp[$len])') g.type_definitions.writeln(def_str) @@ -1486,28 +1520,6 @@ pub fn (mut g Gen) write_multi_return_types() { g.type_definitions.writeln('// END_multi_return_structs\n') } -pub fn (mut g Gen) write(s string) { - $if trace_gen ? { - eprintln('gen file: ${g.file.path:-30} | last_fn_c_name: ${g.last_fn_c_name:-45} | write: $s') - } - if g.indent > 0 && g.empty_line { - g.out.write_string(util.tabs(g.indent)) - } - g.out.write_string(s) - g.empty_line = false -} - -pub fn (mut g Gen) writeln(s string) { - $if trace_gen ? { - eprintln('gen file: ${g.file.path:-30} | last_fn_c_name: ${g.last_fn_c_name:-45} | writeln: $s') - } - if g.indent > 0 && g.empty_line { - g.out.write_string(util.tabs(g.indent)) - } - g.out.writeln(s) - g.empty_line = true -} - pub fn (mut g Gen) new_tmp_var() string { g.tmp_count++ return '_t$g.tmp_count' @@ -3244,6 +3256,7 @@ fn (mut g Gen) expr(node_ ast.Expr) { g.write(')') if gen_or { if !node.is_option { + g.write('/*JJJ*/') g.or_block(tmp_opt, node.or_block, elem_type) } if is_gen_or_and_assign_rhs { @@ -3757,7 +3770,7 @@ fn (mut g Gen) map_init(node ast.MapInit) { is_amp := g.is_amp g.is_amp = false if is_amp { - g.out.go_back(1) // delete the `&` already generated in `prefix_expr() + g.go_back(1) // delete the `&` already generated in `prefix_expr() } if g.is_shared { mut shared_typ := node.typ.set_flag(.shared_f) @@ -4715,7 +4728,8 @@ fn (mut g Gen) const_decl_precomputed(mod string, name string, field_name string fn (mut g Gen) const_decl_write_precomputed(mod string, styp string, cname string, field_name string, ct_value string) { g.global_const_defs[util.no_dots(field_name)] = GlobalConstDef{ mod: mod - def: '$styp $cname = $ct_value; // precomputed' + def: '$g.static_modifier const $styp $cname = $ct_value; // precomputed2' + // is_precomputed: true } } @@ -4803,7 +4817,8 @@ fn (mut g Gen) global_decl(node ast.GlobalDecl) { && !util.should_bundle_module(node.mod) { 'extern ' } else { - '' + //'' + '$g.static_modifier ' // TODO used to be '' before parallel_cc, may cause issues } // should the global be initialized now, not later in `vinit()` cinit := node.attrs.contains('cinit') @@ -5307,38 +5322,6 @@ fn (mut g Gen) sort_structs(typesa []&ast.TypeSymbol) []&ast.TypeSymbol { } } -[inline] -fn (g &Gen) nth_stmt_pos(n int) int { - return g.stmt_path_pos[g.stmt_path_pos.len - (1 + n)] -} - -[inline] -fn (mut g Gen) set_current_pos_as_last_stmt_pos() { - g.stmt_path_pos << g.out.len -} - -fn (mut g Gen) go_before_stmt(n int) string { - stmt_pos := g.nth_stmt_pos(n) - return g.out.cut_to(stmt_pos) -} - -[inline] -fn (mut g Gen) go_before_ternary() string { - return g.go_before_stmt(g.inside_ternary) -} - -fn (mut g Gen) insert_before_stmt(s string) { - cur_line := g.go_before_stmt(g.inside_ternary) - g.writeln(s) - g.write(cur_line) -} - -fn (mut g Gen) insert_at(pos int, s string) { - cur_line := g.out.cut_to(pos) - g.writeln(s) - g.write(cur_line) -} - // fn (mut g Gen) start_tmp() { // } // If user is accessing the return value eg. in assigment, pass the variable name. @@ -5348,6 +5331,9 @@ fn (mut g Gen) insert_at(pos int, s string) { // Returns the type of the last stmt fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Type) { cvar_name := c_name(var_name) + // if var_name == '_t1' && g.cur_fn.name.contains('load') { + // print_backtrace() + //} mut mr_styp := g.base_type(return_type) is_none_ok := return_type == ast.ovoid_type g.writeln(';') @@ -5791,12 +5777,12 @@ fn (mut g Gen) interface_table() string { iname_table_length := inter_info.types.len if iname_table_length == 0 { // msvc can not process `static struct x[0] = {};` - methods_struct.writeln('$methods_struct_name ${interface_name}_name_table[1];') + methods_struct.writeln('$g.static_modifier $methods_struct_name ${interface_name}_name_table[1];') } else { if g.pref.build_mode != .build_module { - methods_struct.writeln('$methods_struct_name ${interface_name}_name_table[$iname_table_length] = {') + methods_struct.writeln('$g.static_modifier $methods_struct_name ${interface_name}_name_table[$iname_table_length] = {') } else { - methods_struct.writeln('$methods_struct_name ${interface_name}_name_table[$iname_table_length];') + methods_struct.writeln('$g.static_modifier $methods_struct_name ${interface_name}_name_table[$iname_table_length];') } } mut cast_functions := strings.new_builder(100) @@ -6018,7 +6004,7 @@ static inline __shared__$interface_name ${shared_fn_name}(__shared__$cctype* x) } iin_idx := already_generated_mwrappers[interface_index_name] - iinidx_minimum_base if g.pref.build_mode != .build_module { - sb.writeln('const int $interface_index_name = $iin_idx;') + sb.writeln('$g.static_modifier const int $interface_index_name = $iin_idx;') } else { sb.writeln('extern const int $interface_index_name;') } diff --git a/vlib/v/gen/c/dumpexpr.v b/vlib/v/gen/c/dumpexpr.v index bc3f432491..85472e3431 100644 --- a/vlib/v/gen/c/dumpexpr.v +++ b/vlib/v/gen/c/dumpexpr.v @@ -50,7 +50,7 @@ fn (mut g Gen) dump_expr_definitions() { tdef_pos := g.out.len g.write_fn_ptr_decl(&fninfo, str_dumparg_type) str_tdef := g.out.after(tdef_pos) - g.out.go_back(str_tdef.len) + g.go_back(str_tdef.len) dump_typedefs['typedef $str_tdef;'] = true } dump_fn_name := '_v_dump_expr_$name' + (if is_ptr { '_ptr' } else { '' }) diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 28bc6b3c00..352d9f8be0 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -48,6 +48,15 @@ fn (mut g Gen) fn_decl(node ast.FnDecl) { g.definitions.write_string('#define _v_malloc GC_MALLOC\n') return } + if g.pref.parallel_cc { + if node.is_anon { + g.write('static ') + g.definitions.write_string('static ') + } + if !node.is_anon { + g.out_fn_start_pos << g.out.len + } + } g.gen_attrs(node.attrs) mut skip := false pos := g.out.len @@ -98,13 +107,18 @@ fn (mut g Gen) fn_decl(node ast.FnDecl) { } g.fn_decl = keep_fn_decl if skip { - g.out.go_back_to(pos) + g.go_back_to(pos) } if !g.pref.skip_unused { if node.language != .c { g.writeln('') } } + // Write the next function into another parallel C file + // g.out_idx++ + // if g.out_idx >= g.out_parallel.len { + // g.out_idx = 0 + //} } fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { @@ -487,6 +501,7 @@ fn (mut g Gen) gen_anon_fn_decl(mut node ast.AnonFn) { } node.has_gen = true mut builder := strings.new_builder(256) + builder.writeln('/*F*/') if node.inherited_vars.len > 0 { ctx_struct := closure_ctx(node.decl) builder.writeln('$ctx_struct {') @@ -646,6 +661,7 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { } else { '' } + // g.write('/*EE line="$cur_line"*/') tmp_opt := if gen_or || gen_keep_alive { g.new_tmp_var() } else { '' } if gen_or || gen_keep_alive { mut ret_typ := node.return_type @@ -2000,7 +2016,7 @@ fn (mut g Gen) go_expr(node ast.GoExpr) { } g.type_definitions.writeln('} $wrapper_struct_name;') thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' } - g.type_definitions.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg);') + g.type_definitions.writeln('$g.static_modifier $thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg);') g.gowrappers.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg) {') if node.call_expr.return_type != ast.void_type { if g.pref.os == .windows { @@ -2049,7 +2065,7 @@ fn (mut g Gen) go_expr(node ast.GoExpr) { pos := g.out.len g.call_args(expr) mut call_args_str := g.out.after(pos) - g.out.go_back(call_args_str.len) + g.go_back(call_args_str.len) mut rep_group := []string{cap: 2 * expr.args.len} for i in 0 .. expr.args.len { rep_group << g.expr_string(expr.args[i].expr) diff --git a/vlib/v/gen/c/for.v b/vlib/v/gen/c/for.v index 6ff23b5dc1..32e7cd8ad1 100644 --- a/vlib/v/gen/c/for.v +++ b/vlib/v/gen/c/for.v @@ -60,7 +60,7 @@ fn (mut g Gen) for_c_stmt(node ast.ForCStmt) { g.stmt(node.init) // Remove excess return and add space if g.out.last_n(1) == '\n' { - g.out.go_back(1) + g.go_back(1) g.empty_line = false g.write(' ') } diff --git a/vlib/v/gen/c/struct.v b/vlib/v/gen/c/struct.v index 85ce5d4c0e..0bc1d1e4e2 100644 --- a/vlib/v/gen/c/struct.v +++ b/vlib/v/gen/c/struct.v @@ -26,7 +26,7 @@ fn (mut g Gen) struct_init(node ast.StructInit) { mut shared_styp := '' // only needed for shared x := St{... if styp in c.skip_struct_init { // needed for c++ compilers - g.out.go_back(3) + g.go_back(3) return } mut sym := g.table.final_sym(g.unwrap_generic(node.typ)) @@ -34,7 +34,7 @@ fn (mut g Gen) struct_init(node ast.StructInit) { is_multiline := node.fields.len > 5 g.is_amp = false // reset the flag immediately so that other struct inits in this expr are handled correctly if is_amp { - g.out.go_back(1) // delete the `&` already generated in `prefix_expr() + g.go_back(1) // delete the `&` already generated in `prefix_expr() } mut is_anon := false if sym.kind == .struct_ { diff --git a/vlib/v/gen/c/text_manipulation.v b/vlib/v/gen/c/text_manipulation.v new file mode 100644 index 0000000000..8bfe511b5c --- /dev/null +++ b/vlib/v/gen/c/text_manipulation.v @@ -0,0 +1,79 @@ +// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module c + +import v.util + +fn (mut g Gen) write(s string) { + $if trace_gen ? { + eprintln('gen file: ${g.file.path:-30} | last_fn_c_name: ${g.last_fn_c_name:-45} | write: $s') + } + if g.indent > 0 && g.empty_line { + g.out.write_string(util.tabs(g.indent)) + // g.out_parallel[g.out_idx].write_string(util.tabs(g.indent)) + } + g.out.write_string(s) + ////g.out_parallel[g.out_idx].write_string(s) + g.empty_line = false +} + +fn (mut g Gen) writeln(s string) { + $if trace_gen ? { + eprintln('gen file: ${g.file.path:-30} | last_fn_c_name: ${g.last_fn_c_name:-45} | writeln: $s') + } + if g.indent > 0 && g.empty_line { + g.out.write_string(util.tabs(g.indent)) + // g.out_parallel[g.out_idx].write_string(util.tabs(g.indent)) + } + // println('w len=$g.out_parallel.len') + g.out.writeln(s) + // g.out_parallel[g.out_idx].writeln(s) + g.empty_line = true +} + +// Below are hacks that should be removed at some point. + +fn (mut g Gen) go_back(n int) { + g.out.go_back(n) + // g.out_parallel[g.out_idx].go_back(n) +} + +fn (mut g Gen) go_back_to(n int) { + g.out.go_back_to(n) + // g.out_parallel[g.out_idx].go_back_to(n) +} + +[inline] +fn (g &Gen) nth_stmt_pos(n int) int { + return g.stmt_path_pos[g.stmt_path_pos.len - (1 + n)] +} + +[inline] +fn (mut g Gen) set_current_pos_as_last_stmt_pos() { + g.stmt_path_pos << g.out.len +} + +fn (mut g Gen) go_before_stmt(n int) string { + stmt_pos := g.nth_stmt_pos(n) + // g.out_parallel[g.out_idx].cut_to(stmt_pos) + return g.out.cut_to(stmt_pos) +} + +[inline] +fn (mut g Gen) go_before_ternary() string { + return g.go_before_stmt(g.inside_ternary) +} + +fn (mut g Gen) insert_before_stmt(s string) { + cur_line := g.go_before_stmt(g.inside_ternary) + g.writeln(s) + g.write(cur_line) +} + +fn (mut g Gen) insert_at(pos int, s string) { + cur_line := g.out.cut_to(pos) + // g.out_parallel[g.out_idx].cut_to(pos) + g.writeln(s) + g.write(cur_line) +} diff --git a/vlib/v/parser/v_parser_test.v b/vlib/v/parser/v_parser_test.v index 1fc31d2cbb..86298271d7 100644 --- a/vlib/v/parser/v_parser_test.v +++ b/vlib/v/parser/v_parser_test.v @@ -79,7 +79,7 @@ x := 10 prog := parse_file(s, table, .skip_comments, vpref) mut checker := checker.new_checker(table, vpref) checker.check(prog) - res := c.gen([prog], table, vpref) + res, _, _, _ := c.gen([prog], table, vpref) println(res) } @@ -107,7 +107,7 @@ fn test_one() { } mut checker := checker.new_checker(table, vpref) checker.check(program) - res := c.gen([program], table, vpref).replace('\n', '').trim_space().after('#endif') + res, _, _, _ := c.gen([program], table, vpref).replace('\n', '').trim_space().after('#endif') println(res) ok := expected == res println(res) @@ -148,7 +148,7 @@ fn test_parse_expr() { global_scope: scope } chk.check(program) - res := c.gen([program], table, vpref).after('#endif') + res, _, _, _ := c.gen([program], table, vpref).after('#endif') println('========') println(res) println('========') diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index e1e89b9a1b..693251d93f 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -200,6 +200,7 @@ pub mut: no_std bool // when true, do not pass -std=gnu99(linux)/-std=c99 to the C backend // no_parallel bool // do not use threads when compiling; slower, but more portable and sometimes less buggy + parallel_cc bool // whether to split the resulting .c file into many .c files + a common .h file, that are then compiled in parallel, then linked together. only_check_syntax bool // when true, just parse the files, then stop, before running checker check_only bool // same as only_check_syntax, but also runs the checker experimental bool // enable experimental features @@ -565,6 +566,10 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin '-no-parallel' { res.no_parallel = true } + '-parallel-cc' { + res.parallel_cc = true + res.no_parallel = true // TODO: see how to make both work + } '-native' { res.backend = .native res.build_options << arg diff --git a/vlib/v/util/util.v b/vlib/v/util/util.v index 554698893e..7c148d276c 100644 --- a/vlib/v/util/util.v +++ b/vlib/v/util/util.v @@ -8,6 +8,7 @@ import time import v.pref import v.vmod import v.util.recompilation +import runtime // math.bits is needed by strconv.ftoa pub const ( @@ -37,6 +38,8 @@ const ( ] ) +pub const nr_jobs = runtime.nr_jobs() + pub fn module_is_builtin(mod string) bool { return mod in util.builtin_module_parts }