Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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