From 53c2e262f1cf3a01b13314f721f62a35f76df750 Mon Sep 17 00:00:00 2001 From: ChAoS_UnItY <43753315+ChAoSUnItY@users.noreply.github.com> Date: Mon, 18 Oct 2021 15:59:52 +0800 Subject: [PATCH] cgen & jsgen: generate when possible a `switch` from `match x {` (#12216) --- vlib/v/gen/c/cgen.v | 77 +++++++++++++++++++++++++++++++++++++++++++++ vlib/v/gen/js/js.v | 37 ++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 16a34fd83e..30ca101a61 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -104,6 +104,7 @@ mut: inside_map_index bool inside_opt_data bool inside_if_optional bool + loop_depth int ternary_names map[string]string ternary_level_names map[string][]string stmt_path_pos []int // positions of each statement start, for inserting C statements before the current statement @@ -1780,6 +1781,7 @@ fn (mut g Gen) write_defer_stmts() { } fn (mut g Gen) for_c_stmt(node ast.ForCStmt) { + g.loop_depth++ if node.is_multi { g.is_vlines_enabled = false if node.label.len > 0 { @@ -1853,9 +1855,11 @@ fn (mut g Gen) for_c_stmt(node ast.ForCStmt) { g.writeln('${node.label}__break: {}') } } + g.loop_depth-- } fn (mut g Gen) for_stmt(node ast.ForStmt) { + g.loop_depth++ g.is_vlines_enabled = false if node.label.len > 0 { g.writeln('$node.label:') @@ -1878,9 +1882,11 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) { if node.label.len > 0 { g.writeln('${node.label}__break: {}') } + g.loop_depth-- } fn (mut g Gen) for_in_stmt(node ast.ForInStmt) { + g.loop_depth++ if node.label.len > 0 { g.writeln('\t$node.label: {}') } @@ -2120,6 +2126,7 @@ fn (mut g Gen) for_in_stmt(node ast.ForInStmt) { if node.label.len > 0 { g.writeln('\t${node.label}__break: {}') } + g.loop_depth-- } struct SumtypeCastingFn { @@ -4255,10 +4262,14 @@ fn (mut g Gen) unlock_locks() { fn (mut g Gen) need_tmp_var_in_match(node ast.MatchExpr) bool { if node.is_expr && node.return_type != ast.void_type && node.return_type != 0 { + cond_sym := g.table.get_final_type_symbol(node.cond_type) sym := g.table.get_type_symbol(node.return_type) if sym.kind == .multi_return { return false } + if cond_sym.kind == .enum_ && node.branches.len > 5 { + return true + } for branch in node.branches { if branch.stmts.len > 1 { return true @@ -4278,6 +4289,27 @@ fn (mut g Gen) need_tmp_var_in_match(node ast.MatchExpr) bool { return false } +fn (mut g Gen) branches_all_resolvable_in_runtime(node ast.MatchExpr, typ ast.TypeSymbol) bool { + for branch in node.branches { + for expr in branch.exprs { + if expr is ast.EnumVal { + continue + } else if expr is ast.RangeExpr { + return false + // we must implement constant folding on enum fields and make it accessible + // anywhere to prove that range expr's actual branches are resolvale + + // if expr.high !is ast.IntegerLiteral || expr.low !is ast.IntegerLiteral { + // return false + // } + // continue + } + return true + } + } + return true +} + fn (mut g Gen) match_expr(node ast.MatchExpr) { // println('match expr typ=$it.expr_type') // TODO @@ -4321,8 +4353,13 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) { // brackets needed otherwise '?' will apply to everything on the left g.write('(') } + typ := g.table.get_final_type_symbol(node.cond_type) + all_resolvable := g.branches_all_resolvable_in_runtime(node, typ) if node.is_sum_type { g.match_expr_sumtype(node, is_expr, cond_var, tmp_var) + } else if typ.kind == .enum_ && g.loop_depth == 0 && node.branches.len > 5 && g.fn_decl != 0 + && all_resolvable { // do not optimize while in top-level + g.match_expr_switch(node, is_expr, cond_var, tmp_var, typ) } else { g.match_expr_classic(node, is_expr, cond_var, tmp_var) } @@ -4411,6 +4448,46 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str } } +fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string, enum_typ ast.TypeSymbol) { + cname := '${enum_typ.cname}__' + mut covered_enum := []string{cap: (enum_typ.info as ast.Enum).vals.len} + g.empty_line = true + g.writeln('switch ($cond_var) {') + g.indent++ + for branch in node.branches { + if branch.is_else { + for val in (enum_typ.info as ast.Enum).vals { + if val !in covered_enum { + g.writeln('case $cname$val:') + } + } + g.writeln('default:') + } else { + for expr in branch.exprs { + if expr is ast.EnumVal { + covered_enum << (expr as ast.EnumVal).val + g.write('case ') + g.expr(expr) + g.writeln(': ') + } else if expr is ast.RangeExpr { + // low, high := (expr.low as ast.IntegerLiteral).val.int(), (expr.high as ast.IntegerLiteral).val.int() + // for val in (enum_typ.info as ast.Enum).vals[low..high + 1] { + // covered_enum << val + // g.writeln('case $cname$val:') + // } + } + } + } + g.indent++ + g.writeln('{') + g.stmts_with_tmp_var(branch.stmts, tmp_var) + g.writeln('} break;') + g.indent-- + } + g.indent-- + g.writeln('}') +} + fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string) { type_sym := g.table.get_type_symbol(node.cond_type) for j, branch in node.branches { diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 3c2a757f85..ccae5434ba 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -1956,10 +1956,14 @@ fn (mut g JsGen) gen_lock_expr(node ast.LockExpr) { fn (mut g JsGen) need_tmp_var_in_match(node ast.MatchExpr) bool { if node.is_expr && node.return_type != ast.void_type && node.return_type != 0 { + cond_sym := g.table.get_final_type_symbol(node.cond_type) sym := g.table.get_type_symbol(node.return_type) if sym.kind == .multi_return { return false } + if cond_sym.kind == .enum_ && node.branches.len > 5 { + return true + } for branch in node.branches { if branch.stmts.len > 1 { return true @@ -2165,6 +2169,7 @@ fn (mut g JsGen) match_expr(node ast.MatchExpr) { is_expr := (node.is_expr && node.return_type != ast.void_type) || g.inside_ternary mut cond_var := MatchCond(CondString{''}) mut tmp_var := '' + mut cur_line := '' if is_expr && !need_tmp_var { g.inside_ternary = true } @@ -2180,17 +2185,23 @@ fn (mut g JsGen) match_expr(node ast.MatchExpr) { g.writeln(';') } if need_tmp_var { + g.empty_line = true + cur_line = g.out.cut_to(g.stmt_start_pos).trim_left(' \t') tmp_var = g.new_tmp_var() g.writeln('let $tmp_var = undefined;') } if is_expr && !need_tmp_var { g.write('(') } + typ := g.table.get_final_type_symbol(node.cond_type) if node.is_sum_type { g.match_expr_sumtype(node, is_expr, cond_var, tmp_var) + } else if typ.kind == .enum_ && !g.inside_loop && node.branches.len > 5 && g.fn_decl != 0 { // do not optimize while in top-level + g.match_expr_switch(node, is_expr, cond_var, tmp_var) } else { g.match_expr_classic(node, is_expr, cond_var, tmp_var) } + g.write(cur_line) if need_tmp_var { g.write('$tmp_var') } @@ -2302,6 +2313,32 @@ fn (mut g JsGen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var M } } +fn (mut g JsGen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var MatchCond, tmp_var string) { + g.empty_line = true + g.write('switch (') + g.match_cond(cond_var) + g.writeln(') {') + g.inc_indent() + for branch in node.branches { + if branch.is_else { + g.writeln('default:') + } else { + for expr in branch.exprs { + g.write('case ') + g.expr(expr) + g.writeln(': ') + } + } + g.inc_indent() + g.writeln('{') + g.stmts_with_tmp_var(branch.stmts, tmp_var) + g.writeln('} break;') + g.dec_indent() + } + g.dec_indent() + g.writeln('}') +} + fn (mut g JsGen) need_tmp_var_in_if(node ast.IfExpr) bool { if node.is_expr && g.inside_ternary { if node.typ.has_flag(.optional) {