Skip to content

Commit 2a27b72

Browse files
committed
v3: speed up cgen x8
1 parent 4db8977 commit 2a27b72

5 files changed

Lines changed: 102 additions & 16 deletions

File tree

vlib/v3/gen/c/cleanc.v

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ mut:
4343
modules map[string]string // alias -> full module name
4444
fn_ptr_types map[string]string // fn_ptr:ret|params -> typedef name
4545
fn_decl_param_types map[string][]types.Type
46+
fn_decl_ret_types map[string]types.Type // fn decl name (and qualified variants) -> return type
4647
struct_decl_infos map[string]StructDeclInfo
4748
struct_decl_short_infos map[string]StructDeclInfo
4849
runtime_inits []string
@@ -60,6 +61,7 @@ mut:
6061
emitted_optional_types map[string]bool
6162
emitted_fns map[string]bool
6263
array_method_cache map[string]string
64+
param_types_cache map[string][]types.Type // (name|fallback) -> resolved param types
6365
spawn_wrapper_names map[string]string
6466
spawn_wrapper_defs []string
6567
parallel_used bool
@@ -104,6 +106,7 @@ pub fn FlatGen.new() FlatGen {
104106
modules: map[string]string{}
105107
fn_ptr_types: map[string]string{}
106108
fn_decl_param_types: map[string][]types.Type{}
109+
fn_decl_ret_types: map[string]types.Type{}
107110
struct_decl_infos: map[string]StructDeclInfo{}
108111
struct_decl_short_infos: map[string]StructDeclInfo{}
109112
cur_param_names: []string{}
@@ -113,6 +116,7 @@ pub fn FlatGen.new() FlatGen {
113116
emitted_optional_types: map[string]bool{}
114117
emitted_fns: map[string]bool{}
115118
array_method_cache: map[string]string{}
119+
param_types_cache: map[string][]types.Type{}
116120
spawn_wrapper_names: map[string]string{}
117121
spawn_wrapper_defs: []string{}
118122
str_lits: []string{}
@@ -171,6 +175,7 @@ pub fn (mut g FlatGen) gen_with_used_options(a &flat.FlatAst, used_fns map[strin
171175
g.modules = map[string]string{}
172176
g.fn_ptr_types = map[string]string{}
173177
g.fn_decl_param_types = map[string][]types.Type{}
178+
g.fn_decl_ret_types = map[string]types.Type{}
174179
g.struct_decl_infos = map[string]StructDeclInfo{}
175180
g.struct_decl_short_infos = map[string]StructDeclInfo{}
176181
g.cur_param_names = []string{}
@@ -180,6 +185,7 @@ pub fn (mut g FlatGen) gen_with_used_options(a &flat.FlatAst, used_fns map[strin
180185
g.emitted_optional_types = map[string]bool{}
181186
g.emitted_fns = map[string]bool{}
182187
g.array_method_cache = map[string]string{}
188+
g.param_types_cache = map[string][]types.Type{}
183189
g.spawn_wrapper_names = map[string]string{}
184190
g.spawn_wrapper_defs = []string{}
185191
g.parallel_used = false
@@ -284,6 +290,7 @@ fn (mut g FlatGen) collect_gen_info() {
284290
}
285291
ptypes = g.fn_param_types_with_implicit_veb_ctx(node, ptypes)
286292
g.register_fn_decl_param_types(node.value, full_name, ptypes)
293+
g.register_fn_decl_ret_type(node.value, full_name, node.typ)
287294
// Module-level `init()` functions run once at startup. Collect their C
288295
// names so _vinit can invoke them (V semantics).
289296
if node.value == 'init' && ptypes.len == 0 {
@@ -708,6 +715,29 @@ fn (mut g FlatGen) register_fn_decl_param_types(name string, full_name string, p
708715
}
709716
}
710717

718+
// register_fn_decl_ret_type indexes a fn decl's return type by its name (and qualified
719+
// variants), so the return type can be looked up in O(1) instead of scanning all AST
720+
// nodes per call (see fn_decl_return_type_for_call_name).
721+
fn (mut g FlatGen) register_fn_decl_ret_type(name string, full_name string, ret_typ string) {
722+
rt := g.tc.parse_type(ret_typ)
723+
if name !in g.fn_decl_ret_types {
724+
g.fn_decl_ret_types[name] = rt
725+
}
726+
if g.tc.cur_module.len > 0 && g.tc.cur_module != 'main' && g.tc.cur_module != 'builtin' {
727+
dotted_name := '${g.tc.cur_module}.${name}'
728+
if dotted_name !in g.fn_decl_ret_types {
729+
g.fn_decl_ret_types[dotted_name] = rt
730+
}
731+
}
732+
if full_name !in g.fn_decl_ret_types {
733+
g.fn_decl_ret_types[full_name] = rt
734+
}
735+
cname := c_name(name)
736+
if cname != name && cname !in g.fn_decl_ret_types {
737+
g.fn_decl_ret_types[cname] = rt
738+
}
739+
}
740+
711741
// register_struct_decl_info updates register struct decl info state for c.
712742
fn (mut g FlatGen) register_struct_decl_info(name string, full_name string, module_name string, node flat.Node) {
713743
info := StructDeclInfo{

vlib/v3/gen/c/fn.v

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,7 +2000,21 @@ fn (g &FlatGen) normalize_call_key(name string) string {
20002000
}
20012001

20022002
// param_types_for supports param types for handling for FlatGen.
2003+
// param_types_for resolves the parameter types of a called function. It is invoked once
2004+
// per call site during codegen, and the slow path below scans every known function, so
2005+
// results are memoized: without this, generic/monomorphized call names that miss the
2006+
// direct lookups re-scan (and copy) the whole function table on every call (O(n^2)).
20032007
fn (mut g FlatGen) param_types_for(name string, fallback string) []types.Type {
2008+
cache_key := '${name}\x01${fallback}'
2009+
if cached := g.param_types_cache[cache_key] {
2010+
return cached
2011+
}
2012+
result := g.param_types_for_uncached(name, fallback)
2013+
g.param_types_cache[cache_key] = result
2014+
return result
2015+
}
2016+
2017+
fn (mut g FlatGen) param_types_for_uncached(name string, fallback string) []types.Type {
20042018
if name in ['json2.LinkedList[ValueInfo].push', 'json2.LinkedList_ValueInfo.push'] {
20052019
return [
20062020
g.tc.parse_type('&json2.LinkedList[ValueInfo]'),
@@ -2031,14 +2045,17 @@ fn (mut g FlatGen) param_types_for(name string, fallback string) []types.Type {
20312045
}
20322046
if name.contains('.') {
20332047
short_name := name.all_after_last('.')
2034-
for candidate, ptypes in g.fn_decl_param_types {
2035-
if candidate.ends_with('.${short_name}') {
2036-
return ptypes
2048+
suffix := '.${short_name}'
2049+
// Look up the value only on a match: `for _, v in map` copies every array value
2050+
// on every iteration, which is wasteful for a table-wide scan.
2051+
for candidate, _ in g.fn_decl_param_types {
2052+
if candidate.ends_with(suffix) {
2053+
return g.fn_decl_param_types[candidate]
20372054
}
20382055
}
2039-
for candidate, ptypes in g.tc.fn_param_types {
2040-
if candidate.ends_with('.${short_name}') {
2041-
return ptypes
2056+
for candidate, _ in g.tc.fn_param_types {
2057+
if candidate.ends_with(suffix) {
2058+
return g.tc.fn_param_types[candidate]
20422059
}
20432060
}
20442061
}

vlib/v3/gen/c/fn_d_parallel.v

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ fn (g &FlatGen) new_parallel_worker(worker_id int) &FlatGen {
354354
modules: g.modules.clone()
355355
fn_ptr_types: g.fn_ptr_types.clone()
356356
fn_decl_param_types: g.fn_decl_param_types.clone()
357+
fn_decl_ret_types: g.fn_decl_ret_types.clone()
357358
struct_decl_infos: g.struct_decl_infos.clone()
358359
struct_decl_short_infos: g.struct_decl_short_infos.clone()
359360
runtime_inits: g.runtime_inits.clone()
@@ -370,6 +371,7 @@ fn (g &FlatGen) new_parallel_worker(worker_id int) &FlatGen {
370371
emitted_optional_types: g.emitted_optional_types.clone()
371372
emitted_fns: g.emitted_fns.clone()
372373
array_method_cache: g.array_method_cache.clone()
374+
param_types_cache: g.param_types_cache.clone()
373375
}
374376
}
375377

vlib/v3/gen/c/names.v

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,45 @@ module c
22

33
import strings
44

5-
const c_reserved_words = ['auto', 'break', 'case', 'char', 'const', 'continue', 'copy', 'default',
6-
'do', 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', 'long',
7-
'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
8-
'typedef', 'union', 'unsigned', 'void', 'volatile', 'while']
5+
// c_reserved_words is a set (not a list) so `name in c_reserved_words` is an O(1) hash
6+
// lookup. c_name() runs on every emitted identifier, so a linear scan here is costly.
7+
const c_reserved_words = {
8+
'auto': true
9+
'break': true
10+
'case': true
11+
'char': true
12+
'const': true
13+
'continue': true
14+
'copy': true
15+
'default': true
16+
'do': true
17+
'double': true
18+
'else': true
19+
'enum': true
20+
'extern': true
21+
'float': true
22+
'for': true
23+
'goto': true
24+
'if': true
25+
'inline': true
26+
'int': true
27+
'long': true
28+
'register': true
29+
'restrict': true
30+
'return': true
31+
'short': true
32+
'signed': true
33+
'sizeof': true
34+
'static': true
35+
'struct': true
36+
'switch': true
37+
'typedef': true
38+
'union': true
39+
'unsigned': true
40+
'void': true
41+
'volatile': true
42+
'while': true
43+
}
944

1045
// c_name converts c name data for c.
1146
fn c_name(name string) string {

vlib/v3/gen/c/stmt.v

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -673,13 +673,15 @@ fn (g &FlatGen) fn_decl_return_type_for_call_name(name string) ?types.Type {
673673
if name.len == 0 {
674674
return none
675675
}
676+
// Indexed in collect_gen_info (register_fn_decl_ret_type); previously this scanned
677+
// every AST node per call (O(n^2)) and re-mangled each decl name with c_name.
678+
if rt := g.fn_decl_ret_types[name] {
679+
return rt
680+
}
676681
cname := c_name(name)
677-
for node in g.a.nodes {
678-
if node.kind != .fn_decl && node.kind != .c_fn_decl {
679-
continue
680-
}
681-
if node.value == name || c_name(node.value) == cname {
682-
return g.tc.parse_type(node.typ)
682+
if cname != name {
683+
if rt := g.fn_decl_ret_types[cname] {
684+
return rt
683685
}
684686
}
685687
return none

0 commit comments

Comments
 (0)