From 9dd86f6fb86e3ac15d36f2013cda9b6e2f69b090 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Mon, 9 Sep 2019 16:22:39 +0300 Subject: [PATCH] compiler: more memory logic + replace "cur_fn &Fn" with "cur_fn Fn" --- compiler/fn.v | 53 +++++++++++-------------- compiler/main.v | 8 ++-- compiler/parser.v | 89 ++++++++++++++++++++++++++---------------- compiler/table.v | 2 + vlib/builtin/builtin.v | 14 ++++--- vlib/builtin/map.v | 19 ++++++++- vlib/builtin/string.v | 2 +- vlib/os/os.v | 5 +-- 8 files changed, 114 insertions(+), 78 deletions(-) diff --git a/compiler/fn.v b/compiler/fn.v index fb5dac609e..3b1c2d3d8a 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -43,43 +43,31 @@ fn (f &Fn) find_var(name string) Var { } -fn (f mut Fn) open_scope() { - f.defer_text << '' - f.scope_level++ +fn (p mut Parser) open_scope() { + p.cur_fn.defer_text << '' + p.cur_fn.scope_level++ } -fn (f mut Fn) close_scope() { - f.scope_level-- - f.defer_text = f.defer_text.left(f.scope_level + 1) -} - -fn (f mut Fn) mark_var_used(v Var) { - for i, vv in f.local_vars { +fn (p mut Parser) mark_var_used(v Var) { + for i, vv in p.cur_fn.local_vars { if vv.name == v.name { - //mut ptr := &f.local_vars[i] - //ptr.is_used = true - f.local_vars[i].is_used = true - return + p.cur_fn.local_vars[i].is_used = true } } } -fn (f mut Fn) mark_var_returned(v Var) { - for i, vv in f.local_vars { +fn (p mut Parser) mark_var_returned(v Var) { + for i, vv in p.cur_fn.local_vars { if vv.name == v.name { - f.local_vars[i].is_returned = true - return + p.cur_fn.local_vars[i].is_returned = true } } } -fn (f mut Fn) mark_var_changed(v Var) { - for i, vv in f.local_vars { +fn (p mut Parser) mark_var_changed(v Var) { + for i, vv in p.cur_fn.local_vars { if vv.name == v.name { - //mut ptr := &f.local_vars[i] - //ptr.is_used = true - f.local_vars[i].is_changed = true - // return + p.cur_fn.local_vars[i].is_changed = true } } } @@ -112,8 +100,8 @@ fn (p mut Parser) is_sig() bool { (p.file_path.contains(ModPath)) } -fn new_fn(mod string, is_public bool) &Fn { - return &Fn { +fn new_fn(mod string, is_public bool) Fn { + return Fn { mod: mod local_vars: [Var{} ; MaxLocalVars] is_public: is_public @@ -158,8 +146,9 @@ fn (p mut Parser) fn_decl() { println('p.mod=$p.mod') p.error('cannot define new methods on non-local type `$receiver_typ`') } - // (a *Foo) instead of (a mut Foo) is a common mistake - if !p.builtin_mod && receiver_typ.contains('*') { + // `(f *Foo)` instead of `(f mut Foo)` is a common mistake + //if !p.builtin_mod && receiver_typ.contains('*') { + if receiver_typ.contains('*') { t := receiver_typ.replace('*', '') p.error('use `($receiver_name mut $t)` instead of `($receiver_name *$t)`') } @@ -463,7 +452,9 @@ _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0); if p.pref.is_prof && f.name != 'main' && f.name != 'time__ticks' { p.genln('double _PROF_START = time__ticks();//$f.name') cgen_name := p.table.cgen_name(f) + if f.defer_text.len > f.scope_level { f.defer_text[f.scope_level] = ' ${cgen_name}_time += time__ticks() - _PROF_START;' + } } if is_generic { // Don't need to generate body for the actual generic definition @@ -477,7 +468,9 @@ _thread_so = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&reload_so, 0, 0, 0); } // Counting or not, always need to add defer before the end if !p.is_vweb { + if f.defer_text.len > f.scope_level { p.genln(f.defer_text[f.scope_level]) + } } if typ != 'void' && !p.returns && f.name != 'main' && f.name != 'WinMain' { p.error('$f.name must return "$typ"') @@ -658,7 +651,7 @@ fn (p mut Parser) fn_call(f Fn, method_ph int, receiver_var, receiver_type strin p.error('`$p.expr_var.name` is immutable, declare it with `mut`') } if !p.expr_var.is_changed { - p.cur_fn.mark_var_changed(p.expr_var) + p.mark_var_changed(p.expr_var) } // if receiver is key_mut or a ref (&), generate & for the first arg if receiver.ref || (receiver.is_mut && !receiver_type.contains('*')) { @@ -837,7 +830,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn { p.error('`$arg.name` is a mutable argument, you need to provide a variable to modify: `$f.name(... mut a...)`') } if !v.is_changed { - p.cur_fn.mark_var_changed(v) + p.mark_var_changed(v) } } p.expected_type = arg.typ diff --git a/compiler/main.v b/compiler/main.v index 91bc9063c2..25c5934508 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -187,7 +187,7 @@ fn main() { if v.pref.is_test { v.run_compiled_executable_and_exit() } - + } fn (v mut V) compile() { @@ -234,8 +234,8 @@ fn (v mut V) compile() { imports_json := 'json' in v.table.imports // TODO remove global UI hack - if v.os == .mac && ((v.pref.build_mode == .embed_vlib && 'ui' in - v.table.imports) || (v.pref.build_mode == .build_module && + if v.os == .mac && ((v.pref.build_mode == .embed_vlib && 'ui' in + v.table.imports) || (v.pref.build_mode == .build_module && v.dir.contains('/ui'))) { cgen.genln('id defaultFont = 0; // main.v') } @@ -992,5 +992,5 @@ fn vhash() string { mut buf := [50]byte buf[0] = 0 C.snprintf(buf, 50, '%s', C.V_COMMIT_HASH ) - return tos_clone(buf) + return tos_clone(buf) } diff --git a/compiler/parser.v b/compiler/parser.v index 7b36357f13..cbd0bf2487 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -40,7 +40,7 @@ mut: expected_type string tmp_cnt int is_script bool - pref &Preferences // Setting and Preferences shared from V struct + pref &Preferences // Preferences shared from V struct builtin_mod bool vh_lines []string inside_if_expr bool @@ -51,7 +51,7 @@ mut: for_expr_cnt int // to detect whether `continue` can be used ptr_cast bool calling_c bool - cur_fn &Fn + cur_fn Fn returns bool vroot string is_c_struct_init bool @@ -71,8 +71,8 @@ mut: } const ( - EmptyFn = &Fn { } - MainFn= &Fn{name:'main'} + EmptyFn = Fn { } + MainFn= Fn{name:'main'} ) const ( @@ -121,8 +121,9 @@ fn (v mut V) new_parser(path string) Parser { return p } -fn (p mut Parser) set_current_fn(f &Fn) { +fn (p mut Parser) set_current_fn(f Fn) { p.cur_fn = f + //p.cur_fn = p.table.fns[f.name] p.scanner.fn_name = '${f.mod}.${f.name}' } @@ -882,9 +883,10 @@ fn (p mut Parser) get_type() string { return typ } // + mut warn := false for p.tok == .mul { if p.first_pass() { - p.warn('use `&Foo` instead of `*Foo`') + warn = true } mul = true nr_muls++ @@ -908,6 +910,9 @@ fn (p mut Parser) get_type() string { typ = p.lit } else { + if warn { + p.warn('use `&Foo` instead of `*Foo`') + } // Module specified? (e.g. gx.Image) if p.peek() == .dot { // try resolve full submodule @@ -1010,7 +1015,7 @@ fn (p mut Parser) statements() string { } fn (p mut Parser) statements_no_rcbr() string { - p.cur_fn.open_scope() + p.open_scope() if !p.inside_if_expr { p.genln('') @@ -1055,39 +1060,45 @@ fn (p mut Parser) close_scope() { mut i := p.cur_fn.var_idx - 1 for ; i >= 0; i-- { v := p.cur_fn.local_vars[i] + //if p.cur_fn.name == 'main' { + //println('var in main $v.name $v.typ $v.is_alloc ptr=$v.ptr') + //} if v.scope_level ≠ p.cur_fn.scope_level { // println('breaking. "$v.name" v.scope_level=$v.scope_level') break } // Clean up memory, only do this for V compiler for now if p.pref.building_v && v.is_alloc && !p.pref.is_test { + mut free_fn := 'free' if v.typ.starts_with('array_') { - //if false && p.returns { - if p.returns { - if !v.is_returned { - prev_line := p.cgen.lines[p.cgen.lines.len-2] - p.cgen.lines[p.cgen.lines.len-2] = - 'v_array_free($v.name); /* :) close_scope free */' + prev_line - } - } else { - p.genln('v_array_free($v.name); // close_scope free') + free_fn = 'v_array_free' + } else if v.typ == 'string' { + free_fn = 'v_string_free' + continue + } else if v.ptr || v.typ.ends_with('*') { + free_fn = 'v_ptr_free' + //continue + } else { + continue + } + //if false && p.returns { + if p.returns { + if !v.is_returned && v.typ != 'FILE*' { //!v.is_c { + prev_line := p.cgen.lines[p.cgen.lines.len-2] + p.cgen.lines[p.cgen.lines.len-2] = + '$free_fn($v.name); /* :) close_scope free $v.typ */' + prev_line } - } - else if v.typ == 'string' { - //p.genln('v_string_free($v.name); // close_scope free') - } - else if v.ptr { - //p.genln('free($v.name); // close_scope free') + } else { + p.genln('$free_fn($v.name); // close_scope free') } } } - if p.cur_fn.defer_text.last() != '' { p.genln(p.cur_fn.defer_text.last()) //p.cur_fn.defer_text[f] = '' } - - p.cur_fn.close_scope() + p.cur_fn.scope_level-- + p.cur_fn.defer_text = p.cur_fn.defer_text.left(p.cur_fn.scope_level + 1) p.cur_fn.var_idx = i + 1 // println('close_scope new var_idx=$f.var_idx\n') } @@ -1225,7 +1236,7 @@ fn ($v.name mut $v.typ) $p.cur_fn.name (...) { p.error('`$v.name` is immutable.') } if !v.is_changed { - p.cur_fn.mark_var_changed(v) + p.mark_var_changed(v) } is_str := v.typ == 'string' switch tok { @@ -1270,7 +1281,7 @@ fn ($v.name mut $v.typ) $p.cur_fn.name (...) { // p.assigned_var = '' p.assigned_type = '' if !v.is_used { - p.cur_fn.mark_var_used(v) + p.mark_var_used(v) } } @@ -1337,6 +1348,7 @@ fn (p mut Parser) var_decl() { is_mut: is_mut is_alloc: p.is_alloc }) + //if p.is_alloc { println('REG VAR IS ALLOC $name') } if !or_else { gen_name := p.table.var_cgen_name(name) mut nt_gen := p.table.cgen_name_type_pair(gen_name, typ) @@ -1544,7 +1556,7 @@ fn (p mut Parser) name_expr() string { } if p.inside_return_expr { //println('marking $v.name returned') - p.cur_fn.mark_var_returned(v) + p.mark_var_returned(v) // v.is_returned = true // TODO modifying a local variable // that's not used afterwards, this should be a compilation // error @@ -1706,13 +1718,17 @@ fn (p mut Parser) name_expr() string { return typ } p.log('end of name_expr') + + if f.typ.ends_with('*') { + p.is_alloc = true + } return f.typ } fn (p mut Parser) var_expr(v Var) string { p.log('\nvar_expr() v.name="$v.name" v.typ="$v.typ"') // println('var expr is_tmp=$p.cgen.is_tmp\n') - p.cur_fn.mark_var_used(v) + p.mark_var_used(v) fn_ph := p.cgen.add_placeholder() p.expr_var = v p.gen(p.table.var_cgen_name(v.name)) @@ -1761,7 +1777,7 @@ fn (p mut Parser) var_expr(v Var) string { p.error('`$v.name` is immutable') } if !v.is_changed { - p.cur_fn.mark_var_changed(v) + p.mark_var_changed(v) } if typ != 'int' { if !p.pref.translated && !is_number_type(typ) { @@ -1895,6 +1911,9 @@ struct $f.parent_fn { // if is_indexer { //return p.index_expr(method.typ, method_ph) //} + if method.typ.ends_with('*') { + p.is_alloc = true + } return method.typ } @@ -2148,7 +2167,7 @@ fn (p mut Parser) expression() string { p.error('`$p.expr_var.name` is immutable (can\'t <<)') } if !p.expr_var.is_changed { - p.cur_fn.mark_var_changed(p.expr_var) + p.mark_var_changed(p.expr_var) } expr_type := p.expression() // Two arrays of the same type? @@ -2883,7 +2902,9 @@ fn (p mut Parser) struct_init(typ string, is_c_struct_init bool) string { p.check(.rcbr) return typ } - p.gen('($no_star*)memdup(&($no_star) {') //sizeof(Node)); + p.gen('($no_star*)memdup(&($no_star) {') + p.is_alloc = true + //println('setting is_alloc=true (ret $typ)') } mut did_gen_something := false // Loop thru all struct init keys and assign values @@ -3150,7 +3171,7 @@ fn (p mut Parser) for_st() { p.for_expr_cnt++ next_tok := p.peek() //debug := p.scanner.file_path.contains('r_draw') - p.cur_fn.open_scope() + p.open_scope() if p.tok == .lcbr { // Infinite loop p.gen('while (1) {') @@ -3703,7 +3724,7 @@ fn (p mut Parser) go_statement() { if p.peek() == .dot { var_name := p.lit v := p.cur_fn.find_var(var_name) - p.cur_fn.mark_var_used(v) + p.mark_var_used(v) p.next() p.check(.dot) typ := p.table.find_type(v.typ) diff --git a/compiler/table.v b/compiler/table.v index 4366136c44..7cba06a51c 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -77,6 +77,7 @@ mut: is_used bool is_changed bool scope_level int + is_c bool // todo remove once `typ` is `Type`, not string } struct Type { @@ -442,6 +443,7 @@ fn (table mut Table) add_method(type_name string, f Fn) { print_backtrace() cerror('add_method: empty type') } + // TODO table.typesmap[type_name].methods << f mut t := table.typesmap[type_name] t.methods << f table.typesmap[type_name] = t diff --git a/vlib/builtin/builtin.v b/vlib/builtin/builtin.v index 39061bd8b1..b16c8e57c9 100644 --- a/vlib/builtin/builtin.v +++ b/vlib/builtin/builtin.v @@ -99,14 +99,14 @@ pub fn print(s string) { } __global total_m i64 = 0 -//__global nr_mallocs int = 0 +//__global nr_mallocs int = 0 pub fn malloc(n int) byteptr { if n < 0 { panic('malloc(<0)') } - //nr_mallocs++ -/* -TODO + //nr_mallocs++ +/* +TODO #ifdef VPLAY if n > 10000 { panic('allocating more than 10 KB is not allowed in the playground') @@ -117,7 +117,7 @@ TODO println('\n\n\nmalloc($n) total=$total_m') print_backtrace() #endif -*/ +*/ ptr := C.malloc(n) if isnil(ptr) { panic('malloc($n) failed') @@ -141,5 +141,9 @@ fn memdup(src voidptr, sz int) voidptr { return C.memcpy(mem, src, sz) } +fn v_ptr_free(ptr voidptr) { + C.free(ptr) +} + diff --git a/vlib/builtin/map.v b/vlib/builtin/map.v index 7f5f0c00c8..dfc8b1edcc 100644 --- a/vlib/builtin/map.v +++ b/vlib/builtin/map.v @@ -236,7 +236,24 @@ pub fn (m map) print() { println('>>>>>>>>>>') } -pub fn (m map) free() { +fn (n mut mapnode) free() { + if n.val != 0 { + free(n.val) + } + if n.left != 0 { + n.left.free() + } + if n.right != 0 { + n.right.free() + } + free(n) +} + +pub fn (m mut map) free() { + if m.root == 0 { + return + } + m.root.free() // C.free(m.table) // C.free(m.keys_table) } diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 21feeb3aaf..116cbc6393 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -156,7 +156,7 @@ pub fn (s string) u64() u64 { // == fn (s string) eq(a string) bool { - if isnil(s.str) { + if isnil(s.str) { // should never happen panic('string.eq(): nil string') } if s.len != a.len { diff --git a/vlib/os/os.v b/vlib/os/os.v index 05795bca3b..e229cfe58c 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -289,8 +289,7 @@ pub fn (f File) close() { } // system starts the specified command, waits for it to complete, and returns its code. - -fn popen(path string) &FILE { +fn popen(path string) *C.FILE { $if windows { mode := 'rb' wpath := path.to_wide() @@ -302,7 +301,7 @@ fn popen(path string) &FILE { } } -fn pclose(f &FILE) int { +fn pclose(f *C.FILE) int { $if windows { return C._pclose(f) }