1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

native: support ||, && and simple boolean expression evaluation (#15256)

This commit is contained in:
lemon 2022-07-29 13:17:01 +09:00 committed by GitHub
parent 04b28d11be
commit 927ec1fadb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 211 additions and 52 deletions

View File

@ -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

View File

@ -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' {

View File

@ -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')
}

View File

@ -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