Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions vlib/v/driver/driver.v
Original file line number Diff line number Diff line change
Expand Up @@ -12297,6 +12297,7 @@ pub fn run(args []string) {
} else {
b.step('finalize')
}
mut cg_pre_sw := time.new_stopwatch()
stage_macos_v3_compiler_error_fallback(macos_v3_fallback_file, 'backend code generation')
if backend == 'wasm' {
if msg := unsupported_backend_error(a, &pre_tc, used_fns, backend) {
Expand Down Expand Up @@ -12477,6 +12478,9 @@ pub fn run(args []string) {
g.set_incremental_fn_names(incremental_changed_names)
g.set_cached_support_declarations(incremental_known_declarations)
g.set_scope_parallel_workers(!generic_cache_hit)
if verbose {
eprintln(' [ttime] cg setup ${f64(cg_pre_sw.elapsed().microseconds()) / 1000.0:7.2f} ms')
}
g.gen_to_file_with_used_test_options(generated_path, a, cgen_used_fns, &pre_tc, cache_no_parallel_cgen || test_files.len > 0, test_files) or {
eprintln('error writing ${generated_path}: ${err}')
cleanup_c_build_dir(cc_dir)
Expand Down Expand Up @@ -16472,14 +16476,15 @@ fn set_diagnostic_files(mut tc types.TypeChecker, user_files []string) {
}
ids
}
mut resolver := types.new_shadow_file_resolver()
for i in file_ids {
node := tc.a.nodes[i]
if i < tc.a.user_code_start || node.kind != .file || node.value.len == 0
|| node.value in tc.diagnostic_files {
continue
}
if types.shadow_roots_own_file(node.value, tc.shadow_diagnostic_root,
tc.shadow_explicit_roots, tc.shadow_dependency_roots) {
if resolver.owns_file(node.value, tc.shadow_diagnostic_root, tc.shadow_explicit_roots,
tc.shadow_dependency_roots) {
tc.diagnostic_files[node.value] = true
}
}
Expand Down
50 changes: 41 additions & 9 deletions vlib/v/gen/c/cleanc.v
Original file line number Diff line number Diff line change
Expand Up @@ -3903,8 +3903,8 @@ pub fn (mut g FlatGen) gen_with_used_options(a &flat.FlatAst, used_fns map[strin
}
mut prefix := unsafe { g.sb.reuse_as_plain_u8_array() }
$if !windows {
if os.getenv('V3_NO_MMAP_CGEN_OUTPUT') == '' {
write_c_output_mapped(g.output_path, prefix, g.fn_segs, tail, separator) or {
if os.getenv('V3_NO_WRITEV_CGEN_OUTPUT') == '' {
write_c_output_vectored(g.output_path, prefix, g.fn_segs, tail, separator) or {
g.output_error = err.msg()
}
unsafe { prefix.free() }
Expand Down Expand Up @@ -4428,6 +4428,10 @@ mut:
return_type types.Type = types.Type(types.void_)
decl_is_variadic bool
first_param_is_mut bool
// registration is precomputed by the parallel prep when signature
// registration is deferred (has_registration).
has_registration bool
registration FnSignatureRegistration
}

struct FnSignatureRegistration {
Expand Down Expand Up @@ -4594,8 +4598,13 @@ fn (mut g FlatGen) collect_gen_info(no_parallel bool) {
if g.output_cross_c {
g.index_cross_directive_guards()
}
fn_preps := g.collect_gen_info_fn_preps(top_level_nodes, no_parallel)
mut cisub_sw := time.new_stopwatch()
fn_preps := g.collect_gen_info_fn_preps(top_level_nodes, no_parallel, defer_fn_signature_registrations)
has_parallel_fn_preps := fn_preps.len == top_level_nodes.len
if profile {
g.timing_profile(' [ttime] ci fn preps ${f64(cisub_sw.elapsed().microseconds()) / 1000.0:7.2f} ms')
cisub_sw.restart()
}
for top_level_pos, node_idx in top_level_nodes {
node := g.a.nodes[node_idx]
node_ref := g.a.node(flat.NodeId(node_idx))
Expand Down Expand Up @@ -4685,7 +4694,14 @@ fn (mut g FlatGen) collect_gen_info(no_parallel bool) {
ci_ret_ns += time.sys_mono_now() - ci_r0
}
if defer_fn_signature_registrations {
fn_signature_registrations << g.prepare_fn_signature_registration(node.value, full_name, ptypes, shared_params, decl_is_variadic, first_param_is_mut, return_type)
if prep.has_registration {
if shared_params.any(it) {
g.has_shared_params = true
}
fn_signature_registrations << prep.registration
} else {
fn_signature_registrations << g.prepare_fn_signature_registration(node.value, full_name, ptypes, shared_params, decl_is_variadic, first_param_is_mut, return_type)
}
} else {
g.register_fn_decl_signature_type(node.value, full_name, ptypes, shared_params, decl_is_variadic, first_param_is_mut, return_type)
}
Expand Down Expand Up @@ -4882,10 +4898,18 @@ fn (mut g FlatGen) collect_gen_info(no_parallel bool) {
continue
}
}
if profile {
g.timing_profile(' [ttime] ci decl loop ${f64(cisub_sw.elapsed().microseconds()) / 1000.0:7.2f} ms')
cisub_sw.restart()
}
if defer_fn_signature_registrations {
g.reserve_fn_signature_registrations(fn_signature_registrations)
g.apply_fn_signature_registrations(fn_signature_registrations)
}
if profile {
g.timing_profile(' [ttime] ci apply sigs ${f64(cisub_sw.elapsed().microseconds()) / 1000.0:7.2f} ms')
cisub_sw.restart()
}
if g.has_shared_params {
for full_name, flags in preferred_shared_fn_params {
g.fn_decl_shared_params[full_name] = flags
Expand Down Expand Up @@ -11044,6 +11068,14 @@ fn (mut g FlatGen) prepare_fn_signature_registration(name string, full_name stri
break
}
}
return g.fn_signature_registration_in_module(g.tc.cur_module, name, full_name, ptypes,
shared_params, is_variadic, is_mut, rt)
}

// fn_signature_registration_in_module computes the spellings a declaration in
// `module_name` registers. It only reads generator state (the C-name cache just
// memoizes), so the parallel collect prep can build it on a worker view.
fn (g &FlatGen) fn_signature_registration_in_module(module_name string, name string, full_name string, ptypes []types.Type, shared_params []bool, is_variadic bool, is_mut bool, rt types.Type) FnSignatureRegistration {
mut aliases := [6]string{}
mut alias_count := 0
if !g.dedup_fn_decl_aliases {
Expand All @@ -11052,8 +11084,8 @@ fn (mut g FlatGen) prepare_fn_signature_registration(name string, full_name stri
cname := g.cname(name)
aliases[alias_count] = cname
alias_count++
if g.tc.cur_module.len > 0 && g.tc.cur_module != 'main' && g.tc.cur_module != 'builtin' {
dotted_name := '${g.tc.cur_module}.${name}'
if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
dotted_name := '${module_name}.${name}'
aliases[alias_count] = dotted_name
alias_count++
cdotted_name := g.cname(dotted_name)
Expand All @@ -11075,8 +11107,8 @@ fn (mut g FlatGen) prepare_fn_signature_registration(name string, full_name stri
}
mut dotted_name := ''
mut cdotted_name := ''
if g.tc.cur_module.len > 0 && g.tc.cur_module != 'main' && g.tc.cur_module != 'builtin' {
dotted_name = '${g.tc.cur_module}.${name}'
if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
dotted_name = '${module_name}.${name}'
if dotted_name != name && dotted_name != cname {
aliases[alias_count] = dotted_name
alias_count++
Expand All @@ -11098,7 +11130,7 @@ fn (mut g FlatGen) prepare_fn_signature_registration(name string, full_name stri
}
}
return FnSignatureRegistration{
module_key: fn_decl_module_key(g.tc.cur_module, name)
module_key: fn_decl_module_key(module_name, name)
short_name: c_short_name_view(name)
aliases: aliases
alias_count: u8(alias_count)
Expand Down
23 changes: 19 additions & 4 deletions vlib/v/gen/c/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -323,11 +323,13 @@ fn (g &FlatGen) fn_gen_selection_info() (DirectArrayAccessFns, DirectArrayAccess
mut program_modules := map[string]bool{}
mut non_program_modules := map[string]bool{}
mut scan_file_is_program := false
// Resolving a path is a syscall per file; there is nothing to match when no
// program files are cached, and each file is resolved once for both passes.
mut program_file_flags := map[string]bool{}
for directive_idx in g.top_level_nodes() {
directive := g.a.nodes[directive_idx]
if directive.kind == .file {
scan_file_is_program = g.cache_program_files[directive.value]
|| g.cache_program_files[os.real_path(directive.value)]
scan_file_is_program = g.file_is_cache_program_file(directive.value, mut program_file_flags)
continue
}
if directive.kind == .module_decl && !scan_file_is_program {
Expand All @@ -338,8 +340,7 @@ fn (g &FlatGen) fn_gen_selection_info() (DirectArrayAccessFns, DirectArrayAccess
for directive_idx in g.top_level_nodes() {
directive := g.a.nodes[directive_idx]
if directive.kind == .file {
cur_file_is_program = g.cache_program_files[directive.value]
|| g.cache_program_files[os.real_path(directive.value)]
cur_file_is_program = g.file_is_cache_program_file(directive.value, mut program_file_flags)
continue
}
if directive.kind == .module_decl {
Expand Down Expand Up @@ -520,6 +521,20 @@ fn (mut g FlatGen) gen_fn_items(items []FlatFnGenItem) {
}
}

// file_is_cache_program_file reports whether `file`, as written or resolved,
// is one of the cached program files, memoizing the answer per file.
fn (g &FlatGen) file_is_cache_program_file(file string, mut memo map[string]bool) bool {
if g.cache_program_files.len == 0 {
return false
}
if known := memo[file] {
return known
}
is_program := g.cache_program_files[file] || g.cache_program_files[os.real_path(file)]
memo[file] = is_program
return is_program
}

fn c_backend_fn_file_rank(file string) int {
if file.ends_with('.c.v') {
return 1
Expand Down
29 changes: 20 additions & 9 deletions vlib/v/gen/c/fn_parallel_notd_v3_no_parallel.v
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ struct CollectGenInfoFnPrepArgs {
end int
file string
module_name string
// registrations also builds each signature's alias registration.
registrations bool
}

struct CollectGenInfoScanArgs {
Expand Down Expand Up @@ -133,8 +135,16 @@ fn collect_gen_info_fn_prep_thread(arg voidptr) voidptr {
cur_module = node.value
} else if node.kind == .fn_decl && (!view.has_used_fn_filter()
|| view.used_fn_contains_in_module(node.value, cur_module)) {
mut prep := view.compute_collect_gen_fn_prep(node, cur_module, cur_file)
if a.registrations {
full_name := qualify_name_in_module(cur_module, node.value)
prep.registration = view.fn_signature_registration_in_module(cur_module, node.value,
full_name, prep.ptypes, prep.shared_params, prep.decl_is_variadic, prep.first_param_is_mut,
prep.return_type)
prep.has_registration = true
}
unsafe {
preps[pos] = view.compute_collect_gen_fn_prep(node, cur_module, cur_file)
preps[pos] = prep
}
}
}
Expand Down Expand Up @@ -785,7 +795,7 @@ fn (mut g FlatGen) prepare_shared_sum_and_fixed_array_ret_wrappers(parallel bool
// collect_gen_info_fn_preps resolves used function signatures on the persistent
// worker pool. Registration stays serial in collect_gen_info, preserving all
// source-order and duplicate-declaration semantics.
fn (mut g FlatGen) collect_gen_info_fn_preps(node_ids []i32, no_parallel bool) []CollectGenFnPrep {
fn (mut g FlatGen) collect_gen_info_fn_preps(node_ids []i32, no_parallel bool, with_registrations bool) []CollectGenFnPrep {
if no_parallel || isnil(g.a.worker_pool) || g.a.worker_pool.size() == 0
|| node_ids.len < 2048 || os.getenv('V3_NO_PAR_CGEN_INFO_FNS') != '' {
return []CollectGenFnPrep{}
Expand Down Expand Up @@ -823,13 +833,14 @@ fn (mut g FlatGen) collect_gen_info_fn_preps(node_ids []i32, no_parallel bool) [
mut tasks := []workers.Task{cap: n_jobs}
for job in 0 .. n_jobs {
args << CollectGenInfoFnPrepArgs{
g: voidptr(g)
node_ids_ptr: unsafe { voidptr(&node_ids) }
preps_ptr: unsafe { voidptr(&preps) }
start: node_ids.len * job / n_jobs
end: node_ids.len * (job + 1) / n_jobs
file: context_files[job]
module_name: context_modules[job]
g: voidptr(g)
node_ids_ptr: unsafe { voidptr(&node_ids) }
preps_ptr: unsafe { voidptr(&preps) }
start: node_ids.len * job / n_jobs
end: node_ids.len * (job + 1) / n_jobs
file: context_files[job]
module_name: context_modules[job]
registrations: with_registrations
}
}
for job in 0 .. n_jobs {
Expand Down
16 changes: 15 additions & 1 deletion vlib/v/gen/c/names.v
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,18 @@ fn c_escape(s string) string {
// c_escape_into appends the C-escaped form of s to out, without the temporary
// builder and copy that c_escape needs for its return value.
fn c_escape_into(mut out strings.Builder, s string) {
for b in s.bytes() {
// Copy runs of bytes that need no escape in one write; literal tables are
// mostly plain text, and a per-byte write dominated emitting them.
mut run_start := 0
for i in 0 .. s.len {
b := s[i]
if b >= 32 && b != 127 && b != `\\` && b != `"` {
continue
}
if i > run_start {
unsafe { out.write_ptr(s.str + run_start, i - run_start) }
}
run_start = i + 1
match b {
`\\` {
out.write_string('\\\\')
Expand Down Expand Up @@ -583,6 +594,9 @@ fn c_escape_into(mut out strings.Builder, s string) {
}
}
}
if s.len > run_start {
unsafe { out.write_ptr(s.str + run_start, s.len - run_start) }
}
}

// c_string_literal_chunk_len bounds how much of an escaped byte string is written
Expand Down
Loading
Loading