transformer: eliminate unreachable branches in IfExpr and fold more expressions and statements (#12135)

* v: fix using constant as length in fixed array

* format test file

* v/trasnformer: discard unreachable if branches

* transform more expressions and statements

* replace IfExpr with EmptyExpr when all branches were eliminated

* fix typo

* fix gens

* only allows branch elimination on if expression statement

* fix native gen

* fix handling of multi branch ifs in the native backend, also allow for `if true {}`

Co-authored-by: KyleLin921021 <43753315+KyleLin921021@users.noreply.github.com>
Co-authored-by: Delyan Angelov <delian66@gmail.com>
pull/12153/head
ChAoS_UnItY 2021-10-11 19:29:17 +08:00 committed by GitHub
parent 35b301f73c
commit d0c961ebc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 178 additions and 20 deletions

View File

@ -5066,7 +5066,9 @@ fn (mut g Gen) if_expr(node ast.IfExpr) {
g.stmt_path_pos << stmt_pos g.stmt_path_pos << stmt_pos
} }
} }
g.writeln('}') if node.branches.len > 0 {
g.writeln('}')
}
g.set_current_pos_as_last_stmt_pos() g.set_current_pos_as_last_stmt_pos()
if needs_tmp_var { if needs_tmp_var {
if g.infix_left_var_name.len > 0 { if g.infix_left_var_name.len > 0 {

View File

@ -2387,7 +2387,9 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) {
g.stmts(branch.stmts) g.stmts(branch.stmts)
} }
} }
g.writeln('}') if node.branches.len > 0 {
g.writeln('}')
}
if needs_tmp_var { if needs_tmp_var {
g.write('$tmp') g.write('$tmp')
} }

View File

