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

View File

@ -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 {
for _ in 1 .. 5 {
println('Hello world from V native machine code generator!')
// i++
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, in a range loop!')
}
/*
println('Hello again!')
//test_fn()
test_fn()
println('done')
*/
}

View File

@ -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
@ -260,7 +287,7 @@ 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]
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,8 +1496,18 @@ 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())
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')
}

View File

@ -446,25 +446,84 @@ fn (mut g Gen) println(comment string) {
println(' ' + comment)
}
fn (mut g Gen) for_in_stmt(node ast.ForInStmt) {
g.v_error('for-in statement is not yet implemented', node.pos)
/*
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 = ')
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.write32(0x3232) // ; $i < ')
g.expr(node.high)
g.write32(0x3333) // '; ++$i) {')
}
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) {
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_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,9 +746,15 @@ fn (mut g Gen) postfix_expr(node ast.PostfixExpr) {
}
ident := node.expr as ast.Ident
var_name := ident.name
if node.op == .inc {
match node.op {
.inc {
g.inc_var(var_name)
}
.dec {
g.dec_var(var_name)
}
else {}
}
}
[noreturn]

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