native: implement for-c and for-in range loops (#12155)

pull/12615/head
pancake 2021-11-30 10:49:30 +01:00 committed by GitHub
parent 05db3533d3
commit 1b691e7612
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 262 additions and 62 deletions

View File

@ -71,7 +71,13 @@ fn get_all_commands() []Command {
line: '$vexe run examples/v_script.vsh > /dev/null' line: '$vexe run examples/v_script.vsh > /dev/null'
okmsg: 'V can run the .VSH script file examples/v_script.vsh' 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{ res << Command{
line: '$vexe -os linux -b native -o hw.linux examples/hello_world.v' 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' okmsg: 'V compiles hello_world.v on the native backend for linux'

View File

@ -1,20 +1,18 @@
// fn println(s string) { } fn test_fn() {
println('test fn')
// fn test_fn() { }
// println('test fn')
//}
fn main() { fn main() {
println('native test') println('native test')
// i := 0 mut i := 0
// for i < 5 { for i < 5 {
for _ in 1 .. 5 { println('Hello world from V native machine code generator, in a while style for loop!')
println('Hello world from V native machine code generator!') i++
// i++ }
for _ in 1 .. 5 {
println('Hello world from V native machine code generator, in a range loop!')
} }
/*
println('Hello again!') println('Hello again!')
//test_fn() test_fn()
println('done') println('done')
*/
} }

View File

@ -105,18 +105,36 @@ fn (mut g Gen) cmp(reg Register, size Size, val i64) {
g.println('cmp $reg, $val') g.println('cmp $reg, $val')
} }
/* // `cmp rax, rbx`
rax // 0 fn (mut g Gen) cmp_reg(reg Register, reg2 Register) {
rcx // 1 match reg {
rdx // 2 .rax {
rbx // 3 match reg2 {
rsp // 4 .rbx {
rbp // 5 g.write([byte(0x48), 0x39, 0xd8])
rsi // 6 }
rdi // 7 else {
*/ g.n_error('Cannot compare $reg and $reg2')
// `a == 1` }
// `cmp DWORD [rbp-0x4],0x1` }
}
.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) { fn (mut g Gen) cmp_var(var_name string, val int) {
g.write8(0x81) // 83 for 1 byte? g.write8(0x81) // 83 for 1 byte?
g.write8(0x7d) 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') 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` // `add DWORD [rbp-0x4], 1`
fn (mut g Gen) inc_var(var_name string) { fn (mut g Gen) inc_var(var_name string) {
g.write16(0x4581) // 83 for 1 byte 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) { 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 { match reg {
.rax, .rsi { .rax, .rbx, .rsi {
g.write8(0x48) g.write8(0x48)
} }
else {} else {}
@ -616,6 +643,7 @@ fn (mut g Gen) lea(reg Register, val int) {
g.write8(0x8d) g.write8(0x8d)
g.write8(0x15) g.write8(0x15)
g.write32(val) g.write32(val)
g.println('lea $reg, $val')
} }
fn (mut g Gen) mov(reg Register, val int) { 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(0xc7)
g.write8(0xc0) g.write8(0xc0)
g.write32(-1) g.write32(-1)
return g.println('mov $reg, $val')
} }
.rcx { .rcx {
if val == -1 { if val == -1 {
@ -638,12 +666,14 @@ fn (mut g Gen) mov(reg Register, val int) {
g.write8(0xff) g.write8(0xff)
g.write8(0xff) // mov rcx 0xffff5 g.write8(0xff) // mov rcx 0xffff5
} }
return g.println('mov $reg, $val')
} }
else { else {
g.n_error('unhandled mov $reg, -1') g.n_error('unhandled mov $reg, -1')
} }
} }
g.println('mov $reg, $val')
return
} }
if val == 0 { if val == 0 {
// Optimise to xor reg, reg when val is 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 { } else {
g.call(int(addr)) g.call(int(addr))
} }
g.println('fn call `${name}()`') g.println('call `${name}()`')
// println('call $name $addr')
} }
fn (mut g Gen) patch_calls() { 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) g.mov_reg_to_var(dest, .rax)
} }
.decl_assign { .decl_assign {
g.allocate_var(name, 4, right.val.int()) g.allocate_var(name, 8, right.val.int())
} }
.assign { .assign {
match node.left_types[i] { // dump(g.typ(node.left_types[i]))
7 { // ast.IndexExpr { 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 ie := node.left[i] as ast.IndexExpr
bracket := name.index('[') or { bracket := name.index('[') or {
g.v_error('bracket expected', node.pos) g.v_error('bracket expected', node.pos)
exit(1) exit(1)
} }
var_name := name[0..bracket] var_name := name[0 .. bracket]
mut dest := g.get_var_offset(var_name) mut dest := g.get_var_offset(var_name)
index := ie.index as ast.IntegerLiteral index := ie.index as ast.IntegerLiteral
dest += index.val.int() * 8 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(.rax, right.val.int())
g.mov_reg_to_var(dest, .rax) g.mov_reg_to_var(dest, .rax)
// eprintln('${var_name}[$index] = ${right.val.int()}') // eprintln('${var_name}[$index] = ${right.val.int()}')
} else {
dump(node)
g.v_error('oops', node.pos)
}
}
} }
*/
else { else {
tn := node.left[i].type_name() tn := node.left[i].type_name()
dump(node.left_types) dump(node.left_types)
@ -988,12 +1041,12 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) {
ast.InfixExpr { ast.InfixExpr {
// eprintln('infix') dump(node) dump(right) // eprintln('infix') dump(node) dump(right)
g.infix_expr(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` // `mov DWORD PTR [rbp-0x8],eax`
if g.pref.is_verbose { if g.pref.is_verbose {
println('infix assignment $name offset=$offset.hex2()') println('infix assignment $name offset=$offset.hex2()')
} }
g.mov_reg_to_var(offset, .eax) g.mov_reg_to_var(offset, .rax)
} }
ast.Ident { ast.Ident {
// eprintln('identr') dump(node) dump(right) // 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) g.mov_reg_to_var(dest, .rax)
} }
.decl_assign { .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_var_to_reg(.rax, g.get_var_offset(right.name))
g.mov_reg_to_var(dest, .rax) g.mov_reg_to_var(dest, .rax)
} }
@ -1069,7 +1122,7 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) {
} }
ast.IndexExpr { ast.IndexExpr {
// a := arr[0] // a := arr[0]
offset := g.allocate_var(name, 4, 0) offset := g.allocate_var(name, 8, 0)
if g.pref.is_verbose { if g.pref.is_verbose {
println('infix assignment $name offset=$offset.hex2()') 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) { fn (mut g Gen) infix_expr(node ast.InfixExpr) {
if g.pref.is_verbose {
println('infix expr op=$node.op')
}
// TODO // TODO
if node.left is ast.InfixExpr { if node.left is ast.InfixExpr {
g.n_error('only simple expressions are supported right now (not more than 2 operands)') 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 { match t.name {
'nop' { 'nop' {
g.write8(byte(0x90)) g.write8(byte(0x90))
g.println('nop')
} }
'syscall' { 'syscall' {
g.write8(byte(0x0f)) g.write8(byte(0x0f))
g.write8(byte(0x05)) g.write8(byte(0x05))
g.println('syscall')
} }
'ret' { 'ret' {
g.write8(byte(0xc3)) g.write8(byte(0xc3))
g.println('ret')
} }
'int3' { 'int3' {
g.write8(byte(0xcc)) g.write8(byte(0xcc))
g.write8(byte(imm)) g.write8(byte(imm))
g.println('int3')
} }
'sti' { 'sti' {
g.write8(byte(0xfb)) g.write8(byte(0xfb))
g.println('sti')
} }
'cli' { 'cli' {
g.write8(byte(0xfa)) g.write8(byte(0xfa))
g.println('cli')
} }
'int' { 'int' {
g.write8(byte(0xcd)) g.write8(byte(0xcd))
g.write8(byte(imm)) g.write8(byte(imm))
g.println('int')
} }
'cpuid' { 'cpuid' {
g.write8(byte(0x0f)) g.write8(byte(0x0f))
g.write8(byte(0xa2)) g.write8(byte(0xa2))
g.println('cpuid')
} }
'mov' { 'mov' {
g.write8(byte(0xb8 + reg)) 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, 1))
g.write8(byt(imm, 2)) g.write8(byt(imm, 2))
g.write8(byt(imm, 3)) g.write8(byt(imm, 3))
g.println('mov $reg, $imm')
} }
else { else {
g.v_error('unsupported instruction $t.name', asm_node.pos) 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 { ast.Ident {
lit := infix_expr.right as ast.IntegerLiteral lit := infix_expr.right as ast.IntegerLiteral
g.cmp_var(infix_expr.left.name, lit.val.int()) 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 { else {
g.n_error('unhandled infix.left') g.n_error('unhandled infix.left')

View File

@ -446,25 +446,84 @@ fn (mut g Gen) println(comment string) {
println(' ' + comment) println(' ' + comment)
} }
fn (mut g Gen) for_in_stmt(node ast.ForInStmt) { fn (mut g Gen) gen_forc_stmt(node ast.ForCStmt) {
g.v_error('for-in statement is not yet implemented', node.pos) if node.has_init {
/* g.stmts([node.init])
if node.is_range { }
// `for x in 1..10 {` start := g.pos()
// i := if node.val_var == '_' { g.new_tmp_var() } else { c_name(node.val_var) } mut jump_addr := i64(0)
// val_typ := g.table.mktyp(node.val_type) if node.has_cond {
g.write32(0x3131) // 'for (${g.typ(val_typ)} $i = ') 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.expr(node.cond)
g.write32(0x3232) // ; $i < ') }
g.expr(node.high) g.stmts(node.stmts)
g.write32(0x3333) // '; ++$i) {') 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) {
if node.stmts.len == 0 {
// if no statements, just dont make it
return
}
if node.is_range {
// for a in node.cond .. node.high {
i := g.allocate_var(node.val_var, 8, 0) // iterator variable
g.expr(node.cond)
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 {
} else if node.kind == .array_fixed { } else if node.kind == .array_fixed {
} else if node.kind == .map { } else if node.kind == .map {
} else if node.kind == .string { } else if node.kind == .string {
} else if node.kind == .struct_ { } 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) { pub fn (mut g Gen) gen_exit(node ast.Expr) {
@ -487,6 +546,9 @@ fn (mut g Gen) stmt(node ast.Stmt) {
ast.FnDecl { ast.FnDecl {
g.fn_decl(node) g.fn_decl(node)
} }
ast.ForCStmt {
g.gen_forc_stmt(node)
}
ast.ForInStmt { ast.ForInStmt {
g.for_in_stmt(node) g.for_in_stmt(node)
} }
@ -619,7 +681,10 @@ fn (mut g Gen) expr(node ast.Expr) {
ast.ArrayInit { ast.ArrayInit {
g.n_error('array init expr not supported yet') 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 { ast.CallExpr {
if node.name == 'C.syscall' { if node.name == 'C.syscall' {
g.gen_syscall(node) g.gen_syscall(node)
@ -633,7 +698,12 @@ fn (mut g Gen) expr(node ast.Expr) {
} }
} }
ast.FloatLiteral {} 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 { ast.IfExpr {
if node.is_comptime { if node.is_comptime {
eprintln('Warning: ignored compile time conditional not yet supported for the native backend.') 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) g.if_expr(node)
} }
} }
ast.InfixExpr {} ast.InfixExpr {
ast.IntegerLiteral {} 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 { ast.PostfixExpr {
g.postfix_expr(node) g.postfix_expr(node)
} }
@ -669,8 +746,14 @@ fn (mut g Gen) postfix_expr(node ast.PostfixExpr) {
} }
ident := node.expr as ast.Ident ident := node.expr as ast.Ident
var_name := ident.name var_name := ident.name
if node.op == .inc { match node.op {
g.inc_var(var_name) .inc {
g.inc_var(var_name)
}
.dec {
g.dec_var(var_name)
}
else {}
} }
} }

View File

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

View File

@ -0,0 +1,18 @@
loop0
loop0
loop0
loop1
loop1
loop1
loop2
loop2
loop2
loop3
loop3
loop3
loop4
loop4
loop4
loop5
loop5
loop5