From 927ec1fadbc42c70f06dd4b43a350df267b739d0 Mon Sep 17 00:00:00 2001 From: lemon Date: Fri, 29 Jul 2022 13:17:01 +0900 Subject: [PATCH] native: support `||`, `&&` and simple boolean expression evaluation (#15256) --- vlib/v/gen/native/amd64.v | 151 ++++++++++++++++++++++++----- vlib/v/gen/native/gen.v | 55 ++++++----- vlib/v/gen/native/tests/ifs.vv | 50 +++++++++- vlib/v/gen/native/tests/ifs.vv.out | 7 ++ 4 files changed, 211 insertions(+), 52 deletions(-) diff --git a/vlib/v/gen/native/amd64.v b/vlib/v/gen/native/amd64.v index 2d850b0fdd..2eeb2145dc 100644 --- a/vlib/v/gen/native/amd64.v +++ b/vlib/v/gen/native/amd64.v @@ -120,6 +120,9 @@ fn (mut g Gen) cmp_reg(reg Register, reg2 Register) { match reg { .rax { match reg2 { + .rdx { + g.write([u8(0x48), 0x39, 0xd0]) + } .rbx { g.write([u8(0x48), 0x39, 0xd8]) } @@ -128,6 +131,16 @@ fn (mut g Gen) cmp_reg(reg Register, reg2 Register) { } } } + .rdx { + match reg2 { + .rax { + g.write([u8(0x48), 0x39, 0xc2]) + } + else { + g.n_error('Cannot compare $reg and $reg2') + } + } + } .rbx { match reg2 { .rax { @@ -309,7 +322,7 @@ enum JumpOp { jne = 0x850f jg = 0x8f0f jge = 0x8d0f - lt = 0x8c0f + jl = 0x8c0f jle = 0x8e0f } @@ -330,6 +343,22 @@ fn (mut g Gen) jmp(addr int) int { return int(pos) } +enum SetOp { + e = 0x940f + ne = 0x950f + g = 0x9f0f + ge = 0x9d0f + l = 0x9c0f + le = 0x9e0f +} + +// SETcc al +fn (mut g Gen) cset(op SetOp) { + g.write16(u16(op)) + g.write8(0xc0) + g.println('set$op al') +} + fn abs(a i64) i64 { return if a < 0 { -a } else { a } } @@ -486,7 +515,7 @@ fn (mut g Gen) mov_reg_to_var(var Var, reg Register, config VarConfig) { g.write16(0x8966) 'WORD' } - ast.i8_type_idx, ast.u8_type_idx, ast.char_type_idx { + ast.i8_type_idx, ast.u8_type_idx, ast.char_type_idx, ast.bool_type_idx { g.write8(0x88) 'BYTE' } @@ -634,7 +663,7 @@ fn (mut g Gen) mov_var_to_reg(reg Register, var Var, config VarConfig) { g.write([u8(0x48), 0x0f, 0xbe]) 'movsx', 'BYTE' } - ast.u8_type_idx, ast.char_type_idx { + ast.u8_type_idx, ast.char_type_idx, ast.bool_type_idx { // movzx rax, BYTE PTR [rbp-0x8] g.write([u8(0x48), 0x0f, 0xb6]) 'movzx', 'BYTE' @@ -1664,32 +1693,96 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) { } else { // dump(node) - g.v_error('unhandled assign_stmt expression: $right.type_name()', right.pos()) + size := g.get_type_size(node.left_types[i]) + if size !in [1, 2, 4, 8] || node.op !in [.assign, .decl_assign] { + g.v_error('unhandled assign_stmt expression: $right.type_name()', + right.pos()) + } + if node.op == .decl_assign { + g.allocate_var(name, size, 0) + } + g.expr(right) + var := g.get_var_from_ident(ident) + match var { + LocalVar { g.mov_reg_to_var(var as LocalVar, .rax) } + GlobalVar { g.mov_reg_to_var(var as GlobalVar, .rax) } + Register { g.mov_reg(var as Register, .rax) } + } } } // } } } -fn (mut g Gen) infix_expr(node ast.InfixExpr) { - // TODO - if node.left is ast.InfixExpr { - g.n_error('only simple expressions are supported right now (not more than 2 operands)') - } - match node.left { - ast.Ident { - g.mov_var_to_reg(.eax, node.left as ast.Ident) +fn (mut g Gen) cset_op(op token.Kind) { + match op { + .gt { + g.cset(.g) + } + .lt { + g.cset(.l) + } + .ne { + g.cset(.ne) + } + .eq { + g.cset(.e) + } + .ge { + g.cset(.ge) + } + .le { + g.cset(.le) + } + else { + g.cset(.ne) } - else {} } - if node.right is ast.Ident { - var_offset := g.get_var_offset(node.right.name) - match node.op { - .plus { g.add8_var(.eax, var_offset) } - .mul { g.mul8_var(.eax, var_offset) } - .div { g.div8_var(.eax, var_offset) } - .minus { g.sub8_var(.eax, var_offset) } - else {} +} + +fn (mut g Gen) infix_expr(node ast.InfixExpr) { + if node.left is ast.Ident && node.right is ast.Ident { + left := node.left as ast.Ident + right := node.right as ast.Ident + g.mov_var_to_reg(.eax, left) + var_offset := g.get_var_offset(right.name) + for { + match node.op { + .plus { g.add8_var(.eax, var_offset) } + .mul { g.mul8_var(.eax, var_offset) } + .div { g.div8_var(.eax, var_offset) } + .minus { g.sub8_var(.eax, var_offset) } + else { break } + } + return + } + } + g.expr(node.left) + mut label := 0 + g.push(.rax) + if node.op in [.logical_or, .and] { + label = g.labels.new_label() + g.cmp_zero(.rax) + jump_addr := g.cjmp(if node.op == .logical_or { .jne } else { .je }) + g.labels.patches << LabelPatch{ + id: label + pos: jump_addr + } + } + g.expr(node.right) + g.pop(.rdx) + // left: rdx, right: rax + match node.op { + .logical_or, .and { + g.labels.addrs[label] = g.pos() + } + .eq, .ne, .gt, .lt, .ge, .le { + g.cmp_reg(.rdx, .rax) + g.mov64(.rax, 0) + g.cset_op(node.op) + } + else { + g.n_error('`$node.op` expression is not supported right now') } } } @@ -1870,7 +1963,7 @@ fn (mut g Gen) cjmp_op(op token.Kind) int { g.cjmp(.jg) } .lt { - g.cjmp(.lt) + g.cjmp(.jl) } .ne { g.cjmp(.jne) @@ -1884,7 +1977,13 @@ fn (mut g Gen) cjmp_op(op token.Kind) int { } } -fn (mut g Gen) condition(infix_expr ast.InfixExpr, neg bool) int { +fn (mut g Gen) condition(expr ast.Expr, neg bool) int { + // if infix_expr.op !in [.eq, .ne, .gt, .lt, .ge, .le] { + g.expr(expr) + g.cmp_zero(.rax) + return g.cjmp(if neg { .jne } else { .je }) + //} + /* match infix_expr.left { ast.IntegerLiteral { match infix_expr.right { @@ -1933,7 +2032,7 @@ fn (mut g Gen) condition(infix_expr ast.InfixExpr, neg bool) int { } // mut cjmp_addr := 0 // location of `jne *00 00 00 00*` - return if neg { g.cjmp_op(infix_expr.op) } else { g.cjmp_notop(infix_expr.op) } + return if neg { g.cjmp_op(infix_expr.op) } else { g.cjmp_notop(infix_expr.op) }*/ } fn (mut g Gen) if_expr(node ast.IfExpr) { @@ -1959,9 +2058,9 @@ fn (mut g Gen) if_expr(node ast.IfExpr) { } continue } - infix_expr := branch.cond as ast.InfixExpr + expr := branch.cond label := g.labels.new_label() - cjmp_addr := g.condition(infix_expr, false) + cjmp_addr := g.condition(expr, false) g.labels.patches << LabelPatch{ id: label pos: cjmp_addr diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index 96b19e38aa..f57097757f 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -416,8 +416,11 @@ fn (mut g Gen) get_type_size(typ ast.Type) int { if typ in ast.pointer_type_idxs { return 8 } + if typ == ast.bool_type_idx { + return 1 + } // g.n_error('unknown type size') - return 8 + return 0 } fn (mut g Gen) get_sizeof_ident(ident ast.Ident) int { @@ -853,28 +856,33 @@ fn (mut g Gen) stmt(node ast.Stmt) { // dump(node.types) mut s := '?' //${node.exprs[0].val.str()}' // TODO: void return - e0 := node.exprs[0] - match e0 { - ast.IntegerLiteral { - g.mov64(.rax, e0.val.int()) - } - ast.InfixExpr { - g.infix_expr(e0) - } - ast.CastExpr { - g.mov64(.rax, e0.expr.str().int()) - // do the job - } - ast.StringLiteral { - s = e0.val.str() - g.expr(node.exprs[0]) - g.mov64(.rax, g.allocate_string(s, 2, .abs64)) - } - ast.Ident { - g.expr(e0) - } - else { - g.n_error('unknown return type $e0.type_name()') + if e0 := node.exprs[0] { + match e0 { + ast.IntegerLiteral { + g.mov64(.rax, e0.val.int()) + } + ast.InfixExpr { + g.infix_expr(e0) + } + ast.CastExpr { + g.mov64(.rax, e0.expr.str().int()) + // do the job + } + ast.StringLiteral { + s = e0.val.str() + g.expr(node.exprs[0]) + g.mov64(.rax, g.allocate_string(s, 2, .abs64)) + } + ast.Ident { + g.expr(e0) + } + else { + size := g.get_type_size(node.types[0]) + if size !in [1, 2, 4, 8] { + g.n_error('unknown return type $e0.type_name()') + } + g.expr(e0) + } } } @@ -965,7 +973,6 @@ fn (mut g Gen) expr(node ast.Expr) { } ast.BoolLiteral { g.mov64(.rax, if node.val { 1 } else { 0 }) - eprintln('bool literal') } ast.CallExpr { if node.name == 'C.syscall' { diff --git a/vlib/v/gen/native/tests/ifs.vv b/vlib/v/gen/native/tests/ifs.vv index 9945cdd884..2c7cde536c 100644 --- a/vlib/v/gen/native/tests/ifs.vv +++ b/vlib/v/gen/native/tests/ifs.vv @@ -4,6 +4,10 @@ fn print_number(n int) { } } +fn get_bool() bool { + return true +} + fn test_add() { n := 3 print_number(0) @@ -11,14 +15,12 @@ fn test_add() { if n > 1 { println('var(3) > 1') } - /* if 1 < n { println('1 < var(3)') } if 1 > n { println('1 > 3 ERROR') } - */ if 1 < 3 { println('1 < 3') } @@ -38,6 +40,14 @@ fn test_add() { println('1 > 3 ERROR') // TODO assert here } + if get_bool() { + println('bool is true') + } +} + +fn do_not_call() bool { + println('must not be called ERROR') + return false } fn test_elses() { @@ -55,9 +65,45 @@ fn test_elses() { println('end else') } +fn test_logic_op() { + a := true + b := false + if a && a { + println('true && true') + } + if a && b { + println('true && false ERROR') + } + if b && a { + println('false && true ERROR') + } + if b && b { + println('false && false ERROR') + } + if a || a { + println('true || true') + } + if a || b { + println('true || false') + } + if b || a { + println('false || true') + } + if b || b { + println('false || false ERROR') + } + if b && do_not_call() { + println('call ERROR') + } + if a || do_not_call() { + println('short circuit evaluation') + } +} + fn main() { println('start') test_add() test_elses() + test_logic_op() println('end') } diff --git a/vlib/v/gen/native/tests/ifs.vv.out b/vlib/v/gen/native/tests/ifs.vv.out index e53daabe65..6869426c6c 100644 --- a/vlib/v/gen/native/tests/ifs.vv.out +++ b/vlib/v/gen/native/tests/ifs.vv.out @@ -1,11 +1,18 @@ start print_number var(3) > 1 +1 < var(3) 1 < 3 1 == 1 1 != 3 +bool is true start else ok ok end else +true && true +true || true +true || false +false || true +short circuit evaluation end