From 3619ec38a94cacf123e970055369e7a828fd880b Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Mon, 29 Jun 2026 16:47:28 +0300 Subject: [PATCH] v3: speed up cgen x40 after a recent regression --- vlib/v3/gen/c/cleanc.v | 34 +++++----- vlib/v3/gen/c/fn.v | 72 +++++++++++++++++----- vlib/v3/gen/c/fn_d_parallel.v | 113 +++++++++++++++++----------------- 3 files changed, 132 insertions(+), 87 deletions(-) diff --git a/vlib/v3/gen/c/cleanc.v b/vlib/v3/gen/c/cleanc.v index 458f6f076..03ff5a88b 100644 --- a/vlib/v3/gen/c/cleanc.v +++ b/vlib/v3/gen/c/cleanc.v @@ -71,21 +71,22 @@ mut: // in_return is true only while generating a `return` statement's value, so a bare // generic literal (`return Box{...}`) may adopt `cur_fn_ret`'s concrete instance — // but a literal in a local decl / argument elsewhere in the body does not. - in_return bool - expected_expr_type types.Type = types.Type(types.void_) - expected_enum string - needed_optional_types map[string]string - emitted_optional_types map[string]bool - emitted_fns map[string]bool - array_method_cache map[string]string - param_types_cache map[string][]types.Type // (name|fallback) -> resolved param types - embedded_fields_by_type map[string][]types.StructField // type name -> its embedded fields (usually empty) - param_types_by_short map[string][]types.Type // method short-name suffix -> param types (fallback index) - spawn_wrapper_names map[string]string - spawn_wrapper_defs []string - callback_wrapper_names map[string]string - callback_wrapper_defs []string - parallel_used bool + in_return bool + expected_expr_type types.Type = types.Type(types.void_) + expected_enum string + needed_optional_types map[string]string + emitted_optional_types map[string]bool + emitted_fns map[string]bool + array_method_cache map[string]string + param_types_cache map[string][]types.Type // (name|fallback) -> resolved param types + embedded_fields_by_type map[string][]types.StructField // type name -> its embedded fields (usually empty) + param_types_by_short map[string][]types.Type // method short-name suffix -> param types (fallback index) + generic_method_candidates map[string][]GenericMethodCandidate + spawn_wrapper_names map[string]string + spawn_wrapper_defs []string + callback_wrapper_names map[string]string + callback_wrapper_defs []string + parallel_used bool } struct FixedArrayTypedefInfo { @@ -166,6 +167,7 @@ pub fn FlatGen.new() FlatGen { param_types_cache: map[string][]types.Type{} embedded_fields_by_type: map[string][]types.StructField{} param_types_by_short: map[string][]types.Type{} + generic_method_candidates: map[string][]GenericMethodCandidate{} spawn_wrapper_names: map[string]string{} spawn_wrapper_defs: []string{} callback_wrapper_names: map[string]string{} @@ -260,6 +262,7 @@ pub fn (mut g FlatGen) gen_with_used_options(a &flat.FlatAst, used_fns map[strin g.param_types_cache = map[string][]types.Type{} g.embedded_fields_by_type = map[string][]types.StructField{} g.param_types_by_short = map[string][]types.Type{} + g.generic_method_candidates = map[string][]GenericMethodCandidate{} g.spawn_wrapper_names = map[string]string{} g.spawn_wrapper_defs = []string{} g.callback_wrapper_names = map[string]string{} @@ -277,6 +280,7 @@ pub fn (mut g FlatGen) gen_with_used_options(a &flat.FlatAst, used_fns map[strin g.preseed_struct_fn_ptr_types() g.preseed_global_fn_ptr_types() g.preseed_c_extern_fn_ptr_types() + g.precompute_generic_method_candidate_index() // Decide fixed-array return wrappers before generating function bodies, so // signatures, returns and call sites all agree on the wrapped types. g.populate_fixed_array_ret_wrappers() diff --git a/vlib/v3/gen/c/fn.v b/vlib/v3/gen/c/fn.v index 247e61e6d..d90c1b58a 100644 --- a/vlib/v3/gen/c/fn.v +++ b/vlib/v3/gen/c/fn.v @@ -24,6 +24,12 @@ struct TopLevelStmt { module string } +struct GenericMethodCandidate { + name string + ret types.Type + params []types.Type +} + // gen_fns emits fns output for c. fn (mut g FlatGen) gen_fns() { preferred_fns := g.preferred_c_backend_fn_nodes() @@ -292,6 +298,45 @@ fn (g &FlatGen) emitted_fn_contains(name string) bool { return name.len > 0 && g.emitted_fns[name] } +fn generic_method_candidate_key(receiver string, method string) string { + return '${receiver}\n${method}' +} + +fn (mut g FlatGen) precompute_generic_method_candidate_index() { + g.generic_method_candidates = map[string][]GenericMethodCandidate{} + for name, ret in g.tc.fn_ret_types { + if !name.contains('[') || !name.contains('.') { + continue + } + method := name.all_after_last('.') + receiver := name.all_before_last('.') + if method.len == 0 || !receiver.contains('[') { + continue + } + base_receiver := receiver.all_before('[') + if base_receiver.len == 0 { + continue + } + candidate := GenericMethodCandidate{ + name: name + ret: ret + params: g.tc.fn_param_types[name] or { []types.Type{} } + } + g.add_generic_method_candidate(base_receiver, method, candidate) + short_receiver := base_receiver.all_after_last('.') + if short_receiver != base_receiver { + g.add_generic_method_candidate(short_receiver, method, candidate) + } + } +} + +fn (mut g FlatGen) add_generic_method_candidate(receiver string, method string, candidate GenericMethodCandidate) { + key := generic_method_candidate_key(receiver, method) + mut candidates := g.generic_method_candidates[key] or { []GenericMethodCandidate{} } + candidates << candidate + g.generic_method_candidates[key] = candidates +} + // qualified_fn_name supports qualified fn name handling for FlatGen. fn (g &FlatGen) qualified_fn_name(name string) string { return qualified_fn_name_in_module(g.tc.cur_module, name) @@ -545,26 +590,21 @@ fn (g &FlatGen) specialized_generic_method_name_for_call_with_arg_count(id flat. } } } - for allow_short_receiver in [false, true] { - for candidate, ret in g.tc.fn_ret_types { - if !candidate.contains('[') || candidate.all_after_last('.') != method { + for lookup_receiver in [receiver, receiver.all_after_last('.')] { + candidates := g.generic_method_candidates[generic_method_candidate_key(lookup_receiver, + method)] or { continue } + for candidate in candidates { + if arg_count >= 0 && candidate.params.len != arg_count { continue } - candidate_receiver := candidate.all_before_last('.') - base_receiver := candidate_receiver.all_before('[') - receiver_matches := base_receiver == receiver || (allow_short_receiver - && base_receiver.all_after_last('.') == receiver.all_after_last('.')) - if !receiver_matches { - continue - } - params := g.tc.fn_param_types[candidate] or { []types.Type{} } - if arg_count >= 0 && params.len != arg_count { - continue - } - if g.type_names_match(call_ret, ret) || call_ret.name() == ret.name() { - return candidate + if g.type_names_match(call_ret, candidate.ret) + || call_ret.name() == candidate.ret.name() { + return candidate.name } } + if lookup_receiver == receiver.all_after_last('.') { + break + } } return none } diff --git a/vlib/v3/gen/c/fn_d_parallel.v b/vlib/v3/gen/c/fn_d_parallel.v index 5c3144062..ff9717f1d 100644 --- a/vlib/v3/gen/c/fn_d_parallel.v +++ b/vlib/v3/gen/c/fn_d_parallel.v @@ -362,62 +362,63 @@ fn (mut g FlatGen) fn_ptr_type_key(typ types.FnType) string { // worker and, under -gc none, never freed. fn (g &FlatGen) new_parallel_worker(worker_id int) &FlatGen { return &FlatGen{ - sb: strings.new_builder(64_000) - a: unsafe { g.a } - used_fns: g.used_fns - used_fn_names: g.used_fn_names - test_files: g.test_files.clone() - str_lits: g.str_lits.clone() - str_lit_ids: g.str_lit_ids.clone() - global_types: g.global_types - enum_vals: g.enum_vals - interfaces: g.interfaces - const_vals: g.const_vals - const_modules: g.const_modules - const_init_order: g.const_init_order - global_modules: g.global_modules - global_inits: g.global_inits - global_init_order: g.global_init_order - iface_impls: g.iface_impls - iface_type_ids: g.iface_type_ids - module_init_fns: g.module_init_fns - module_init_fn_modules: g.module_init_fn_modules - module_imports: g.module_imports - libc_compat_fns: g.libc_compat_fns.clone() - tc: g.clone_parallel_type_checker() - has_builtins: g.has_builtins - tmp_count: (worker_id + 1) * 100_000 - line_start: true - modules: g.modules - fn_ptr_types: g.fn_ptr_types.clone() - fixed_array_ret_wrappers: g.fixed_array_ret_wrappers - fn_decl_param_types: g.fn_decl_param_types - fn_decl_ret_types: g.fn_decl_ret_types - struct_decl_infos: g.struct_decl_infos - struct_decl_short_infos: g.struct_decl_short_infos - const_runtime_inits: g.const_runtime_inits.clone() - runtime_inits: g.runtime_inits.clone() - compiler_vroot: g.compiler_vroot - cur_param_names: g.cur_param_names.clone() - cur_param_type_values: g.cur_param_type_values.clone() - cur_param_types: g.cur_param_types.clone() - cur_mut_params: g.cur_mut_params.clone() - cur_fn_ret: g.cur_fn_ret - cur_fn_ret_is_optional: g.cur_fn_ret_is_optional - cur_fn_ret_base: g.cur_fn_ret_base - expected_expr_type: g.expected_expr_type - expected_enum: g.expected_enum - needed_optional_types: g.needed_optional_types.clone() - emitted_optional_types: g.emitted_optional_types.clone() - emitted_fns: g.emitted_fns.clone() - array_method_cache: g.array_method_cache.clone() - param_types_cache: g.param_types_cache.clone() - embedded_fields_by_type: g.embedded_fields_by_type - param_types_by_short: g.param_types_by_short - spawn_wrapper_names: g.spawn_wrapper_names.clone() - spawn_wrapper_defs: g.spawn_wrapper_defs.clone() - callback_wrapper_names: g.callback_wrapper_names.clone() - callback_wrapper_defs: g.callback_wrapper_defs.clone() + sb: strings.new_builder(64_000) + a: unsafe { g.a } + used_fns: g.used_fns + used_fn_names: g.used_fn_names + test_files: g.test_files.clone() + str_lits: g.str_lits.clone() + str_lit_ids: g.str_lit_ids.clone() + global_types: g.global_types + enum_vals: g.enum_vals + interfaces: g.interfaces + const_vals: g.const_vals + const_modules: g.const_modules + const_init_order: g.const_init_order + global_modules: g.global_modules + global_inits: g.global_inits + global_init_order: g.global_init_order + iface_impls: g.iface_impls + iface_type_ids: g.iface_type_ids + module_init_fns: g.module_init_fns + module_init_fn_modules: g.module_init_fn_modules + module_imports: g.module_imports + libc_compat_fns: g.libc_compat_fns.clone() + tc: g.clone_parallel_type_checker() + has_builtins: g.has_builtins + tmp_count: (worker_id + 1) * 100_000 + line_start: true + modules: g.modules + fn_ptr_types: g.fn_ptr_types.clone() + fixed_array_ret_wrappers: g.fixed_array_ret_wrappers + fn_decl_param_types: g.fn_decl_param_types + fn_decl_ret_types: g.fn_decl_ret_types + struct_decl_infos: g.struct_decl_infos + struct_decl_short_infos: g.struct_decl_short_infos + const_runtime_inits: g.const_runtime_inits.clone() + runtime_inits: g.runtime_inits.clone() + compiler_vroot: g.compiler_vroot + cur_param_names: g.cur_param_names.clone() + cur_param_type_values: g.cur_param_type_values.clone() + cur_param_types: g.cur_param_types.clone() + cur_mut_params: g.cur_mut_params.clone() + cur_fn_ret: g.cur_fn_ret + cur_fn_ret_is_optional: g.cur_fn_ret_is_optional + cur_fn_ret_base: g.cur_fn_ret_base + expected_expr_type: g.expected_expr_type + expected_enum: g.expected_enum + needed_optional_types: g.needed_optional_types.clone() + emitted_optional_types: g.emitted_optional_types.clone() + emitted_fns: g.emitted_fns.clone() + array_method_cache: g.array_method_cache.clone() + param_types_cache: g.param_types_cache.clone() + embedded_fields_by_type: g.embedded_fields_by_type + param_types_by_short: g.param_types_by_short + generic_method_candidates: g.generic_method_candidates + spawn_wrapper_names: g.spawn_wrapper_names.clone() + spawn_wrapper_defs: g.spawn_wrapper_defs.clone() + callback_wrapper_names: g.callback_wrapper_names.clone() + callback_wrapper_defs: g.callback_wrapper_defs.clone() } } -- 2.39.5