From af90e2747ae2e56909e026a22a1668e7b02f4b3b Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sat, 27 Jun 2026 12:17:11 +0300 Subject: [PATCH] v3: optimize transformer (10% speed up) --- vlib/v3/transform/fn.v | 2 +- vlib/v3/transform/transform.v | 63 ++++++++++----- vlib/v3/transform/type_propagation.v | 117 +++++++++++++++++++++++++-- 3 files changed, 156 insertions(+), 26 deletions(-) diff --git a/vlib/v3/transform/fn.v b/vlib/v3/transform/fn.v index 4a5e55e35..7352424f1 100644 --- a/vlib/v3/transform/fn.v +++ b/vlib/v3/transform/fn.v @@ -633,7 +633,7 @@ fn (t &Transformer) call_param_types(call_name string) []types.Type { return []types.Type{} } params := t.tc.fn_param_types[call_name] or { return []types.Type{} } - return params.clone() + return params } // call_is_variadic updates call is variadic state for Transformer. diff --git a/vlib/v3/transform/transform.v b/vlib/v3/transform/transform.v index 2b1768d77..b4ebb0864 100644 --- a/vlib/v3/transform/transform.v +++ b/vlib/v3/transform/transform.v @@ -184,17 +184,20 @@ pub fn transform_with_used_opt(mut a flat.FlatAst, tc &types.TypeChecker, used_f mut augmented_used_fns := used_fns.clone() mut t := new_transformer(mut a, tc, augmented_used_fns) t.prepare() - // Transform roughly grows the node/children arrays by ~75%. Reserve that capacity - // up front so they don't double past it (the parsed AST already overshoots to the - // next power of two, wasting ~40MB, and each doubling briefly holds both the old and - // new arrays — the dominant peak-RSS contributor under -gc none). - reserve_nodes := a.nodes.len * 7 / 4 - a.nodes.cap - if reserve_nodes > 0 { - unsafe { a.nodes.grow_cap(reserve_nodes) } - } - reserve_children := a.children.len * 7 / 4 - a.children.cap - if reserve_children > 0 { - unsafe { a.children.grow_cap(reserve_children) } + if want_parallel { + // Transform roughly grows the node/children arrays by ~75%. Reserve that capacity + // up front so they don't double past it (the parsed AST already overshoots to the + // next power of two, wasting ~40MB, and each doubling briefly holds both the old and + // new arrays — the dominant peak-RSS contributor under -gc none). The serial path is + // latency-sensitive and does not clone worker ASTs, so let it grow naturally. + reserve_nodes := a.nodes.len * 7 / 4 - a.nodes.cap + if reserve_nodes > 0 { + unsafe { a.nodes.grow_cap(reserve_nodes) } + } + reserve_children := a.children.len * 7 / 4 - a.children.cap + if reserve_children > 0 { + unsafe { a.children.grow_cap(reserve_children) } + } } was_parallel := t.transform_all_dispatch(want_parallel) return augmented_used_fns, was_parallel @@ -219,6 +222,9 @@ fn new_transformer(mut a flat.FlatAst, tc &types.TypeChecker, used_fns map[strin a: a tc: unsafe { tc } pointer_value_lvalues: map[string]bool{} + escaping_amp_ptrs: map[string]bool{} + escaping_amp_sources: map[string]bool{} + heaped_amp_locals: map[string]bool{} used_fns: used_fns.clone() } } @@ -864,6 +870,9 @@ fn (t &Transformer) fork_worker(ast &flat.FlatAst, wtc &types.TypeChecker) &Tran w.smartcast_stack = []SmartcastContext{} w.pending_stmts = []flat.NodeId{} w.pointer_value_lvalues = map[string]bool{} + w.escaping_amp_ptrs = map[string]bool{} + w.escaping_amp_sources = map[string]bool{} + w.heaped_amp_locals = map[string]bool{} w.temp_counter = 0 w.cur_file = '' w.cur_module = '' @@ -1266,12 +1275,7 @@ fn (mut t Transformer) heap_escaping_source_decl(node flat.Node, var_name string // transform. Purely structural (no type info needed here): the type check happens // at rewrite time when `v`'s type is known. fn (mut t Transformer) mark_escaping_amp_ptrs(body_ids []flat.NodeId) { - t.escaping_amp_ptrs = map[string]bool{} - t.escaping_amp_sources = map[string]bool{} - t.heaped_amp_locals = map[string]bool{} - // Cleared per function: heaped locals add their names below (in heap_escaping_source_decl); - // for-loop element vars set and restore their own entries within the loop body. - t.pointer_value_lvalues = map[string]bool{} + t.reset_escaping_amp_state() mut amp_ptrs := map[string]bool{} mut amp_sources := map[string]string{} // pointer `p` -> source local `v` mut ptr_aliases := map[string]string{} // copy `q := p` -> aliased pointer `p` @@ -1304,6 +1308,15 @@ fn (mut t Transformer) mark_escaping_amp_ptrs(body_ids []flat.NodeId) { } } +fn (mut t Transformer) reset_escaping_amp_state() { + t.escaping_amp_ptrs.clear() + t.escaping_amp_sources.clear() + t.heaped_amp_locals.clear() + // Cleared per function: heaped locals add their names below (in heap_escaping_source_decl); + // for-loop element vars set and restore their own entries within the loop body. + t.pointer_value_lvalues.clear() +} + // scan_escape_pass recursively collects, in a function-body subtree, (a) the LHS // names of `p := &ident` declarations into `amp_ptrs` (and the source `ident` into // `amp_sources[p]`), (b) plain pointer copies `q := p` into `ptr_aliases[q] = p`, and @@ -1423,7 +1436,11 @@ fn (mut t Transformer) transform_fn_body(fn_idx int) { body_ids << child_id } } - t.mark_escaping_amp_ptrs(body_ids) + if t.cur_fn_ret_type == 'void' { + t.reset_escaping_amp_state() + } else { + t.mark_escaping_amp_ptrs(body_ids) + } new_body := t.transform_stmts(body_ids) // Rebuild function children: params then new body start := t.a.children.len @@ -5895,6 +5912,10 @@ fn (t &Transformer) resolve_expr_type(id flat.NodeId) string { return '' } .call { + concrete_typ := t.concrete_node_type_name(node) + if concrete_typ.len > 0 { + return concrete_typ + } mut ret := t.current_call_return_type(node) if ret.len == 0 { ret = t.get_call_return_type(id, node) @@ -5963,6 +5984,12 @@ fn (t &Transformer) resolve_expr_type(id flat.NodeId) string { return '' } .selector { + if t.smartcast_stack.len == 0 { + typ := t.concrete_node_type_name(node) + if typ.len > 0 { + return typ + } + } if !isnil(t.tc) && node.children_count > 0 { base := t.a.child_node(&node, 0) if base.kind == .ident { diff --git a/vlib/v3/transform/type_propagation.v b/vlib/v3/transform/type_propagation.v index 5d9766a0d..bd06986eb 100644 --- a/vlib/v3/transform/type_propagation.v +++ b/vlib/v3/transform/type_propagation.v @@ -92,6 +92,93 @@ fn fn_value_type_name_from_type(typ types.Type) ?string { return none } +fn (t &Transformer) concrete_node_type_name(node flat.Node) string { + if node.typ.len == 0 { + return '' + } + typ := t.normalize_type_alias(node.typ) + if typ.len == 0 || typ in ['array', 'map', 'unknown'] { + return '' + } + if type_text_has_unresolved_generic_placeholder(typ) { + return '' + } + return typ +} + +fn type_text_has_unresolved_generic_placeholder(typ string) bool { + clean := typ.trim_space() + if clean.len == 0 { + return false + } + if is_generic_fn_placeholder_name(clean) { + return true + } + if clean.starts_with('&') { + return type_text_has_unresolved_generic_placeholder(clean[1..]) + } + if clean.starts_with('mut ') { + return type_text_has_unresolved_generic_placeholder(clean[4..]) + } + if clean.starts_with('?') || clean.starts_with('!') { + return type_text_has_unresolved_generic_placeholder(clean[1..]) + } + if clean.starts_with('...') { + return type_text_has_unresolved_generic_placeholder(clean[3..]) + } + if clean.starts_with('[]') { + return type_text_has_unresolved_generic_placeholder(clean[2..]) + } + if clean.starts_with('map[') { + bracket_end := generic_matching_bracket(clean, 3) + if bracket_end < clean.len { + return type_text_has_unresolved_generic_placeholder(clean[4..bracket_end]) + || type_text_has_unresolved_generic_placeholder(clean[bracket_end + 1..]) + } + } + if clean.starts_with('[') { + bracket_end := generic_matching_bracket(clean, 0) + if bracket_end < clean.len { + return type_text_has_unresolved_generic_placeholder(clean[bracket_end + 1..]) + } + } + if !type_text_has_generic_placeholder_token(clean) { + return false + } + if !clean.contains('[') { + return true + } + _, args, ok := generic_app_parts(clean) + if ok { + for arg in args { + if type_text_has_unresolved_generic_placeholder(arg) { + return true + } + } + } + return false +} + +fn type_text_has_generic_placeholder_token(text string) bool { + for i := 0; i < text.len; i++ { + ch := text[i] + if ch < `A` || ch > `Z` { + continue + } + prev_is_ident := i > 0 && type_text_ident_char(text[i - 1]) + next_is_ident := i + 1 < text.len && type_text_ident_char(text[i + 1]) + if !prev_is_ident && !next_is_ident { + return true + } + } + return false +} + +fn type_text_ident_char(ch u8) bool { + return (ch >= `a` && ch <= `z`) || (ch >= `A` && ch <= `Z`) + || (ch >= `0` && ch <= `9`) || ch == `_` +} + // resolve_selector_type resolves the type of a .selector node (e.g. `obj.field`). // Looks up the base expression type, then finds the field in the struct definition. fn (t &Transformer) resolve_selector_type(node flat.Node) string { @@ -124,15 +211,20 @@ fn (t &Transformer) resolve_selector_type(node flat.Node) string { if field_name.len == 0 { return '' } - if enum_type := t.enum_type_name_from_expr(base_id) { - if fields := t.enum_types[enum_type] { - if field_name in fields { - return enum_type + base_selector_name := t.selector_expr_name(base_id) + if base_selector_name.len > 0 { + if enum_type := t.enum_type_name_from_selector_name(base_selector_name) { + if fields := t.enum_types[enum_type] { + if field_name in fields { + return enum_type + } } } - } - if type_name := t.qualified_enum_type_selector_name(base_id, field_name) { - return type_name + if type_name := t.qualified_enum_type_selector_name_from_selector_name(base_selector_name, + field_name) + { + return type_name + } } if base_node.kind == .ident { if typ := t.const_type_name('${base_node.value}.${field_name}') { @@ -223,6 +315,10 @@ fn (t &Transformer) selector_expr_name(id flat.NodeId) string { // enum_type_name_from_expr converts enum type name from expr data for transform. fn (t &Transformer) enum_type_name_from_expr(id flat.NodeId) ?string { name := t.selector_expr_name(id) + return t.enum_type_name_from_selector_name(name) +} + +fn (t &Transformer) enum_type_name_from_selector_name(name string) ?string { if name.len == 0 { return none } @@ -243,6 +339,10 @@ fn (t &Transformer) enum_type_name_from_expr(id flat.NodeId) ?string { // supports helper handling in transform. fn (t &Transformer) qualified_enum_type_selector_name(base_id flat.NodeId, field_name string) ?string { base := t.selector_expr_name(base_id) + return t.qualified_enum_type_selector_name_from_selector_name(base, field_name) +} + +fn (t &Transformer) qualified_enum_type_selector_name_from_selector_name(base string, field_name string) ?string { if base.len == 0 || field_name.len == 0 { return none } @@ -265,6 +365,9 @@ fn (t &Transformer) lookup_struct_field_type(type_name string, field_name string lookup := t.lookup_struct_info_for_field(type_name, field_name) or { return none } for f in lookup.info.fields { if f.name == field_name { + if !lookup.owner_type.contains('[') { + return f.typ + } return t.normalize_field_type(f.typ, lookup.owner_type) } } -- 2.39.5