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:
parent
be7b0f1dc5
commit
3d2588f101
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
86
vlib/v/builder/cbuilder/parallel_cc.v
Normal file
86
vlib/v/builder/cbuilder/parallel_cc.v
Normal 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 { '' }
|
@ -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 {
|
||||
|
@ -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('')
|
||||
|
@ -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('}')
|
||||
|
@ -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;')
|
||||
}
|
||||
|
@ -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 { '' })
|
||||
|
@ -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)
|
||||
|
@ -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(' ')
|
||||
}
|
||||
|
@ -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_ {
|
||||
|
79
vlib/v/gen/c/text_manipulation.v
Normal file
79
vlib/v/gen/c/text_manipulation.v
Normal 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)
|
||||
}
|
@ -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('========')
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user