@ -1268,22 +1268,32 @@ fn (mut g Gen) condition(infix_expr ast.InfixExpr, neg bool) int {
} }
fn (mut g Gen) if_expr(node ast.IfExpr) { fn (mut g Gen) if_expr(node ast.IfExpr) {
branch := node.branches[0]
infix_expr := branch.cond as ast.InfixExpr
cjmp_addr := g.condition(infix_expr, false)
if node.is_comptime { if node.is_comptime {
g.n_error('ignored comptime') g.n_error('ignored comptime')
} }
g.stmts(branch.stmts)
// Now that we know where we need to jump if the condition is false, update the `jne` call.
// The value is the relative address, difference between current position and the location
// after `jne 00 00 00 00`
// println('after if g.pos=$g.pos() jneaddr=$cjmp_addr')
g.write32_at(cjmp_addr, int(g.pos() - cjmp_addr - 4)) // 4 is for "00 00 00 00"
if node.has_else { if node.has_else {
g.n_error('else statements not yet supported') g.n_error('else statements not yet supported')
} }
if node.branches.len == 0 {
return
}
for idx in 0 .. node.branches.len {
branch := node.branches[idx]
if branch.cond is ast.BoolLiteral {
if branch.cond.val {
g.stmts(branch.stmts)
}
continue
}
infix_expr := branch.cond as ast.InfixExpr
cjmp_addr := g.condition(infix_expr, false)
g.stmts(branch.stmts)
// Now that we know where we need to jump if the condition is false, update the `jne` call.
// The value is the relative address, difference between current position and the location
// after `jne 00 00 00 00`
// println('after if g.pos=$g.pos() jneaddr=$cjmp_addr')
g.write32_at(cjmp_addr, int(g.pos() - cjmp_addr - 4)) // 4 is for "00 00 00 00"
}
} }
fn (mut g Gen) infloop() { fn (mut g Gen) infloop() {

View File

@ -56,10 +56,19 @@ pub fn (t Transformer) stmt(mut node ast.Stmt) {
ast.DeferStmt {} ast.DeferStmt {}
ast.EnumDecl {} ast.EnumDecl {}
ast.ExprStmt { ast.ExprStmt {
expr := t.expr(node.expr) if node.expr is ast.IfExpr {
node = &ast.ExprStmt{ mut untrans_expr := node.expr as ast.IfExpr
...node expr := t.if_expr(mut untrans_expr)
expr: expr node = &ast.ExprStmt{
...node
expr: expr
}
} else {
expr := t.expr(node.expr)
node = &ast.ExprStmt{
...node
expr: expr
}
} }
} }
ast.FnDecl { ast.FnDecl {
@ -69,7 +78,15 @@ pub fn (t Transformer) stmt(mut node ast.Stmt) {
} }
ast.ForCStmt {} ast.ForCStmt {}
ast.ForInStmt {} ast.ForInStmt {}
ast.ForStmt {} ast.ForStmt {
node = &ast.ForStmt{
...node
cond: t.expr(node.cond)
}
if node.cond is ast.BoolLiteral && !(node.cond as ast.BoolLiteral).val { // for false { ... } should be eleminated
node = &ast.EmptyStmt{}
}
}
ast.GlobalDecl {} ast.GlobalDecl {}
ast.GotoLabel {} ast.GotoLabel {}
ast.GotoStmt {} ast.GotoStmt {}
@ -89,12 +106,71 @@ pub fn (t Transformer) stmt(mut node ast.Stmt) {
} }
pub fn (t Transformer) expr(node ast.Expr) ast.Expr { pub fn (t Transformer) expr(node ast.Expr) ast.Expr {
match node { match mut node {
ast.InfixExpr { return t.infix_expr(node) } ast.InfixExpr {
else { return node } return t.infix_expr(node)
}
ast.IndexExpr {
return ast.IndexExpr{
...node
index: t.expr(node.index)
}
}
ast.MatchExpr {
for mut branch in node.branches {
for mut stmt in branch.stmts {
t.stmt(mut stmt)
}
}
return node
}
else {
return node
}
} }
} }
pub fn (t Transformer) if_expr(mut original ast.IfExpr) ast.Expr {
mut stop_index, mut unreachable_branches := -1, []int{cap: original.branches.len}
for i, mut branch in original.branches {
for mut stmt in branch.stmts {
t.stmt(mut stmt)
}
cond := t.expr(branch.cond)
branch = ast.IfBranch{
...(*branch)
cond: cond
}
if cond is ast.BoolLiteral {
if cond.val { // eliminates remaining branches when reached first bool literal `true`
stop_index = i
break
} else { // discard unreachable branch when reached bool literal `false`
unreachable_branches << i
}
}
}
if stop_index != -1 {
unreachable_branches = unreachable_branches.filter(it < stop_index)
original.branches = original.branches[..stop_index + 1]
}
for unreachable_branches.len != 0 {
original.branches.delete(unreachable_branches.pop())
}
if original.branches.len == 0 { // no remain branches to walk through
return ast.EmptyExpr{}
}
if original.branches.len == 1 && original.branches[0].cond.type_name() == 'unknown v.ast.Expr' {
original.branches[0] = &ast.IfBranch{
...original.branches[0]
cond: ast.BoolLiteral{
val: true
}
}
}
return *original
}
pub fn (t Transformer) infix_expr(original ast.InfixExpr) ast.Expr { pub fn (t Transformer) infix_expr(original ast.InfixExpr) ast.Expr {
mut node := original mut node := original
node.left = t.expr(node.left) node.left = t.expr(node.left)
@ -109,6 +185,16 @@ pub fn (t Transformer) infix_expr(original ast.InfixExpr) ast.Expr {
match right_node { match right_node {
ast.BoolLiteral { ast.BoolLiteral {
match node.op { match node.op {
.eq {
return ast.BoolLiteral{
val: left_node.val == right_node.val
}
}
.ne {
return ast.BoolLiteral{
val: left_node.val != right_node.val
}
}
.and { .and {
return ast.BoolLiteral{ return ast.BoolLiteral{
val: left_node.val && right_node.val val: left_node.val && right_node.val
@ -133,6 +219,16 @@ pub fn (t Transformer) infix_expr(original ast.InfixExpr) ast.Expr {
match right_node { match right_node {
ast.StringLiteral { ast.StringLiteral {
match node.op { match node.op {
.eq {
return ast.BoolLiteral{
val: left_node.val == right_node.val
}
}
.ne {
return ast.BoolLiteral{
val: left_node.val != right_node.val
}
}
.plus { .plus {
return ast.StringLiteral{ return ast.StringLiteral{
val: left_node.val + right_node.val val: left_node.val + right_node.val
@ -155,6 +251,36 @@ pub fn (t Transformer) infix_expr(original ast.InfixExpr) ast.Expr {
left_val := left_node.val.int() left_val := left_node.val.int()
right_val := right_node.val.int() right_val := right_node.val.int()
match node.op { match node.op {
.eq {
return ast.BoolLiteral{
val: left_node.val == right_node.val
}
}
.ne {
return ast.BoolLiteral{
val: left_node.val != right_node.val
}
}
.gt {
return ast.BoolLiteral{
val: left_node.val > right_node.val
}
}
.ge {
return ast.BoolLiteral{
val: left_node.val >= right_node.val
}
}
.lt {
return ast.BoolLiteral{
val: left_node.val < right_node.val
}
}
.le {
return ast.BoolLiteral{
val: left_node.val <= right_node.val
}
}
.plus { .plus {
return ast.IntegerLiteral{ return ast.IntegerLiteral{
val: (left_val + right_val).str() val: (left_val + right_val).str()
@ -203,6 +329,24 @@ pub fn (t Transformer) infix_expr(original ast.InfixExpr) ast.Expr {
pos: pos pos: pos
} }
} }
.left_shift {
return ast.IntegerLiteral{
val: (left_val << right_val).str()
pos: pos
}
}
.right_shift {
return ast.IntegerLiteral{
val: (left_val >> right_val).str()
pos: pos
}
}
.unsigned_right_shift {
return ast.IntegerLiteral{
val: (left_val >>> right_val).str()
pos: pos
}
}
else { else {
return node return node
} }