mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
cgen: use switch for matches with integer literals, instead of an if else if
ladder (#15254)
This commit is contained in:
parent
17ce1a0e8d
commit
04b28d11be
@ -589,6 +589,12 @@ pub fn (node Stmt) str() string {
|
||||
EnumDecl {
|
||||
return 'enum $node.name { $node.fields.len fields }'
|
||||
}
|
||||
ForStmt {
|
||||
if node.is_inf {
|
||||
return 'for {'
|
||||
}
|
||||
return 'for $node.cond {'
|
||||
}
|
||||
Module {
|
||||
return 'module $node.name'
|
||||
}
|
||||
@ -599,6 +605,16 @@ pub fn (node Stmt) str() string {
|
||||
}
|
||||
return out
|
||||
}
|
||||
Return {
|
||||
mut out := 'return'
|
||||
for i, val in node.exprs {
|
||||
out += ' $val'
|
||||
if i < node.exprs.len - 1 {
|
||||
out += ','
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
StructDecl {
|
||||
return 'struct $node.name { $node.fields.len fields }'
|
||||
}
|
||||
|
@ -1527,13 +1527,20 @@ fn is_noreturn_callexpr(expr ast.Expr) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// tmp_var is used in `if` expressions only
|
||||
fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) {
|
||||
// stmts_with_tmp_var is used in `if` or `match` branches.
|
||||
// It returns true, if the last statement was a `return`
|
||||
fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) bool {
|
||||
g.indent++
|
||||
if g.inside_ternary > 0 {
|
||||
g.write('(')
|
||||
}
|
||||
mut last_stmt_was_return := false
|
||||
for i, stmt in stmts {
|
||||
if i == stmts.len - 1 {
|
||||
if stmt is ast.Return {
|
||||
last_stmt_was_return = true
|
||||
}
|
||||
}
|
||||
if i == stmts.len - 1 && tmp_var != '' {
|
||||
// Handle if expressions, set the value of the last expression to the temp var.
|
||||
if g.inside_if_optional || g.inside_match_optional {
|
||||
@ -1605,7 +1612,7 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) {
|
||||
// Do not autofree if the position is 0, since the correct scope won't be found.
|
||||
// Report a bug, since position shouldn't be 0 for most nodes.
|
||||
if stmt is ast.Module {
|
||||
return
|
||||
return last_stmt_was_return
|
||||
}
|
||||
if stmt is ast.ExprStmt {
|
||||
// For some reason ExprStmt.pos is 0 when ExprStmt.expr is comp if expr
|
||||
@ -1616,12 +1623,13 @@ fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) {
|
||||
$if trace_autofree ? {
|
||||
println('autofree: first stmt pos = 0. $stmt.type_name()')
|
||||
}
|
||||
return
|
||||
return last_stmt_was_return
|
||||
}
|
||||
}
|
||||
g.autofree_scope_vars(stmt_pos.pos - 1, stmt_pos.line_nr, false)
|
||||
}
|
||||
}
|
||||
return last_stmt_was_return
|
||||
}
|
||||
|
||||
[inline]
|
||||
|
@ -88,15 +88,38 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) {
|
||||
// brackets needed otherwise '?' will apply to everything on the left
|
||||
g.write('(')
|
||||
}
|
||||
typ := g.table.final_sym(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.loop_depth == 0 && node.branches.len > 5
|
||||
&& unsafe { g.fn_decl != 0 } { // do not optimize while in top-level
|
||||
g.match_expr_switch(node, is_expr, cond_var, tmp_var, typ)
|
||||
} else {
|
||||
cond_fsym := g.table.final_sym(node.cond_type)
|
||||
mut can_be_a_switch := true
|
||||
all_branches: for branch in node.branches {
|
||||
for expr in branch.exprs {
|
||||
match expr {
|
||||
ast.BoolLiteral, ast.IntegerLiteral, ast.CharLiteral, ast.EnumVal {
|
||||
continue
|
||||
}
|
||||
else {
|
||||
// ast.StringLiteral, ast.Ident, ast.RangeExpr can not used in switch cases in C
|
||||
// eprintln('>>>> node.cond: $node.cond | branch expr: ${typeof(expr)} | expr: $expr')
|
||||
can_be_a_switch = false
|
||||
break all_branches
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// eprintln('> can_be_a_switch: $can_be_a_switch')
|
||||
if can_be_a_switch && !is_expr && g.loop_depth == 0 && g.fn_decl != unsafe { nil }
|
||||
&& cond_fsym.is_int() {
|
||||
g.match_expr_switch(node, is_expr, cond_var, tmp_var, cond_fsym)
|
||||
} else if cond_fsym.kind == .enum_ && g.loop_depth == 0 && node.branches.len > 5
|
||||
&& g.fn_decl != unsafe { nil } {
|
||||
// do not optimize while in top-level
|
||||
g.match_expr_switch(node, is_expr, cond_var, tmp_var, cond_fsym)
|
||||
} else {
|
||||
g.match_expr_classic(node, is_expr, cond_var, tmp_var)
|
||||
}
|
||||
}
|
||||
g.set_current_pos_as_last_stmt_pos()
|
||||
g.write(cur_line)
|
||||
if need_tmp_var {
|
||||
@ -109,6 +132,8 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) {
|
||||
}
|
||||
|
||||
fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string) {
|
||||
dot_or_ptr := if node.cond_type.is_ptr() { '->' } else { '.' }
|
||||
use_ternary := is_expr && tmp_var.len == 0
|
||||
for j, branch in node.branches {
|
||||
mut sumtype_index := 0
|
||||
// iterates through all types in sumtype branches
|
||||
@ -116,8 +141,8 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str
|
||||
g.aggregate_type_idx = sumtype_index
|
||||
is_last := j == node.branches.len - 1
|
||||
sym := g.table.sym(node.cond_type)
|
||||
if branch.is_else || (node.is_expr && is_last && tmp_var.len == 0) {
|
||||
if is_expr && tmp_var.len == 0 {
|
||||
if branch.is_else || (use_ternary && is_last) {
|
||||
if use_ternary {
|
||||
// TODO too many branches. maybe separate ?: matches
|
||||
g.write(' : ')
|
||||
} else {
|
||||
@ -127,14 +152,14 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str
|
||||
}
|
||||
} else {
|
||||
if j > 0 || sumtype_index > 0 {
|
||||
if is_expr && tmp_var.len == 0 {
|
||||
if use_ternary {
|
||||
g.write(' : ')
|
||||
} else {
|
||||
g.write_v_source_line_info(branch.pos)
|
||||
g.write('else ')
|
||||
}
|
||||
}
|
||||
if is_expr && tmp_var.len == 0 {
|
||||
if use_ternary {
|
||||
g.write('(')
|
||||
} else {
|
||||
if j == 0 && sumtype_index == 0 {
|
||||
@ -144,21 +169,20 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str
|
||||
g.write('if (')
|
||||
}
|
||||
g.write(cond_var)
|
||||
dot_or_ptr := if node.cond_type.is_ptr() { '->' } else { '.' }
|
||||
be := unsafe { &branch.exprs[sumtype_index] }
|
||||
if sym.kind == .sum_type {
|
||||
g.write('${dot_or_ptr}_typ == ')
|
||||
g.expr(branch.exprs[sumtype_index])
|
||||
g.expr(be)
|
||||
} else if sym.kind == .interface_ {
|
||||
if branch.exprs[sumtype_index] is ast.TypeNode {
|
||||
typ := branch.exprs[sumtype_index] as ast.TypeNode
|
||||
if be is ast.TypeNode {
|
||||
typ := be as ast.TypeNode
|
||||
branch_sym := g.table.sym(g.unwrap_generic(typ.typ))
|
||||
g.write('${dot_or_ptr}_typ == _${sym.cname}_${branch_sym.cname}_index')
|
||||
} else if branch.exprs[sumtype_index] is ast.None
|
||||
&& sym.idx == ast.error_type_idx {
|
||||
} else if be is ast.None && sym.idx == ast.error_type_idx {
|
||||
g.write('${dot_or_ptr}_typ == _IError_None___index')
|
||||
}
|
||||
}
|
||||
if is_expr && tmp_var.len == 0 {
|
||||
if use_ternary {
|
||||
g.write(')? ')
|
||||
} else {
|
||||
g.writeln(') {')
|
||||
@ -183,22 +207,34 @@ 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} // collects missing enum variant branches to avoid cstrict errors
|
||||
mut range_branches := []ast.MatchBranch{cap: node.branches.len} // branches have RangeExpr cannot emit as switch case branch, we handle it in default branch
|
||||
fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string, cond_fsym ast.TypeSymbol) {
|
||||
node_cond_type_unsigned := node.cond_type in [ast.u16_type, ast.u32_type, ast.u64_type]
|
||||
cname := '${cond_fsym.cname}__'
|
||||
|
||||
mut covered_enum_cap := 0
|
||||
if cond_fsym.info is ast.Enum {
|
||||
covered_enum_cap = (cond_fsym.info as ast.Enum).vals.len
|
||||
}
|
||||
mut covered_enum := []string{cap: covered_enum_cap} // collects missing enum variant branches to avoid cstrict errors
|
||||
|
||||
// A branch that has a RangeExpr condition, cannot be emitted as a switch case branch;
|
||||
// we will store each of them in range_branches, and then will handle them all in the default branch with if conditions:
|
||||
mut range_branches := []ast.MatchBranch{cap: node.branches.len}
|
||||
mut default_generated := false
|
||||
|
||||
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 cond_fsym.info is ast.Enum {
|
||||
for val in (cond_fsym.info as ast.Enum).vals {
|
||||
if val !in covered_enum {
|
||||
g.writeln('case $cname$val:')
|
||||
}
|
||||
}
|
||||
g.writeln('default:')
|
||||
}
|
||||
g.write('default: ')
|
||||
default_generated = true
|
||||
if range_branches.len > 0 {
|
||||
g.indent++
|
||||
@ -212,8 +248,7 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
||||
// if type is unsigned and low is 0, check is unneeded
|
||||
mut skip_low := false
|
||||
if expr.low is ast.IntegerLiteral {
|
||||
if node.cond_type in [ast.u16_type, ast.u32_type, ast.u64_type]
|
||||
&& expr.low.val == '0' {
|
||||
if node_cond_type_unsigned && expr.low.val == '0' {
|
||||
skip_low = true
|
||||
}
|
||||
}
|
||||
@ -233,8 +268,10 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
||||
}
|
||||
}
|
||||
g.writeln(') {')
|
||||
g.stmts_with_tmp_var(range_branch.stmts, tmp_var)
|
||||
ends_with_return := g.stmts_with_tmp_var(range_branch.stmts, tmp_var)
|
||||
if !ends_with_return {
|
||||
g.writeln('\tbreak;')
|
||||
}
|
||||
g.writeln('}')
|
||||
}
|
||||
g.indent--
|
||||
@ -247,8 +284,12 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
||||
for expr in branch.exprs {
|
||||
if expr is ast.EnumVal {
|
||||
covered_enum << expr.val
|
||||
}
|
||||
g.write('case ')
|
||||
g.expr(expr)
|
||||
if branch.stmts.len > 0 {
|
||||
g.write(': ')
|
||||
} else {
|
||||
g.writeln(': ')
|
||||
}
|
||||
}
|
||||
@ -258,14 +299,16 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
||||
if is_expr && tmp_var.len > 0 && g.table.sym(node.return_type).kind == .sum_type {
|
||||
g.expected_cast_type = node.return_type
|
||||
}
|
||||
g.stmts_with_tmp_var(branch.stmts, tmp_var)
|
||||
ends_with_return := g.stmts_with_tmp_var(branch.stmts, tmp_var)
|
||||
g.expected_cast_type = 0
|
||||
if !ends_with_return {
|
||||
g.writeln('\tbreak;')
|
||||
g.writeln('}')
|
||||
}
|
||||
g.indent--
|
||||
g.writeln('}')
|
||||
}
|
||||
if range_branches.len > 0 && !default_generated {
|
||||
g.writeln('default:')
|
||||
g.write('default: ')
|
||||
g.indent++
|
||||
for range_branch in range_branches {
|
||||
g.write('if (')
|
||||
@ -277,8 +320,7 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
||||
// if type is unsigned and low is 0, check is unneeded
|
||||
mut skip_low := false
|
||||
if expr.low is ast.IntegerLiteral {
|
||||
if node.cond_type in [ast.u16_type, ast.u32_type, ast.u64_type]
|
||||
&& expr.low.val == '0' {
|
||||
if node_cond_type_unsigned && expr.low.val == '0' {
|
||||
skip_low = true
|
||||
}
|
||||
}
|
||||
@ -298,8 +340,10 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
||||
}
|
||||
}
|
||||
g.writeln(') {')
|
||||
g.stmts_with_tmp_var(range_branch.stmts, tmp_var)
|
||||
ends_with_return := g.stmts_with_tmp_var(range_branch.stmts, tmp_var)
|
||||
if !ends_with_return {
|
||||
g.writeln('\tbreak;')
|
||||
}
|
||||
g.writeln('}')
|
||||
}
|
||||
g.indent--
|
||||
@ -309,12 +353,14 @@ fn (mut g Gen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var stri
|
||||
}
|
||||
|
||||
fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string) {
|
||||
node_cond_type_unsigned := node.cond_type in [ast.u16_type, ast.u32_type, ast.u64_type]
|
||||
type_sym := g.table.sym(node.cond_type)
|
||||
use_ternary := is_expr && tmp_var.len == 0
|
||||
for j, branch in node.branches {
|
||||
is_last := j == node.branches.len - 1
|
||||
if branch.is_else || (node.is_expr && is_last && tmp_var.len == 0) {
|
||||
if branch.is_else || (use_ternary && is_last) {
|
||||
if node.branches.len > 1 {
|
||||
if is_expr && tmp_var.len == 0 {
|
||||
if use_ternary {
|
||||
// TODO too many branches. maybe separate ?: matches
|
||||
g.write(' : ')
|
||||
} else {
|
||||
@ -325,7 +371,7 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str
|
||||
}
|
||||
} else {
|
||||
if j > 0 {
|
||||
if is_expr && tmp_var.len == 0 {
|
||||
if use_ternary {
|
||||
g.write(' : ')
|
||||
} else {
|
||||
g.writeln('')
|
||||
@ -333,7 +379,7 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str
|
||||
g.write('else ')
|
||||
}
|
||||
}
|
||||
if is_expr && tmp_var.len == 0 {
|
||||
if use_ternary {
|
||||
g.write('(')
|
||||
} else {
|
||||
if j == 0 {
|
||||
@ -381,8 +427,7 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str
|
||||
// if type is unsigned and low is 0, check is unneeded
|
||||
mut skip_low := false
|
||||
if expr.low is ast.IntegerLiteral {
|
||||
if node.cond_type in [ast.u16_type, ast.u32_type, ast.u64_type]
|
||||
&& expr.low.val == '0' {
|
||||
if node_cond_type_unsigned && expr.low.val == '0' {
|
||||
skip_low = true
|
||||
}
|
||||
}
|
||||
@ -403,7 +448,7 @@ fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var str
|
||||
}
|
||||
}
|
||||
}
|
||||
if is_expr && tmp_var.len == 0 {
|
||||
if use_ternary {
|
||||
g.write(')? ')
|
||||
} else {
|
||||
g.writeln(') {')
|
||||
|
@ -2449,12 +2449,12 @@ fn (mut g JsGen) match_expr(node ast.MatchExpr) {
|
||||
if is_expr && !need_tmp_var {
|
||||
g.write('(')
|
||||
}
|
||||
typ := g.table.final_sym(node.cond_type)
|
||||
cond_fsym := g.table.final_sym(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
|
||||
} else if cond_fsym.kind == .enum_ && !g.inside_loop && node.branches.len > 5
|
||||
&& unsafe { g.fn_decl != 0 } { // do not optimize while in top-level
|
||||
g.match_expr_switch(node, is_expr, cond_var, tmp_var, typ)
|
||||
g.match_expr_switch(node, is_expr, cond_var, tmp_var, cond_fsym)
|
||||
} else {
|
||||
g.match_expr_classic(node, is_expr, cond_var, tmp_var)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user