From bdc2b4357bd8710471957b96a2c54796fa1c4e08 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sun, 26 Apr 2026 01:25:38 +0300 Subject: [PATCH] cgen, checker: optimizations --- vlib/builtin/string.v | 23 +++++++++++++ vlib/v/builder/icon.v | 52 ++++++++++++++++++++--------- vlib/v/checker/checker.v | 23 ++++++++++++- vlib/v/checker/comptime.v | 32 ++++++++++++++---- vlib/v/gen/c/fn.v | 57 +++++++++++++++++++++++++------- vlib/v/gen/c/utils.v | 19 +++++++++-- vlib/v/transformer/transformer.v | 50 +++++++++++++++++++++++++--- vlib/v/util/util.v | 7 +++- 8 files changed, 221 insertions(+), 42 deletions(-) diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 12ccb396e..0bd3e3b0f 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -2077,6 +2077,29 @@ pub fn (s string) trim_right(cutset string) string { if s.len < 1 || cutset.len < 1 { return s.clone() } + if cutset.len == 1 { + cut := cutset[0] + mut pos_right := s.len - 1 + for pos_right >= 0 && s[pos_right] == cut { + pos_right-- + } + if pos_right < 0 { + return '' + } + return s.substr(0, pos_right + 1) + } + if cutset.len == 2 && cutset.is_pure_ascii() { + cut0 := cutset[0] + cut1 := cutset[1] + mut pos_right := s.len - 1 + for pos_right >= 0 && (s[pos_right] == cut0 || s[pos_right] == cut1) { + pos_right-- + } + if pos_right < 0 { + return '' + } + return s.substr(0, pos_right + 1) + } if cutset.is_pure_ascii() { return s.trim_chars(cutset, .trim_right) } else { diff --git a/vlib/v/builder/icon.v b/vlib/v/builder/icon.v index 86ab32088..36a938d94 100644 --- a/vlib/v/builder/icon.v +++ b/vlib/v/builder/icon.v @@ -1,6 +1,5 @@ module builder -import encoding.binary import os const windows_icon_group_resource_id = 1 @@ -131,10 +130,10 @@ fn parse_ico_bytes(data []u8) ![]WindowsIconImage { if data.len < 6 { return error('invalid icon file: missing ICO header') } - if binary.little_endian_u16(data[0..2]) != 0 || binary.little_endian_u16(data[2..4]) != 1 { + if read_le_u16(data, 0) != 0 || read_le_u16(data, 2) != 1 { return error('invalid icon file: expected an ICO header') } - image_count := int(binary.little_endian_u16(data[4..6])) + image_count := int(read_le_u16(data, 4)) if image_count <= 0 { return error('invalid icon file: no icon images were found') } @@ -144,8 +143,8 @@ fn parse_ico_bytes(data []u8) ![]WindowsIconImage { mut images := []WindowsIconImage{cap: image_count} for i := 0; i < image_count; i++ { entry_offset := 6 + (i * 16) - image_size := int(binary.little_endian_u32(data[entry_offset + 8..entry_offset + 12])) - image_offset := int(binary.little_endian_u32(data[entry_offset + 12..entry_offset + 16])) + image_size := int(read_le_u32(data, entry_offset + 8)) + image_offset := int(read_le_u32(data, entry_offset + 12)) image_end := image_offset + image_size if image_offset < 0 || image_size <= 0 || image_offset > data.len || image_end > data.len { return error('invalid icon file: icon image ${i + 1} points outside the file') @@ -154,8 +153,8 @@ fn parse_ico_bytes(data []u8) ![]WindowsIconImage { width: data[entry_offset] height: data[entry_offset + 1] color_count: data[entry_offset + 2] - planes: binary.little_endian_u16(data[entry_offset + 4..entry_offset + 6]) - bit_count: binary.little_endian_u16(data[entry_offset + 6..entry_offset + 8]) + planes: read_le_u16(data, entry_offset + 4) + bit_count: read_le_u16(data, entry_offset + 6) bytes_in_res: u32(image_size) image_data: data[image_offset..image_end].clone() } @@ -192,8 +191,8 @@ fn png_dimensions(png []u8) !WindowsIconSize { if png[12] != `I` || png[13] != `H` || png[14] != `D` || png[15] != `R` { return error('invalid PNG icon file: missing IHDR chunk') } - width := int(binary.big_endian_u32(png[16..20])) - height := int(binary.big_endian_u32(png[20..24])) + width := int(read_be_u32(png, 16)) + height := int(read_be_u32(png, 20)) if width <= 0 || height <= 0 || width > max_windows_icon_dimension || height > max_windows_icon_dimension { return error('PNG icons must be between 1x1 and 256x256 pixels') @@ -223,13 +222,36 @@ fn build_group_icon_resource(images []WindowsIconImage) []u8 { } fn append_le_u16(mut data []u8, value u16) { - mut buf := []u8{len: 2} - binary.little_endian_put_u16(mut buf, value) - data << buf + data << u8(value & 0xff) + data << u8(value >> 8) } fn append_le_u32(mut data []u8, value u32) { - mut buf := []u8{len: 4} - binary.little_endian_put_u32(mut buf, value) - data << buf + data << u8(value & 0xff) + data << u8((value >> 8) & 0xff) + data << u8((value >> 16) & 0xff) + data << u8(value >> 24) +} + +@[direct_array_access; inline] +fn read_le_u16(data []u8, offset int) u16 { + return u16(data[offset]) | (u16(data[offset + 1]) << 8) +} + +@[direct_array_access; inline] +fn read_le_u32(data []u8, offset int) u32 { + b0 := u32(data[offset]) + b1 := u32(data[offset + 1]) + b2 := u32(data[offset + 2]) + b3 := u32(data[offset + 3]) + return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24) +} + +@[direct_array_access; inline] +fn read_be_u32(data []u8, offset int) u32 { + b0 := u32(data[offset]) + b1 := u32(data[offset + 1]) + b2 := u32(data[offset + 2]) + b3 := u32(data[offset + 3]) + return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3 } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 496d2f72b..1e95ce6d7 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -165,6 +165,7 @@ mut: immutable_alias_analysis_in_progress map[string]bool always_error_fn_cache map[string]bool always_error_fn_in_progress map[string]bool + generic_parts_cache []i8 // type idx -> 0 unknown, 1 false, 2 true v_current_commit_hash string // same as old C.V_CURRENT_COMMIT_HASH assign_stmt_attr string // for `x := [1,2,3] @[freed]` @@ -198,6 +199,7 @@ pub fn new_checker(table &ast.Table, pref_ &pref.Preferences) &Checker { immutable_alias_analysis_in_progress: map[string]bool{} always_error_fn_cache: map[string]bool{} always_error_fn_in_progress: map[string]bool{} + generic_parts_cache: []i8{len: table.type_symbols.len} } checker.checker_transformer.skip_array_transform = true checker.type_resolver = type_resolver.TypeResolver.new(table, checker) @@ -7145,9 +7147,28 @@ fn (c &Checker) type_has_unresolved_generic_parts(typ ast.Type) bool { if typ.has_flag(.generic) { return true } - if typ.idx() <= ast.nil_type_idx { + idx := typ.idx() + if idx <= ast.nil_type_idx { return false } + if idx < c.generic_parts_cache.len { + cached := c.generic_parts_cache[idx] + if cached != 0 { + return cached == 2 + } + } else if idx < c.table.type_symbols.len { + mut checker := unsafe { &Checker(c) } + checker.generic_parts_cache << []i8{len: idx - checker.generic_parts_cache.len + 1} + } + resolved := c.type_has_unresolved_generic_parts_uncached(typ) + if idx < c.generic_parts_cache.len { + mut checker := unsafe { &Checker(c) } + checker.generic_parts_cache[idx] = if resolved { i8(2) } else { i8(1) } + } + return resolved +} + +fn (c &Checker) type_has_unresolved_generic_parts_uncached(typ ast.Type) bool { sym := c.table.sym(typ) if sym.kind == .placeholder || (sym.kind == .any && !sym.is_builtin()) { return true diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index c34d22b20..7ef398bc2 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -2,7 +2,6 @@ // Use of this source code is governed by an MIT license that can be found in the LICENSE file. module checker -import math import os import v.ast import v.pref @@ -13,12 +12,33 @@ import v.type_resolver import v.errors import strings +@[ignore_overflow] fn comptime_power_i64(base i64, exponent i64) i64 { - return math.powi(base, exponent) -} - -fn comptime_power_f64(base f64, exponent f64) f64 { - return math.pow(base, exponent) + mut exp := exponent + mut power := base + mut value := i64(1) + if exp < 0 { + if base == 0 { + return -1 + } + return if base * base != 1 { + 0 + } else { + if exp & 1 > 0 { + base + } else { + 1 + } + } + } + for exp > 0 { + if exp & 1 > 0 { + value *= power + } + power *= power + exp >>= 1 + } + return value } fn comptime_power_value(left ast.ComptTimeConstValue, right ast.ComptTimeConstValue) ?ast.ComptTimeConstValue { diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 24c4b1ad2..2ea0707e3 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -3719,33 +3719,31 @@ fn (mut g Gen) current_fn_generic_params() ([]ast.Param, []string) { || g.cur_concrete_types.len == 0 { return []ast.Param{}, []string{} } - mut params := g.cur_fn.params.clone() - mut generic_names := g.cur_fn.generic_names.clone() if g.cur_fn.name.contains('_T_') { for generic_fn in g.file.generic_fns { if generic_fn.generic_names.len == 0 { continue } if g.generic_fn_name(g.cur_concrete_types, generic_fn.name) == g.cur_fn.name { - return generic_fn.params.clone(), generic_fn.generic_names.clone() + return generic_fn.params, generic_fn.generic_names } } - return params, generic_names + return g.cur_fn.params, g.cur_fn.generic_names } if func := g.table.find_fn(g.cur_fn.name) { - params = func.params.clone() if func.generic_names.len > 0 { - generic_names = func.generic_names.clone() + return func.params, func.generic_names } + return func.params, g.cur_fn.generic_names } else if g.cur_fn.mod != '' && !g.cur_fn.name.contains('.') { if func := g.table.find_fn('${g.cur_fn.mod}.${g.cur_fn.name}') { - params = func.params.clone() if func.generic_names.len > 0 { - generic_names = func.generic_names.clone() + return func.params, func.generic_names } + return func.params, g.cur_fn.generic_names } } - return params, generic_names + return g.cur_fn.params, g.cur_fn.generic_names } fn (mut g Gen) current_fn_generic_names() []string { @@ -3930,8 +3928,7 @@ fn (mut g Gen) refresh_current_generic_local_scope_vars(scope &ast.Scope) { } } -fn (mut g Gen) resolve_current_fn_generic_param_type(name string) ast.Type { - params, generic_names := g.current_fn_generic_params() +fn (mut g Gen) resolve_generic_param_type_from_params(name string, params []ast.Param, generic_names []string) ast.Type { if params.len == 0 || generic_names.len == 0 { return 0 } @@ -3947,6 +3944,42 @@ fn (mut g Gen) resolve_current_fn_generic_param_type(name string) ast.Type { return 0 } +fn (mut g Gen) resolve_current_fn_generic_param_type(name string) ast.Type { + if g.cur_fn == unsafe { nil } || g.cur_fn.generic_names.len == 0 + || g.cur_concrete_types.len == 0 { + return 0 + } + if g.cur_fn.name.contains('_T_') { + for generic_fn in g.file.generic_fns { + if generic_fn.generic_names.len == 0 { + continue + } + if g.generic_fn_name(g.cur_concrete_types, generic_fn.name) == g.cur_fn.name { + return g.resolve_generic_param_type_from_params(name, generic_fn.params, + generic_fn.generic_names) + } + } + return g.resolve_generic_param_type_from_params(name, g.cur_fn.params, + g.cur_fn.generic_names) + } + if func := g.table.find_fn(g.cur_fn.name) { + if func.generic_names.len > 0 { + return g.resolve_generic_param_type_from_params(name, func.params, func.generic_names) + } + return g.resolve_generic_param_type_from_params(name, func.params, g.cur_fn.generic_names) + } else if g.cur_fn.mod != '' && !g.cur_fn.name.contains('.') { + if func := g.table.find_fn('${g.cur_fn.mod}.${g.cur_fn.name}') { + if func.generic_names.len > 0 { + return g.resolve_generic_param_type_from_params(name, func.params, + func.generic_names) + } + return g.resolve_generic_param_type_from_params(name, func.params, + g.cur_fn.generic_names) + } + } + return g.resolve_generic_param_type_from_params(name, g.cur_fn.params, g.cur_fn.generic_names) +} + fn (mut g Gen) resolve_current_fn_generic_param_value_type(name string) ast.Type { params, generic_names := g.current_fn_generic_params() if params.len == 0 || generic_names.len == 0 { @@ -6644,7 +6677,7 @@ fn (mut g Gen) ref_or_deref_arg_ex(arg ast.CallArg, expected_type_ ast.Type, lan } } if arg.expr.obj is ast.Var && arg.expr.obj.is_arg && arg.expr.obj.is_mut && !arg.is_mut - && arg_typ.is_ptr() && !expected_type.is_any_kind_of_pointer() { + && arg_typ.is_ptr() && !expected_type.is_any_kind_of_pointer() && in_generic_context { resolved_param_type := g.resolve_current_fn_generic_param_type(arg.expr.name) if resolved_param_type != 0 && !resolved_param_type.is_ptr() { arg_typ = resolved_param_type diff --git a/vlib/v/gen/c/utils.v b/vlib/v/gen/c/utils.v index 70fda9354..26b22d2bc 100644 --- a/vlib/v/gen/c/utils.v +++ b/vlib/v/gen/c/utils.v @@ -6,6 +6,16 @@ module c import v.ast fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type { + if typ == 0 { + return typ + } + if !typ.has_flag(.generic) { + idx := typ.idx() + if idx <= ast.nil_type_idx + || (idx < g.generic_parts_cache.len && g.generic_parts_cache[idx] == 1) { + return typ + } + } mut resolved_typ := g.recheck_concrete_type(typ) if resolved_typ == 0 { resolved_typ = typ @@ -1020,9 +1030,12 @@ fn (mut g Gen) resolved_expr_type(expr ast.Expr, default_typ ast.Type) ast.Type } } } - resolved := g.resolve_current_fn_generic_param_type(expr.name) - if resolved != 0 { - return g.unwrap_generic(g.recheck_concrete_type(resolved)) + if g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0 + && g.cur_concrete_types.len > 0 { + resolved := g.resolve_current_fn_generic_param_type(expr.name) + if resolved != 0 { + return g.unwrap_generic(g.recheck_concrete_type(resolved)) + } } if expr.obj is ast.Var && expr.obj.typ != 0 { resolved_obj_type := g.unwrap_generic(g.recheck_concrete_type(expr.obj.typ)) diff --git a/vlib/v/transformer/transformer.v b/vlib/v/transformer/transformer.v index 85c639ff5..901134ce2 100644 --- a/vlib/v/transformer/transformer.v +++ b/vlib/v/transformer/transformer.v @@ -3,12 +3,16 @@ // that can be found in the LICENSE file. module transformer -import math import v.pref import v.ast import v.token import v.util +union U64F64 { + u u64 + f f64 +} + pub struct Transformer { pref &pref.Preferences pub mut: @@ -66,7 +70,7 @@ fn folded_float_literal(value f64, pos token.Pos) ast.FloatLiteral { // ast.FloatLiteral stores source text, so the folded value needs a // decimal/scientific form that reparses to the same bits. short := value.str() - if math.f64_bits(short.f64()) == math.f64_bits(value) { + if transformer_f64_bits(short.f64()) == transformer_f64_bits(value) { return ast.FloatLiteral{ val: short pos: pos @@ -79,6 +83,44 @@ fn folded_float_literal(value f64, pos token.Pos) ast.FloatLiteral { } } +@[inline] +fn transformer_f64_bits(value f64) u64 { + return unsafe { + U64F64{ + f: value + }.u + } +} + +@[ignore_overflow] +fn folded_power_i64(base i64, exponent i64) i64 { + mut exp := exponent + mut power := base + mut value := i64(1) + if exp < 0 { + if base == 0 { + return -1 + } + return if base * base != 1 { + 0 + } else { + if exp & 1 > 0 { + base + } else { + 1 + } + } + } + for exp > 0 { + if exp & 1 > 0 { + value *= power + } + power *= power + exp >>= 1 + } + return value +} + pub fn (mut t Transformer) find_new_range(node ast.AssignStmt) { if !t.pref.is_prod { return @@ -943,7 +985,7 @@ pub fn (mut t Transformer) infix_expr(mut node ast.InfixExpr) ast.Expr { } .power { return ast.IntegerLiteral{ - val: math.powi(left_val, right_val).str() + val: folded_power_i64(left_val, right_val).str() pos: pos } } @@ -1056,7 +1098,7 @@ pub fn (mut t Transformer) infix_expr(mut node ast.InfixExpr) ast.Expr { } .power { return ast.FloatLiteral{ - val: math.pow(left_val, right_val).str() + val: folded_power_f64(left_val, right_val).str() pos: pos } } diff --git a/vlib/v/util/util.v b/vlib/v/util/util.v index b439a606d..5705796ed 100644 --- a/vlib/v/util/util.v +++ b/vlib/v/util/util.v @@ -540,7 +540,12 @@ pub fn strip_main_name(name string) string { @[inline] pub fn no_dots(s string) string { - return s.replace_each(['.', '__', '-', '_']) + for ch in s { + if ch == `.` || ch == `-` { + return s.replace_each(['.', '__', '-', '_']) + } + } + return s } const map_prefix = 'map[string]' -- 2.39.5