From 1b691e7612ce6b8b49beb9eda989648756c6460b Mon Sep 17 00:00:00 2001 From: pancake Date: Tue, 30 Nov 2021 10:49:30 +0100 Subject: [PATCH] native: implement for-c and for-in range loops (#12155) --- cmd/tools/vtest-all.v | 8 +- examples/native/hello_world.v | 22 +++-- vlib/v/gen/native/amd64.v | 129 ++++++++++++++++++++------- vlib/v/gen/native/gen.v | 115 ++++++++++++++++++++---- vlib/v/gen/native/tests/forin.vv | 26 ++++++ vlib/v/gen/native/tests/forin.vv.out | 18 ++++ 6 files changed, 259 insertions(+), 59 deletions(-) create mode 100644 vlib/v/gen/native/tests/forin.vv create mode 100644 vlib/v/gen/native/tests/forin.vv.out diff --git a/cmd/tools/vtest-all.v b/cmd/tools/vtest-all.v index 76ca6ac4f..55d581b12 100644 --- a/cmd/tools/vtest-all.v +++ b/cmd/tools/vtest-all.v @@ -71,7 +71,13 @@ fn get_all_commands() []Command { line: '$vexe run examples/v_script.vsh > /dev/null' okmsg: 'V can run the .VSH script file examples/v_script.vsh' } - // + $if linux { + res << Command{ + line: '$vexe -b native run examples/native/hello_world.v > /dev/null' + okmsg: 'V compiles and runs examples/native/hello_world.v on the native backend for linux' + } + } + // only compilation: res << Command{ line: '$vexe -os linux -b native -o hw.linux examples/hello_world.v' okmsg: 'V compiles hello_world.v on the native backend for linux' diff --git a/examples/native/hello_world.v b/examples/native/hello_world.v index f2be9599b..3f5e1c185 100644 --- a/examples/native/hello_world.v +++ b/examples/native/hello_world.v @@ -1,20 +1,18 @@ -// fn println(s string) { } - -// fn test_fn() { -// println('test fn') -//} +fn test_fn() { + println('test fn') +} fn main() { println('native test') - // i := 0 - // for i < 5 { + mut i := 0 + for i < 5 { + println('Hello world from V native machine code generator, in a while style for loop!') + i++ + } for _ in 1 .. 5 { - println('Hello world from V native machine code generator!') - // i++ + println('Hello world from V native machine code generator, in a range loop!') } - /* println('Hello again!') - //test_fn() + test_fn() println('done') - */ } diff --git a/vlib/v/gen/native/amd64.v b/vlib/v/gen/native/amd64.v index cc27eb1bb..72b206960 100644 --- a/vlib/v/gen/native/amd64.v +++ b/vlib/v/gen/native/amd64.v @@ -105,18 +105,36 @@ fn (mut g Gen) cmp(reg Register, size Size, val i64) { g.println('cmp $reg, $val') } -/* -rax // 0 - rcx // 1 - rdx // 2 - rbx // 3 - rsp // 4 - rbp // 5 - rsi // 6 - rdi // 7 -*/ -// `a == 1` -// `cmp DWORD [rbp-0x4],0x1` +// `cmp rax, rbx` +fn (mut g Gen) cmp_reg(reg Register, reg2 Register) { + match reg { + .rax { + match reg2 { + .rbx { + g.write([byte(0x48), 0x39, 0xd8]) + } + else { + g.n_error('Cannot compare $reg and $reg2') + } + } + } + .rbx { + match reg2 { + .rax { + g.write([byte(0x48), 0x39, 0xc3]) + } + else { + g.n_error('Cannot compare $reg and $reg2') + } + } + } + else { + g.n_error('Cannot compare $reg and $reg2') + } + } + g.println('cmp $reg, $reg2') +} + fn (mut g Gen) cmp_var(var_name string, val int) { g.write8(0x81) // 83 for 1 byte? g.write8(0x7d) @@ -126,6 +144,15 @@ fn (mut g Gen) cmp_var(var_name string, val int) { g.println('cmp var `$var_name` $val') } +// `sub DWORD [rbp-0x4], 1` +fn (mut g Gen) dec_var(var_name string) { + g.write16(0x6d81) // 83 for 1 byte + offset := g.get_var_offset(var_name) + g.write8(0xff - offset + 1) + g.write32(1) + g.println('dec_var `$var_name`') +} + // `add DWORD [rbp-0x4], 1` fn (mut g Gen) inc_var(var_name string) { g.write16(0x4581) // 83 for 1 byte @@ -258,9 +285,9 @@ fn (mut g Gen) mov_reg_to_var(var_offset int, reg Register) { } fn (mut g Gen) mov_var_to_reg(reg Register, var_offset int) { - // 8b 7d f8 mov edi,DWORD PTR [rbp-0x8] + // 8b 7d f8 mov edi,DWORD PTR [rbp-0x8] match reg { - .rax, .rsi { + .rax, .rbx, .rsi { g.write8(0x48) } else {} @@ -616,6 +643,7 @@ fn (mut g Gen) lea(reg Register, val int) { g.write8(0x8d) g.write8(0x15) g.write32(val) + g.println('lea $reg, $val') } fn (mut g Gen) mov(reg Register, val int) { @@ -626,7 +654,7 @@ fn (mut g Gen) mov(reg Register, val int) { g.write8(0xc7) g.write8(0xc0) g.write32(-1) - return + g.println('mov $reg, $val') } .rcx { if val == -1 { @@ -638,12 +666,14 @@ fn (mut g Gen) mov(reg Register, val int) { g.write8(0xff) g.write8(0xff) // mov rcx 0xffff5 } - return + g.println('mov $reg, $val') } else { g.n_error('unhandled mov $reg, -1') } } + g.println('mov $reg, $val') + return } if val == 0 { // Optimise to xor reg, reg when val is 0 @@ -885,8 +915,7 @@ pub fn (mut g Gen) call_fn(node ast.CallExpr) { } else { g.call(int(addr)) } - g.println('fn call `${name}()`') - // println('call $name $addr') + g.println('call `${name}()`') } fn (mut g Gen) patch_calls() { @@ -953,17 +982,35 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) { g.mov_reg_to_var(dest, .rax) } .decl_assign { - g.allocate_var(name, 4, right.val.int()) + g.allocate_var(name, 8, right.val.int()) } .assign { - match node.left_types[i] { - 7 { // ast.IndexExpr { + // dump(g.typ(node.left_types[i])) + match node.left[i] { + ast.Ident { + // lname := '${node.left[i]}' + // g.expr(node.right[i]) + g.mov(.rax, right.val.int()) + offset := g.get_var_offset('i') // node.left[i]) + g.mov_reg_to_var(offset, .rax) + } + ast.InfixExpr { + eprintln('assign') + // dump(node.left[i]) + offset := g.get_var_offset('i') // node.left[i]) + g.mov_reg_to_var(offset, native.fn_arg_registers[i]) + } + /* + ast.int_type_idx { + g.expr(node.left[i]) +match node.left[i] { +ast.IndexExpr { ie := node.left[i] as ast.IndexExpr bracket := name.index('[') or { g.v_error('bracket expected', node.pos) exit(1) } - var_name := name[0..bracket] + var_name := name[0 .. bracket] mut dest := g.get_var_offset(var_name) index := ie.index as ast.IntegerLiteral dest += index.val.int() * 8 @@ -971,7 +1018,13 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) { g.mov(.rax, right.val.int()) g.mov_reg_to_var(dest, .rax) // eprintln('${var_name}[$index] = ${right.val.int()}') +} else { +dump(node) +g.v_error('oops', node.pos) +} +} } + */ else { tn := node.left[i].type_name() dump(node.left_types) @@ -988,12 +1041,12 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) { ast.InfixExpr { // eprintln('infix') dump(node) dump(right) g.infix_expr(right) - offset := g.allocate_var(name, 4, 0) + offset := g.allocate_var(name, 8, 0) // `mov DWORD PTR [rbp-0x8],eax` if g.pref.is_verbose { println('infix assignment $name offset=$offset.hex2()') } - g.mov_reg_to_var(offset, .eax) + g.mov_reg_to_var(offset, .rax) } ast.Ident { // eprintln('identr') dump(node) dump(right) @@ -1020,7 +1073,7 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) { g.mov_reg_to_var(dest, .rax) } .decl_assign { - dest := g.allocate_var(name, 4, 0) + dest := g.allocate_var(name, 8, 0) g.mov_var_to_reg(.rax, g.get_var_offset(right.name)) g.mov_reg_to_var(dest, .rax) } @@ -1069,7 +1122,7 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) { } ast.IndexExpr { // a := arr[0] - offset := g.allocate_var(name, 4, 0) + offset := g.allocate_var(name, 8, 0) if g.pref.is_verbose { println('infix assignment $name offset=$offset.hex2()') } @@ -1131,9 +1184,6 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) { } fn (mut g Gen) infix_expr(node ast.InfixExpr) { - if g.pref.is_verbose { - println('infix expr op=$node.op') - } // TODO if node.left is ast.InfixExpr { g.n_error('only simple expressions are supported right now (not more than 2 operands)') @@ -1229,31 +1279,39 @@ fn (mut g Gen) gen_asm_stmt_amd64(asm_node ast.AsmStmt) { match t.name { 'nop' { g.write8(byte(0x90)) + g.println('nop') } 'syscall' { g.write8(byte(0x0f)) g.write8(byte(0x05)) + g.println('syscall') } 'ret' { g.write8(byte(0xc3)) + g.println('ret') } 'int3' { g.write8(byte(0xcc)) g.write8(byte(imm)) + g.println('int3') } 'sti' { g.write8(byte(0xfb)) + g.println('sti') } 'cli' { g.write8(byte(0xfa)) + g.println('cli') } 'int' { g.write8(byte(0xcd)) g.write8(byte(imm)) + g.println('int') } 'cpuid' { g.write8(byte(0x0f)) g.write8(byte(0xa2)) + g.println('cpuid') } 'mov' { g.write8(byte(0xb8 + reg)) @@ -1261,6 +1319,7 @@ fn (mut g Gen) gen_asm_stmt_amd64(asm_node ast.AsmStmt) { g.write8(byt(imm, 1)) g.write8(byt(imm, 2)) g.write8(byt(imm, 3)) + g.println('mov $reg, $imm') } else { g.v_error('unsupported instruction $t.name', asm_node.pos) @@ -1437,7 +1496,17 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) { ast.Ident { lit := infix_expr.right as ast.IntegerLiteral g.cmp_var(infix_expr.left.name, lit.val.int()) - jump_addr = g.cjmp(.jge) + match infix_expr.left.tok_kind { + .lt { + jump_addr = g.cjmp(.jge) + } + .gt { + jump_addr = g.cjmp(.jle) + } + else { + g.n_error('unhandled infix cond token') + } + } } else { g.n_error('unhandled infix.left') diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index f412cb19e..7853c8697 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -446,25 +446,84 @@ fn (mut g Gen) println(comment string) { println(' ' + comment) } +fn (mut g Gen) gen_forc_stmt(node ast.ForCStmt) { + if node.has_init { + g.stmts([node.init]) + } + start := g.pos() + mut jump_addr := i64(0) + if node.has_cond { + cond := node.cond + match cond { + ast.InfixExpr { + // g.infix_expr(node.cond) + match mut cond.left { + ast.Ident { + lit := cond.right as ast.IntegerLiteral + g.cmp_var(cond.left.name, lit.val.int()) + match cond.op { + .gt { + jump_addr = g.cjmp(.jle) + } + .lt { + jump_addr = g.cjmp(.jge) + } + else { + g.n_error('unsupported conditional in for-c loop') + } + } + } + else { + g.n_error('unhandled infix.left') + } + } + } + else {} + } + // dump(node.cond) + g.expr(node.cond) + } + g.stmts(node.stmts) + if node.has_inc { + g.stmts([node.inc]) + } + g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1)) + g.write32_at(jump_addr, int(g.pos() - jump_addr - 4)) + + // loop back +} + fn (mut g Gen) for_in_stmt(node ast.ForInStmt) { - g.v_error('for-in statement is not yet implemented', node.pos) - /* + if node.stmts.len == 0 { + // if no statements, just dont make it + return + } if node.is_range { - // `for x in 1..10 {` - // i := if node.val_var == '_' { g.new_tmp_var() } else { c_name(node.val_var) } - // val_typ := g.table.mktyp(node.val_type) - g.write32(0x3131) // 'for (${g.typ(val_typ)} $i = ') + // for a in node.cond .. node.high { + i := g.allocate_var(node.val_var, 8, 0) // iterator variable g.expr(node.cond) - g.write32(0x3232) // ; $i < ') - g.expr(node.high) - g.write32(0x3333) // '; ++$i) {') + g.mov_reg_to_var(i, .rax) // i = node.cond // initial value + start := g.pos() // label-begin: + g.mov_var_to_reg(.rbx, i) // rbx = iterator value + g.expr(node.high) // final value + g.cmp_reg(.rbx, .rax) // rbx = iterator, rax = max value + jump_addr := g.cjmp(.jge) // leave loop if i is beyond end + g.stmts(node.stmts) + g.inc_var(node.val_var) + g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1)) + g.write32_at(jump_addr, int(g.pos() - jump_addr - 4)) + /* } else if node.kind == .array { } else if node.kind == .array_fixed { } else if node.kind == .map { } else if node.kind == .string { } else if node.kind == .struct_ { + } else if it.kind in [.array, .string] || it.cond_type.has_flag(.variadic) { + } else if it.kind == .map { + */ + } else { + g.v_error('for-in statement is not yet implemented', node.pos) } - */ } pub fn (mut g Gen) gen_exit(node ast.Expr) { @@ -487,6 +546,9 @@ fn (mut g Gen) stmt(node ast.Stmt) { ast.FnDecl { g.fn_decl(node) } + ast.ForCStmt { + g.gen_forc_stmt(node) + } ast.ForInStmt { g.for_in_stmt(node) } @@ -619,7 +681,10 @@ fn (mut g Gen) expr(node ast.Expr) { ast.ArrayInit { g.n_error('array init expr not supported yet') } - ast.BoolLiteral {} + ast.BoolLiteral { + g.mov64(.rax, if node.val { 1 } else { 0 }) + eprintln('bool literal') + } ast.CallExpr { if node.name == 'C.syscall' { g.gen_syscall(node) @@ -633,7 +698,12 @@ fn (mut g Gen) expr(node ast.Expr) { } } ast.FloatLiteral {} - ast.Ident {} + ast.Ident { + offset := g.get_var_offset(node.obj.name) // i := 0 + // offset := g.get_var_offset(node.name) + // XXX this is intel specific + g.mov_var_to_reg(.rax, offset) + } ast.IfExpr { if node.is_comptime { eprintln('Warning: ignored compile time conditional not yet supported for the native backend.') @@ -641,8 +711,15 @@ fn (mut g Gen) expr(node ast.Expr) { g.if_expr(node) } } - ast.InfixExpr {} - ast.IntegerLiteral {} + ast.InfixExpr { + g.infix_expr(node) + // get variable by name + // save the result in rax + } + ast.IntegerLiteral { + g.mov64(.rax, node.val.int()) + // g.gen_print_reg(.rax, 3, fd) + } ast.PostfixExpr { g.postfix_expr(node) } @@ -669,8 +746,14 @@ fn (mut g Gen) postfix_expr(node ast.PostfixExpr) { } ident := node.expr as ast.Ident var_name := ident.name - if node.op == .inc { - g.inc_var(var_name) + match node.op { + .inc { + g.inc_var(var_name) + } + .dec { + g.dec_var(var_name) + } + else {} } } diff --git a/vlib/v/gen/native/tests/forin.vv b/vlib/v/gen/native/tests/forin.vv new file mode 100644 index 000000000..db00f5458 --- /dev/null +++ b/vlib/v/gen/native/tests/forin.vv @@ -0,0 +1,26 @@ +fn main() { + mut i := 0 + for i = 0; i < 3; i++ { + println('loop0') + } + i = 0 + for i < 3 { + println('loop1') + i++ + } + for _ in 0 .. 3 { + println('loop2') + } + n := 3 + for _ in 0 .. n { + println('loop3') + } + for i = 3; i > 0; i-- { + println('loop4') + } + i = 3 + for i > 0 { + println('loop5') + i-- + } +} diff --git a/vlib/v/gen/native/tests/forin.vv.out b/vlib/v/gen/native/tests/forin.vv.out new file mode 100644 index 000000000..32e0af2a8 --- /dev/null +++ b/vlib/v/gen/native/tests/forin.vv.out @@ -0,0 +1,18 @@ +loop0 +loop0 +loop0 +loop1 +loop1 +loop1 +loop2 +loop2 +loop2 +loop3 +loop3 +loop3 +loop4 +loop4 +loop4 +loop5 +loop5 +loop5 -- 2.30.2