1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

cgen: parallel cc for much faster compilation using all CPU cores

This commit is contained in:
Alexander Medvednikov 2022-10-01 10:03:59 +03:00
parent be7b0f1dc5
commit 3d2588f101
16 changed files with 289 additions and 95 deletions

View File

@ -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

View File

@ -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() {

View File

@ -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
}

View File

@ -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 { '' }

View File

@ -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 {

View File

@ -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('')

View File

@ -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('}')

View File

@ -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;')
}

View File

@ -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 { '' })

View File

@ -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)

View File

@ -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(' ')
}

View File

@ -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_ {

View File

@ -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)
}

View File

@ -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('========')

View File

@ -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

View File

@ -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
}