From 945769a4f68fb1be236641fe921633e895498e82 Mon Sep 17 00:00:00 2001 From: yuyi Date: Mon, 15 Mar 2021 01:11:21 +0800 Subject: [PATCH] all: implement reference receiver optimization for big structs (#9268) --- vlib/os/process.v | 2 +- vlib/v/ast/str.v | 3 +++ vlib/v/checker/checker.v | 13 +++++++++++-- vlib/v/gen/c/cgen.v | 13 ++++++++++++- vlib/v/parser/fn.v | 14 +++++++++++++- vlib/v/table/table.v | 13 +++++++------ 6 files changed, 47 insertions(+), 11 deletions(-) diff --git a/vlib/os/process.v b/vlib/os/process.v index 2284656ea4..3521f1141b 100644 --- a/vlib/os/process.v +++ b/vlib/os/process.v @@ -28,7 +28,7 @@ pub mut: env_is_custom bool // true, when the environment was customized with .set_environment env []string // the environment with which the process was started (list of 'var=val') use_stdio_ctl bool // when true, then you can use p.stdin_write(), p.stdout_slurp() and p.stderr_slurp() - stdio_fd [3]int // the file descriptors + stdio_fd [3]int // the file descriptors } // new_process - create a new process descriptor diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index ea2ce114f0..116112b42f 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -33,6 +33,9 @@ pub fn (node &FnDecl) stringify(t &table.Table, cur_mod string, m2a map[string]s styp = styp[1..] // remove & } styp = util.no_cur_mod(styp, cur_mod) + if node.params[0].is_auto_rec { + styp = styp.trim('&') + } receiver = '($m$node.receiver.name $styp) ' /* sym := t.get_type_symbol(node.receiver.typ) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 43084a438f..0b6de79c92 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2444,6 +2444,9 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) { got_typ_sym := c.table.get_type_symbol(got_typ) mut exp_typ_sym := c.table.get_type_symbol(exp_type) pos := return_stmt.exprs[i].position() + if return_stmt.exprs[i].is_auto_deref_var() { + continue + } if exp_typ_sym.kind == .interface_ { c.type_implements(got_typ, exp_type, return_stmt.pos) continue @@ -2454,12 +2457,18 @@ pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) { if (got_typ.is_ptr() || got_typ.is_pointer()) && (!exp_type.is_ptr() && !exp_type.is_pointer()) { pos := return_stmt.exprs[i].position() + if return_stmt.exprs[i].is_auto_deref_var() { + continue + } c.error('fn `$c.cur_fn.name` expects you to return a non reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead', pos) } if (exp_type.is_ptr() || exp_type.is_pointer()) && (!got_typ.is_ptr() && !got_typ.is_pointer()) && got_typ != table.int_literal_type { pos := return_stmt.exprs[i].position() + if return_stmt.exprs[i].is_auto_deref_var() { + continue + } c.error('fn `$c.cur_fn.name` expects you to return a reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead', pos) } @@ -4713,7 +4722,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym table.TypeS } // smartcast takes the expression with the current type which should be smartcasted to the target type in the given scope -fn (c Checker) smartcast_sumtype(expr ast.Expr, cur_type table.Type, to_type table.Type, mut scope ast.Scope) { +fn (c &Checker) smartcast_sumtype(expr ast.Expr, cur_type table.Type, to_type table.Type, mut scope ast.Scope) { match mut expr { ast.SelectorExpr { mut is_mut := false @@ -5684,7 +5693,7 @@ pub fn (mut c Checker) error(message string, pos token.Position) { } // check `to` has all fields of `from` -fn (c Checker) check_struct_signature(from table.Struct, to table.Struct) bool { +fn (c &Checker) check_struct_signature(from table.Struct, to table.Struct) bool { // Note: `to` can have extra fields if from.fields.len == 0 { return false diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 2d88582605..ac33e1beb2 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4496,7 +4496,18 @@ fn (mut g Gen) return_statement(node ast.Return) { } else { g.write('return ') } - g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) + if expr0.is_auto_deref_var() { + if g.fn_decl.return_type.is_ptr() { + g.write('&(*') + g.expr(expr0) + g.write(')') + } else { + g.write('*') + g.expr(expr0) + } + } else { + g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) + } if free { expr := node.exprs[0] if expr is ast.Ident { diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index e8dcfea538..f430a7edbd 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -279,7 +279,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { name: param.name typ: param.typ is_mut: param.is_mut - is_auto_deref: param.is_mut + is_auto_deref: param.is_mut || param.is_auto_rec pos: param.pos is_used: true is_arg: true @@ -478,10 +478,22 @@ fn (mut p Parser) fn_receiver(mut params []table.Param, mut rec ReceiverParsingI if is_atomic { rec.typ = rec.typ.set_flag(.atomic_f) } + // optimize method `automatic use fn (a &big_foo) instead of fn (a big_foo)` + type_sym := p.table.get_type_symbol(rec.typ) + mut is_auto_rec := false + if type_sym.kind == .struct_ { + info := type_sym.info as table.Struct + if !rec.is_mut && !rec.typ.is_ptr() && info.fields.len > 8 { + rec.typ = rec.typ.to_ptr() + is_auto_rec = true + } + } + params << table.Param{ pos: rec_start_pos name: rec.name is_mut: rec.is_mut + is_auto_rec: is_auto_rec typ: rec.typ } p.check(.rpar) diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index 83b38cedc1..29359646d6 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -57,12 +57,13 @@ fn (f &Fn) method_equals(o &Fn) bool { pub struct Param { pub: - pos token.Position - name string - is_mut bool - typ Type - type_pos token.Position - is_hidden bool // interface first arg + pos token.Position + name string + is_mut bool + is_auto_rec bool + typ Type + type_pos token.Position + is_hidden bool // interface first arg } fn (p &Param) equals(o &Param) bool {