v.pref: support `v -skip-unused run examples/hello_world.v`
							parent
							
								
									80697ec7f3
								
							
						
					
					
						commit
						40fff7b56a
					
				|  | @ -12,8 +12,6 @@ import v.pref | ||||||
| import v.util | import v.util | ||||||
| import v.errors | import v.errors | ||||||
| import v.pkgconfig | import v.pkgconfig | ||||||
| import v.walker |  | ||||||
| import time |  | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	max_nr_errors                 = 300 | 	max_nr_errors                 = 300 | ||||||
|  | @ -229,26 +227,8 @@ pub fn (mut c Checker) check_files(ast_files []ast.File) { | ||||||
| 	} else if !has_main_fn { | 	} else if !has_main_fn { | ||||||
| 		c.error('function `main` must be declared in the main module', token.Position{}) | 		c.error('function `main` must be declared in the main module', token.Position{}) | ||||||
| 	} | 	} | ||||||
| 	// Walk the tree starting at main() and mark all used fns.
 | 	if c.pref.skip_unused { | ||||||
| 	if c.pref.experimental { | 		c.mark_used(ast_files) | ||||||
| 		println('walking the ast') |  | ||||||
| 		// c.is_recursive = true
 |  | ||||||
| 		// c.fn_decl(mut c.table2.main_fn_decl_node)
 |  | ||||||
| 		t := time.ticks() |  | ||||||
| 		mut walker := walker.Walker{ |  | ||||||
| 			files: ast_files |  | ||||||
| 		} |  | ||||||
| 		for stmt in c.main_fn_decl_node.stmts { |  | ||||||
| 			walker.stmt(stmt) |  | ||||||
| 		} |  | ||||||
| 		// 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) |  | ||||||
| 		} |  | ||||||
| 		// println(walker.used_fns)
 |  | ||||||
| 		// c.walk(ast_files)
 |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,92 @@ | ||||||
|  | module checker | ||||||
|  | 
 | ||||||
|  | import v.ast | ||||||
|  | import v.table | ||||||
|  | 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)
 | ||||||
|  | 
 | ||||||
|  | 	// c.timing_measure(@FN)
 | ||||||
|  | 	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) | ||||||
|  | 	} | ||||||
|  | 	// 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) | ||||||
|  | 	} | ||||||
|  | 	*/ | ||||||
|  | 	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_linux'] = 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.timing_measure(@FN)
 | ||||||
|  | 
 | ||||||
|  | 	// println(walker.used_fns)
 | ||||||
|  | 	// c.walk(ast_files)
 | ||||||
|  | } | ||||||
|  | @ -1,9 +1,8 @@ | ||||||
| // Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
 | // Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
 | ||||||
| // Use of this source code is governed by an MIT license that can be found in the LICENSE file.
 | // Use of this source code is governed by an MIT license that can be found in the LICENSE file.
 | ||||||
| module walker | module mark_used_walker | ||||||
| 
 | 
 | ||||||
| // This module walks the entire program starting at fn main and marks used (called)
 | // This module walks the entire program starting at fn main and marks used (called) functions.
 | ||||||
| // functions.
 |  | ||||||
| // Unused functions can be safely skipped by the backends to save CPU time and space.
 | // Unused functions can be safely skipped by the backends to save CPU time and space.
 | ||||||
| import v.ast | import v.ast | ||||||
| 
 | 
 | ||||||
|  | @ -91,7 +90,7 @@ pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) { | ||||||
| 	if node.language == .c { | 	if node.language == .c { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	println('fn decl $fn_name') | 	// println('fn decl $fn_name')
 | ||||||
| 	w.used_fns[fn_name] = true | 	w.used_fns[fn_name] = true | ||||||
| 	for stmt in node.stmts { | 	for stmt in node.stmts { | ||||||
| 		w.stmt(stmt) | 		w.stmt(stmt) | ||||||
|  | @ -102,7 +101,7 @@ pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) { | ||||||
| pub fn (mut w Walker) call_expr(mut node ast.CallExpr) { | 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 } | 	fn_name := if node.is_method { node.receiver_type.str() + '.' + node.name } else { node.name } | ||||||
| 	// fn_name := node.name
 | 	// fn_name := node.name
 | ||||||
| 	println('call_expr $fn_name') | 	// println('call_expr $fn_name')
 | ||||||
| 	// if node.is_method {
 | 	// if node.is_method {
 | ||||||
| 	// println('M $node.name $node.receiver_type')
 | 	// println('M $node.name $node.receiver_type')
 | ||||||
| 	//}
 | 	//}
 | ||||||
|  | @ -27,13 +27,12 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	*/ | 	*/ | ||||||
| 	if g.pref.experimental { | 	if g.pref.skip_unused { | ||||||
| 		if f := g.table.find_fn(node.name) { | 		is_used_by_main := g.table.used_fns[ node.name ] | ||||||
| 			println('> usages: ${f.usages:-10} | node.name: $node.name') | 		// println('> is_used_by_main: $is_used_by_main | node.name: $node.name')
 | ||||||
| 			if f.usages == 0 { | 		if !is_used_by_main { | ||||||
| 				g.writeln('// fn $node.name UNUSED') | 			g.writeln('// fn $node.name UNUSED') | ||||||
| 				return | 			return | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -136,6 +136,7 @@ pub mut: | ||||||
| 	is_vweb             bool // skip _ var warning in templates
 | 	is_vweb             bool // skip _ var warning in templates
 | ||||||
| 	only_check_syntax   bool // when true, just parse the files, then stop, before running checker
 | 	only_check_syntax   bool // when true, just parse the files, then stop, before running checker
 | ||||||
| 	experimental        bool // enable experimental features
 | 	experimental        bool // enable experimental features
 | ||||||
|  | 	skip_unused         bool // skip generating C code for functions, that are not used
 | ||||||
| 	show_timings        bool // show how much time each compiler stage took
 | 	show_timings        bool // show how much time each compiler stage took
 | ||||||
| 	is_ios_simulator    bool | 	is_ios_simulator    bool | ||||||
| 	is_apk              bool     // build as Android .apk format
 | 	is_apk              bool     // build as Android .apk format
 | ||||||
|  | @ -221,6 +222,9 @@ pub fn parse_args(args []string) (&Preferences, string) { | ||||||
| 				res.autofree = false | 				res.autofree = false | ||||||
| 				res.build_options << arg | 				res.build_options << arg | ||||||
| 			} | 			} | ||||||
|  | 			'-skip-unused' { | ||||||
|  | 				res.skip_unused = true | ||||||
|  | 			} | ||||||
| 			'-compress' { | 			'-compress' { | ||||||
| 				res.compress = true | 				res.compress = true | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | @ -19,6 +19,7 @@ pub mut: | ||||||
| 	fn_gen_types  map[string][][]Type // for generic functions
 | 	fn_gen_types  map[string][][]Type // for generic functions
 | ||||||
| 	cmod_prefix   string // needed for table.type_to_str(Type) while vfmt; contains `os.`
 | 	cmod_prefix   string // needed for table.type_to_str(Type) while vfmt; contains `os.`
 | ||||||
| 	is_fmt        bool | 	is_fmt        bool | ||||||
|  | 	used_fns      map[string]bool // filled in by the checker, when pref.skip_unused = true;
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct Fn { | pub struct Fn { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue