From 167773dd1c795cdd87ec8c17a3f6f8f8565c3dab Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sat, 6 Feb 2021 09:34:01 +0200 Subject: [PATCH] checker: make -skip-unused process a list of root functions, not just main.main --- vlib/v/checker/mark_used.v | 175 +++++++++++------------ vlib/v/checker/mark_used_walker/walker.v | 15 +- vlib/v/gen/c/fn.v | 7 +- 3 files changed, 98 insertions(+), 99 deletions(-) diff --git a/vlib/v/checker/mark_used.v b/vlib/v/checker/mark_used.v index aa9235a11a..3b3268101c 100644 --- a/vlib/v/checker/mark_used.v +++ b/vlib/v/checker/mark_used.v @@ -7,103 +7,102 @@ import v.checker.mark_used_walker // mark_used walks the AST, starting at main() and marks all used fns transitively fn (mut c Checker) mark_used(ast_files []ast.File) { - // println('walking the ast') - // c.is_recursive = true - // c.fn_decl(mut c.table2.main_fn_decl_node) - util.timing_start(@METHOD) mut walker := mark_used_walker.Walker{ files: ast_files } - // TODO: walking over the AST using roots != main.main, like `panic` - // for example, will potentially eliminate many cases where manual - // whitelisting is needed. - // TODO: use the full name of the functions in the map. For now the - // receiver type is skipped, so we can not distinguish between - // array.trim and string.trim etc. - for stmt in c.main_fn_decl_node.stmts { - walker.stmt(stmt) + mut allfns := map[string]ast.FnDecl{} + for i in 0 .. ast_files.len { + file := unsafe { &ast_files[i] } + for node in file.stmts { + if node is ast.FnDecl { + fkey := if node.is_method { + '${int(node.receiver.typ)}.$node.name' + } else { + node.name + } + allfns[fkey] = node + } + } + } + all_fn_root_names := [ + 'main.main', + '__new_array', + '__new_array_with_default', + 'new_array_from_c_array', + 'panic', + 'memdup', + 'vstrlen', + 'tos2', + 'tos', + 'isnil', + 'utf8_char_len', + 'utf8_str_visible_length', + 'builtin_init', + 'print_backtrace_skipping_top_frames', + 'print_backtrace_skipping_top_frames_mac', + 'print_backtrace_skipping_top_frames_linux', + 'print_backtrace_skipping_top_frames_freebsd', + 'print_backtrace_skipping_top_frames_windows', + 'print_backtrace_skipping_top_frames_mingw', + 'print_backtrace_skipping_top_frames_msvc', + 'print_backtrace_skipping_top_frames_tcc', + /* byteptr and charptr */ + '3.vstring', + '3.vstring_with_len', + '4.vstring', + '4.vstring_with_len', + /* string. methods */ + '18.add', + '18.all_after', + '18.all_before', + '18.trim_space', + '18.replace', + '18.clone', + '18.trim', + '18.substr', + '18.at', + '18.index_kmp', + /* string. ==, !=, etc... */ + '18.eq', + '18.ne', + '18.lt', + '18.gt', + '18.le', + '18.ge', + /* ustring. ==, !=, etc... */ + '19.eq', + '19.ne', + '19.lt', + '19.gt', + '19.le', + '19.ge', + '19.add', + /* other array methods */ + '21.get', + '21.get_unsafe', + '59.get', + '65557.free', + '65557.push', + '65557.set', + '65557.set_unsafe', + /* TODO: process the _vinit const initializations automatically too */ + 'os.getwd', + 'os.init_os_args', + ] + // println( allfns.keys() ) + for fn_name in all_fn_root_names { + walker.fn_decl(mut allfns[fn_name]) } - // walker.fn_decl(mut c.table2.main_fn_decl_node) - // println('time = ${time.ticks() - t}ms, nr used fns=$walker.used_fns.len') - /* - for key, _ in walker.used_fns { - println(key) + $if trace_skip_unused ? { + for key, _ in walker.used_fns { + println('> used fn key: $key') + } } - */ + c.table.used_fns = walker.used_fns - - // Whitelist some functions that are used by cgen directly: - c.table.used_fns['tos'] = true - c.table.used_fns['tos2'] = true - c.table.used_fns['v_panic'] = true - c.table.used_fns['panic'] = true - c.table.used_fns['eprintln'] = true - c.table.used_fns['print_backtrace_skipping_top_frames'] = true - c.table.used_fns['print_backtrace_skipping_top_frames_mac'] = true - c.table.used_fns['print_backtrace_skipping_top_frames_linux'] = true - c.table.used_fns['print_backtrace_skipping_top_frames_freebsd'] = true - c.table.used_fns['print_backtrace_skipping_top_frames_windows'] = true - c.table.used_fns['print_backtrace_skipping_top_frames_mingw'] = true - c.table.used_fns['print_backtrace_skipping_top_frames_msvc'] = true - c.table.used_fns['print_backtrace_skipping_top_frames_tcc'] = true - c.table.used_fns['is_atty'] = true - c.table.used_fns['add_unhandled_exception_handler'] = true - c.table.used_fns['add_vectored_exception_handler'] = true - c.table.used_fns['unhandled_exception_handler'] = true - c.table.used_fns['restore_codepage'] = true - c.table.used_fns['new_array_from_c_array'] = true - c.table.used_fns['__new_array_with_default'] = true - c.table.used_fns['__new_array'] = true - c.table.used_fns['vcalloc'] = true - c.table.used_fns['set_unsafe'] = true - c.table.used_fns['get_unsafe'] = true - c.table.used_fns['push'] = true - c.table.used_fns['ensure_cap'] = true - c.table.used_fns['v_realloc'] = true - c.table.used_fns['all_before'] = true - c.table.used_fns['all_after'] = true - c.table.used_fns['add'] = true - c.table.used_fns['isnil'] = true - c.table.used_fns['vstrlen'] = true - c.table.used_fns['trim_space'] = true - c.table.used_fns['malloc'] = true - c.table.used_fns['trim'] = true - c.table.used_fns['at'] = true - c.table.used_fns['replace'] = true - c.table.used_fns['index_'] = true - c.table.used_fns['index_after'] = true - c.table.used_fns['index_kmp'] = true - c.table.used_fns['substr'] = true - c.table.used_fns['clone'] = true - c.table.used_fns['free'] = true - c.table.used_fns['has_index'] = true - c.table.used_fns['key'] = true - c.table.used_fns['set'] = true - c.table.used_fns['get'] = true - c.table.used_fns['new_node'] = true - c.table.used_fns['eq'] = true - c.table.used_fns['ne'] = true - c.table.used_fns['lt'] = true - c.table.used_fns['gt'] = true - c.table.used_fns['le'] = true - c.table.used_fns['ge'] = true - c.table.used_fns['split_child'] = true - c.table.used_fns['bytes'] = true - c.table.used_fns['utf8_char_len'] = true - c.table.used_fns['utf8_str_visible_length'] = true - c.table.used_fns['main.main'] = true - c.table.used_fns['builtin_init'] = true - c.table.used_fns['memdup'] = true - c.table.used_fns['vstring'] = true - c.table.used_fns['vstring_with_len'] = true - c.table.used_fns['string'] = true // array.string - // c.table.used_fns['str'] = true // builtin .str() methods; They use strings.builder and strconv.ftoa_64 etc. - // whitelist common modules const initializers too: - c.table.used_fns['os.getwd'] = true - c.table.used_fns['os.init_os_args'] = true // c.table.used_fns['term.can_show_color_on_stdin'] = true c.table.used_fns['term.can_show_color_on_stdout'] = true diff --git a/vlib/v/checker/mark_used_walker/walker.v b/vlib/v/checker/mark_used_walker/walker.v index 14d7b58fd9..24af677e43 100644 --- a/vlib/v/checker/mark_used_walker/walker.v +++ b/vlib/v/checker/mark_used_walker/walker.v @@ -31,9 +31,9 @@ pub fn (mut w Walker) stmt(node ast.Stmt) { ast.ExprStmt { w.expr(node.expr) } - // ast.FnDecl { - // w.fn_decl(mut node) - //} + ast.FnDecl { + w.fn_decl(mut node) + } ast.ForStmt { w.expr(node.cond) for stmt in node.stmts { @@ -79,10 +79,9 @@ fn (mut w Walker) expr(node ast.Expr) { } } -/* pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) { - fn_name := if node.is_method { node.receiver.typ.str() + '.' + node.name } else { node.name } - if w.used_fns[fn_name] { + fkey := if node.is_method { '${int(node.receiver.typ)}.$node.name' } else { node.name } + if w.used_fns[fkey] { // This function is already known to be called, meaning it has been processed already. // Save CPU time and do nothing. return @@ -90,13 +89,11 @@ pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) { if node.language == .c { return } - // println('fn decl $fn_name') - w.used_fns[fn_name] = true + w.used_fns[fkey] = true for stmt in node.stmts { w.stmt(stmt) } } -*/ pub fn (mut w Walker) call_expr(mut node ast.CallExpr) { fn_name := if node.is_method { node.receiver_type.str() + '.' + node.name } else { node.name } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index dc103737f5..48576c25e6 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -28,8 +28,11 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) { } */ if g.pref.skip_unused { - is_used_by_main := g.table.used_fns[node.name] - // println('> is_used_by_main: $is_used_by_main | node.name: $node.name') + fkey := if node.is_method { '${int(node.receiver.typ)}.$node.name' } else { node.name } + is_used_by_main := g.table.used_fns[fkey] + $if trace_skip_unused ? { + println('> is_used_by_main: $is_used_by_main | node.name: $node.name | fkey: $fkey | node.is_method: $node.is_method') + } if !is_used_by_main { g.writeln('// fn $node.name UNUSED') return