x64: move all the amd64 specific code out of gen.v
parent
aebb551e93
commit
7587458521
|
@ -1,10 +1,640 @@
|
||||||
module x64
|
module x64
|
||||||
|
|
||||||
|
import term
|
||||||
|
import v.ast
|
||||||
|
import strings
|
||||||
|
|
||||||
pub struct Amd64 {
|
pub struct Amd64 {
|
||||||
|
mut:
|
||||||
|
g Gen
|
||||||
// arm64 specific stuff for code generation
|
// arm64 specific stuff for code generation
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut x Amd64) allocate_var(mut g Gen, name string, size int, initial_val int) {
|
// string_addr map[string]i64
|
||||||
|
// The registers are ordered for faster generation
|
||||||
|
// push rax => 50
|
||||||
|
// push rcx => 51 etc
|
||||||
|
enum Register {
|
||||||
|
rax
|
||||||
|
rcx
|
||||||
|
rdx
|
||||||
|
rbx
|
||||||
|
rsp
|
||||||
|
rbp
|
||||||
|
rsi
|
||||||
|
rdi
|
||||||
|
eax
|
||||||
|
edi
|
||||||
|
edx
|
||||||
|
r8
|
||||||
|
r9
|
||||||
|
r10
|
||||||
|
r11
|
||||||
|
r12
|
||||||
|
r13
|
||||||
|
r14
|
||||||
|
r15
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
fn_arg_registers = [Register.rdi, .rsi, .rdx, .rcx, .r8, .r9]
|
||||||
|
)
|
||||||
|
|
||||||
|
fn (mut g Gen) inc(reg Register) {
|
||||||
|
g.write16(0xff49)
|
||||||
|
match reg {
|
||||||
|
.r12 { g.write8(0xc4) }
|
||||||
|
else { panic('unhandled inc $reg') }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) cmp(reg Register, size Size, val i64) {
|
||||||
|
g.write8(0x49)
|
||||||
|
// Second byte depends on the size of the value
|
||||||
|
match size {
|
||||||
|
._8 { g.write8(0x83) }
|
||||||
|
._32 { g.write8(0x81) }
|
||||||
|
else { panic('unhandled cmp') }
|
||||||
|
}
|
||||||
|
// Third byte depends on the register being compared to
|
||||||
|
match reg {
|
||||||
|
.r12 { g.write8(0xfc) }
|
||||||
|
else { panic('unhandled cmp') }
|
||||||
|
}
|
||||||
|
g.write8(int(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
rax // 0
|
||||||
|
rcx // 1
|
||||||
|
rdx // 2
|
||||||
|
rbx // 3
|
||||||
|
rsp // 4
|
||||||
|
rbp // 5
|
||||||
|
rsi // 6
|
||||||
|
rdi // 7
|
||||||
|
*/
|
||||||
|
// `a == 1`
|
||||||
|
// `cmp DWORD [rbp-0x4],0x1`
|
||||||
|
fn (mut g Gen) cmp_var(var_name string, val int) {
|
||||||
|
g.write8(0x81) // 83 for 1 byte?
|
||||||
|
g.write8(0x7d)
|
||||||
|
offset := g.get_var_offset(var_name)
|
||||||
|
g.write8(0xff - offset + 1)
|
||||||
|
g.write32(val)
|
||||||
|
g.println('cmp var `$var_name` $val')
|
||||||
|
}
|
||||||
|
|
||||||
|
// `add DWORD [rbp-0x4], 1`
|
||||||
|
fn (mut g Gen) inc_var(var_name string) {
|
||||||
|
g.write16(0x4581) // 83 for 1 byte
|
||||||
|
offset := g.get_var_offset(var_name)
|
||||||
|
g.write8(0xff - offset + 1)
|
||||||
|
g.write32(1)
|
||||||
|
g.println('inc_var `$var_name`')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the position of the address to jump to (set later).
|
||||||
|
fn (mut g Gen) jne() int {
|
||||||
|
g.write16(0x850f)
|
||||||
|
pos := g.pos()
|
||||||
|
g.write32(placeholder)
|
||||||
|
g.println('jne')
|
||||||
|
return int(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) jge() int {
|
||||||
|
g.write16(0x8d0f)
|
||||||
|
pos := g.pos()
|
||||||
|
g.write32(placeholder)
|
||||||
|
g.println('jne')
|
||||||
|
return int(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) jmp(addr int) {
|
||||||
|
g.write8(0xe9)
|
||||||
|
g.write32(addr) // 0xffffff
|
||||||
|
g.println('jmp')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn abs(a i64) i64 {
|
||||||
|
return if a < 0 { -a } else { a }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) jle(addr i64) {
|
||||||
|
// Calculate the relative offset to jump to
|
||||||
|
// (`addr` is absolute address)
|
||||||
|
offset := 0xff - int(abs(addr - g.buf.len)) - 1
|
||||||
|
g.write8(0x7e)
|
||||||
|
g.write8(offset)
|
||||||
|
g.println('jle')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) println(comment string) {
|
||||||
|
g.nlines++
|
||||||
|
if !g.pref.is_verbose {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addr := g.debug_pos.hex()
|
||||||
|
// println('$g.debug_pos "$addr"')
|
||||||
|
print(term.red(strings.repeat(`0`, 6 - addr.len) + addr + ' '))
|
||||||
|
for i := g.debug_pos; i < g.buf.len; i++ {
|
||||||
|
s := g.buf[i].hex()
|
||||||
|
if s.len == 1 {
|
||||||
|
print(term.blue('0'))
|
||||||
|
}
|
||||||
|
gbihex := g.buf[i].hex()
|
||||||
|
hexstr := term.blue(gbihex) + ' '
|
||||||
|
print(hexstr)
|
||||||
|
}
|
||||||
|
g.debug_pos = g.buf.len
|
||||||
|
print(' ' + comment)
|
||||||
|
println('')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) jl(addr i64) {
|
||||||
|
offset := 0xff - int(abs(addr - g.buf.len)) - 1
|
||||||
|
g.write8(0x7c)
|
||||||
|
g.write8(offset)
|
||||||
|
g.println('jl')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g &Gen) abs_to_rel_addr(addr i64) int {
|
||||||
|
return int(abs(addr - g.buf.len)) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn (mut g Gen) jmp(addr i64) {
|
||||||
|
offset := 0xff - g.abs_to_rel_addr(addr)
|
||||||
|
g.write8(0xe9)
|
||||||
|
g.write8(offset)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
fn (mut g Gen) mov64(reg Register, val i64) {
|
||||||
|
match reg {
|
||||||
|
.rsi {
|
||||||
|
g.write8(0x48)
|
||||||
|
g.write8(0xbe)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
println('unhandled mov $reg')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.write64(val)
|
||||||
|
g.println('mov64 $reg, $val')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) mov_reg_to_rbp(var_offset int, reg Register) {
|
||||||
|
// 89 7d fc mov DWORD PTR [rbp-0x4],edi
|
||||||
|
g.write8(0x89)
|
||||||
|
match reg {
|
||||||
|
.eax, .rax { g.write8(0x45) }
|
||||||
|
.edi, .rdi { g.write8(0x7d) }
|
||||||
|
.rsi { g.write8(0x75) }
|
||||||
|
.rdx { g.write8(0x55) }
|
||||||
|
.rcx { g.write8(0x4d) }
|
||||||
|
else { verror('mov_from_reg $reg') }
|
||||||
|
}
|
||||||
|
g.write8(0xff - var_offset + 1)
|
||||||
|
g.println('mov DWORD PTR[rbp-$var_offset.hex2()],$reg')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) mov_var_to_reg(reg Register, var_offset int) {
|
||||||
|
// 8b 7d f8 mov edi,DWORD PTR [rbp-0x8]
|
||||||
|
g.write8(0x8b)
|
||||||
|
match reg {
|
||||||
|
.eax, .rax { g.write8(0x45) }
|
||||||
|
.edi, .rdi { g.write8(0x7d) }
|
||||||
|
.rsi { g.write8(0x75) }
|
||||||
|
.rdx { g.write8(0x55) }
|
||||||
|
.rcx { g.write8(0x4d) }
|
||||||
|
else { verror('mov_var_to_reg $reg') }
|
||||||
|
}
|
||||||
|
g.write8(0xff - var_offset + 1)
|
||||||
|
g.println('mov $reg,DWORD PTR[rbp-$var_offset.hex2()]')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) call(addr int) {
|
||||||
|
// Need to calculate the difference between current position (position after the e8 call)
|
||||||
|
// and the function to call.
|
||||||
|
// +5 is to get the posistion "e8 xx xx xx xx"
|
||||||
|
// Not sure about the -1.
|
||||||
|
rel := 0xffffffff - (g.buf.len + 5 - addr - 1)
|
||||||
|
// println('call addr=$addr.hex2() rel_addr=$rel.hex2() pos=$g.buf.len')
|
||||||
|
g.write8(0xe8)
|
||||||
|
g.write32(rel)
|
||||||
|
// g.println('fn call')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) syscall() {
|
||||||
|
// g.write(0x050f)
|
||||||
|
g.write8(0x0f)
|
||||||
|
g.write8(0x05)
|
||||||
|
g.println('syscall')
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) ret() {
|
||||||
|
g.write8(0xc3)
|
||||||
|
g.println('ret')
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) push(reg Register) {
|
||||||
|
if int(reg) < int(Register.r8) {
|
||||||
|
g.write8(0x50 + int(reg))
|
||||||
|
} else {
|
||||||
|
g.write8(0x41)
|
||||||
|
g.write8(0x50 + int(reg) - 8)
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
match reg {
|
||||||
|
.rbp { g.write8(0x55) }
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
g.println('push $reg')
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) pop(reg Register) {
|
||||||
|
g.write8(0x58 + int(reg))
|
||||||
|
// TODO r8...
|
||||||
|
g.println('pop $reg')
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) sub32(reg Register, val int) {
|
||||||
|
g.write8(0x48)
|
||||||
|
g.write8(0x81)
|
||||||
|
g.write8(0xe8 + int(reg)) // TODO rax is different?
|
||||||
|
g.write32(val)
|
||||||
|
g.println('sub32 $reg,$val.hex2()')
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) sub8(reg Register, val int) {
|
||||||
|
g.write8(0x48)
|
||||||
|
g.write8(0x83)
|
||||||
|
g.write8(0xe8 + int(reg)) // TODO rax is different?
|
||||||
|
g.write8(val)
|
||||||
|
g.println('sub8 $reg,$val.hex2()')
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) add(reg Register, val int) {
|
||||||
|
g.write8(0x48)
|
||||||
|
g.write8(0x81)
|
||||||
|
g.write8(0xe8 + int(reg)) // TODO rax is different?
|
||||||
|
g.write32(val)
|
||||||
|
g.println('add $reg,$val.hex2()')
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) add8(reg Register, val int) {
|
||||||
|
g.write8(0x48)
|
||||||
|
g.write8(0x83)
|
||||||
|
// g.write8(0xe8 + reg) // TODO rax is different?
|
||||||
|
g.write8(0xc4)
|
||||||
|
g.write8(val)
|
||||||
|
g.println('add8 $reg,$val.hex2()')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) add8_var(reg Register, var_offset int) {
|
||||||
|
g.write8(0x03)
|
||||||
|
match reg {
|
||||||
|
.eax, .rax { g.write8(0x45) }
|
||||||
|
else { verror('add8_var') }
|
||||||
|
}
|
||||||
|
g.write8(0xff - var_offset + 1)
|
||||||
|
g.println('add8 $reg,DWORD PTR[rbp-$var_offset.hex2()]')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) sub8_var(reg Register, var_offset int) {
|
||||||
|
g.write8(0x2b)
|
||||||
|
match reg {
|
||||||
|
.eax, .rax { g.write8(0x45) }
|
||||||
|
else { verror('sub8_var') }
|
||||||
|
}
|
||||||
|
g.write8(0xff - var_offset + 1)
|
||||||
|
g.println('sub8 $reg,DWORD PTR[rbp-$var_offset.hex2()]')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) mul8_var(reg Register, var_offset int) {
|
||||||
|
g.write8(0x0f)
|
||||||
|
g.write8(0xaf)
|
||||||
|
match reg {
|
||||||
|
.eax, .rax { g.write8(0x45) }
|
||||||
|
else { verror('mul8_var') }
|
||||||
|
}
|
||||||
|
g.write8(0xff - var_offset + 1)
|
||||||
|
g.println('mul8 $reg,DWORD PTR[rbp-$var_offset.hex2()]')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) leave() {
|
||||||
|
g.write8(0xc9)
|
||||||
|
g.println('leave')
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns label's relative address
|
||||||
|
pub fn (mut g Gen) gen_loop_start(from int) int {
|
||||||
|
g.mov(.r12, from)
|
||||||
|
label := g.buf.len
|
||||||
|
g.inc(.r12)
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) gen_loop_end(to int, label int) {
|
||||||
|
g.cmp(.r12, ._8, to)
|
||||||
|
g.jl(label)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) save_main_fn_addr() {
|
||||||
|
g.main_fn_addr = i64(g.buf.len)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) gen_print(s string) {
|
||||||
|
//
|
||||||
|
// qq := s + '\n'
|
||||||
|
//
|
||||||
|
g.strings << s
|
||||||
|
// g.string_addr[s] = str_pos
|
||||||
|
g.mov(.eax, 1)
|
||||||
|
g.mov(.edi, 1)
|
||||||
|
str_pos := g.buf.len + 2
|
||||||
|
g.str_pos << str_pos
|
||||||
|
g.mov64(.rsi, 0) // segment_start + 0x9f) // str pos // placeholder
|
||||||
|
g.mov(.edx, s.len) // len
|
||||||
|
g.syscall()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) gen_exit() {
|
||||||
|
// Return 0
|
||||||
|
g.mov(.edi, 0) // ret value
|
||||||
|
g.mov(.eax, 60)
|
||||||
|
g.syscall()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) mov(reg Register, val int) {
|
||||||
|
if val == 0 {
|
||||||
|
// Optimise to xor reg, reg when val is 0
|
||||||
|
match reg {
|
||||||
|
.eax, .rax {
|
||||||
|
g.write8(0x31)
|
||||||
|
g.write8(0xc0)
|
||||||
|
}
|
||||||
|
.edi, .rdi {
|
||||||
|
g.write8(0x31)
|
||||||
|
g.write8(0xff)
|
||||||
|
}
|
||||||
|
.edx {
|
||||||
|
g.write8(0x31)
|
||||||
|
g.write8(0xd2)
|
||||||
|
}
|
||||||
|
.rsi {
|
||||||
|
g.write8(0x48)
|
||||||
|
g.write8(0x31)
|
||||||
|
g.write8(0xf6)
|
||||||
|
}
|
||||||
|
.r12 {
|
||||||
|
g.write8(0x4d)
|
||||||
|
g.write8(0x31)
|
||||||
|
g.write8(0xe4)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic('unhandled mov $reg')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.println('xor $reg, $reg')
|
||||||
|
} else {
|
||||||
|
match reg {
|
||||||
|
.eax, .rax {
|
||||||
|
g.write8(0xb8)
|
||||||
|
}
|
||||||
|
.edi, .rdi {
|
||||||
|
g.write8(0xbf)
|
||||||
|
}
|
||||||
|
.edx {
|
||||||
|
g.write8(0xba)
|
||||||
|
}
|
||||||
|
.rsi {
|
||||||
|
g.write8(0x48)
|
||||||
|
g.write8(0xbe)
|
||||||
|
}
|
||||||
|
.r12 {
|
||||||
|
g.write8(0x41)
|
||||||
|
g.write8(0xbc) // r11 is 0xbb etc
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
panic('unhandled mov $reg')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.write32(val)
|
||||||
|
g.println('mov $reg, $val')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) mov_reg(a Register, b Register) {
|
||||||
|
match a {
|
||||||
|
.rbp {
|
||||||
|
g.write8(0x48)
|
||||||
|
g.write8(0x89)
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generates `mov rbp, rsp`
|
||||||
|
fn (mut g Gen) mov_rbp_rsp() {
|
||||||
|
g.write8(0x48)
|
||||||
|
g.write8(0x89)
|
||||||
|
g.write8(0xe5)
|
||||||
|
g.println('mov rbp,rsp')
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut g Gen) call_fn(node ast.CallExpr) {
|
||||||
|
name := node.name
|
||||||
|
// println('call fn $name')
|
||||||
|
addr := g.fn_addr[name]
|
||||||
|
if addr == 0 {
|
||||||
|
verror('fn addr of `$name` = 0')
|
||||||
|
}
|
||||||
|
// Copy values to registers (calling convention)
|
||||||
|
// g.mov(.eax, 0)
|
||||||
|
for i in 0 .. node.args.len {
|
||||||
|
expr := node.args[i].expr
|
||||||
|
match expr {
|
||||||
|
ast.IntegerLiteral {
|
||||||
|
// `foo(2)` => `mov edi,0x2`
|
||||||
|
g.mov(x64.fn_arg_registers[i], expr.val.int())
|
||||||
|
}
|
||||||
|
ast.Ident {
|
||||||
|
// `foo(x)` => `mov edi,DWORD PTR [rbp-0x8]`
|
||||||
|
var_offset := g.get_var_offset(expr.name)
|
||||||
|
if g.pref.is_verbose {
|
||||||
|
println('i=$i fn name= $name offset=$var_offset')
|
||||||
|
println(int(x64.fn_arg_registers[i]))
|
||||||
|
}
|
||||||
|
g.mov_var_to_reg(x64.fn_arg_registers[i], var_offset)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
verror('unhandled call_fn (name=$name) node: ' + expr.type_name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.args.len > 6 {
|
||||||
|
verror('more than 6 args not allowed for now')
|
||||||
|
}
|
||||||
|
g.call(int(addr))
|
||||||
|
g.println('fn call `${name}()`')
|
||||||
|
// println('call $name $addr')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) assign_stmt(node ast.AssignStmt) {
|
||||||
|
// `a := 1` | `a,b := 1,2`
|
||||||
|
for i, left in node.left {
|
||||||
|
right := node.right[i]
|
||||||
|
name := left.str()
|
||||||
|
// if left is ast.Ident {
|
||||||
|
// ident := left as ast.Ident
|
||||||
|
match right {
|
||||||
|
ast.IntegerLiteral {
|
||||||
|
g.allocate_var(name, 4, right.val.int())
|
||||||
|
}
|
||||||
|
ast.InfixExpr {
|
||||||
|
g.infix_expr(right)
|
||||||
|
g.allocate_var(name, 4, 0)
|
||||||
|
// `mov DWORD PTR [rbp-0x8],eax`
|
||||||
|
offset := g.get_var_offset(name)
|
||||||
|
println('infix assignment $name offset=$offset.hex2()')
|
||||||
|
g.mov_reg_to_rbp(offset, .eax)
|
||||||
|
}
|
||||||
|
ast.StructInit {
|
||||||
|
sym := g.table.get_type_symbol(right.typ)
|
||||||
|
// println(sym)
|
||||||
|
// println(typeof(sym.info))
|
||||||
|
info := sym.info as ast.Struct
|
||||||
|
for field in info.fields {
|
||||||
|
field_name := name + '.' + field.name
|
||||||
|
println(field_name)
|
||||||
|
g.allocate_var(field_name, 4, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
g.error_with_pos('x64 assign_stmt unhandled expr: ' + right.type_name(),
|
||||||
|
right.position())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
||||||
|
println('infix expr op=$node.op')
|
||||||
|
if node.left is ast.InfixExpr {
|
||||||
|
verror('only simple expressions are supported right now (not more than 2 operands)')
|
||||||
|
}
|
||||||
|
match mut node.left {
|
||||||
|
ast.Ident { g.mov_var_to_reg(.eax, g.get_var_offset(node.left.name)) }
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
if mut 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) }
|
||||||
|
.minus { g.sub8_var(.eax, var_offset) }
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) if_expr(node ast.IfExpr) {
|
||||||
|
branch := node.branches[0]
|
||||||
|
infix_expr := branch.cond as ast.InfixExpr
|
||||||
|
mut jne_addr := 0 // location of `jne *00 00 00 00*`
|
||||||
|
match mut infix_expr.left {
|
||||||
|
ast.Ident {
|
||||||
|
lit := infix_expr.right as ast.IntegerLiteral
|
||||||
|
g.cmp_var(infix_expr.left.name, lit.val.int())
|
||||||
|
jne_addr = g.jne()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
verror('unhandled infix.left')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.stmts(branch.stmts)
|
||||||
|
// Now that we know where we need to jump if the condition is false, update the `jne` call.
|
||||||
|
// The value is the relative address, difference between current position and the location
|
||||||
|
// after `jne 00 00 00 00`
|
||||||
|
// println('after if g.pos=$g.pos() jneaddr=$jne_addr')
|
||||||
|
g.write32_at(jne_addr, int(g.pos() - jne_addr - 4)) // 4 is for "00 00 00 00"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) for_stmt(node ast.ForStmt) {
|
||||||
|
infix_expr := node.cond as ast.InfixExpr
|
||||||
|
// g.mov(.eax, 0x77777777)
|
||||||
|
mut jump_addr := 0 // location of `jne *00 00 00 00*`
|
||||||
|
start := g.pos()
|
||||||
|
match mut infix_expr.left {
|
||||||
|
ast.Ident {
|
||||||
|
lit := infix_expr.right as ast.IntegerLiteral
|
||||||
|
g.cmp_var(infix_expr.left.name, lit.val.int())
|
||||||
|
jump_addr = g.jge()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
verror('unhandled infix.left')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.stmts(node.stmts)
|
||||||
|
// Go back to `cmp ...`
|
||||||
|
// Diff between `jmp 00 00 00 00 X` and `cmp`
|
||||||
|
g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1))
|
||||||
|
// Update the jump addr to current pos
|
||||||
|
g.write32_at(jump_addr, int(g.pos() - jump_addr - 4)) // 4 is for "00 00 00 00"
|
||||||
|
g.println('jpm after for')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) fn_decl(node ast.FnDecl) {
|
||||||
|
if g.pref.is_verbose {
|
||||||
|
println(term.green('\n$node.name:'))
|
||||||
|
}
|
||||||
|
g.stack_var_pos = 0
|
||||||
|
is_main := node.name == 'main.main'
|
||||||
|
// println('saving addr $node.name $g.buf.len.hex2()')
|
||||||
|
if is_main {
|
||||||
|
g.save_main_fn_addr()
|
||||||
|
} else {
|
||||||
|
g.register_function_address(node.name)
|
||||||
|
}
|
||||||
|
g.push(.rbp)
|
||||||
|
g.mov_rbp_rsp()
|
||||||
|
// if !is_main {
|
||||||
|
g.sub8(.rsp, 0x10)
|
||||||
|
// }
|
||||||
|
if node.params.len > 0 {
|
||||||
|
// g.mov(.r12, 0x77777777)
|
||||||
|
}
|
||||||
|
// Copy values from registers to local vars (calling convention)
|
||||||
|
mut offset := 0
|
||||||
|
for i in 0 .. node.params.len {
|
||||||
|
name := node.params[i].name
|
||||||
|
// TODO optimize. Right now 2 mov's are used instead of 1.
|
||||||
|
g.allocate_var(name, 4, 0)
|
||||||
|
// `mov DWORD PTR [rbp-0x4],edi`
|
||||||
|
offset += 4
|
||||||
|
g.mov_reg_to_rbp(offset, x64.fn_arg_registers[i])
|
||||||
|
}
|
||||||
|
//
|
||||||
|
g.stmts(node.stmts)
|
||||||
|
if is_main {
|
||||||
|
// println('end of main: gen exit')
|
||||||
|
g.gen_exit()
|
||||||
|
// return
|
||||||
|
}
|
||||||
|
if !is_main {
|
||||||
|
// g.leave()
|
||||||
|
g.add8(.rsp, 0x10)
|
||||||
|
g.pop(.rbp)
|
||||||
|
}
|
||||||
|
g.ret()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn (mut x Amd64) allocate_var(name string, size int, initial_val int) {
|
||||||
|
mut g := x.g
|
||||||
// `a := 3` =>
|
// `a := 3` =>
|
||||||
// `move DWORD [rbp-0x4],0x3`
|
// `move DWORD [rbp-0x4],0x3`
|
||||||
match size {
|
match size {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
module x64
|
module x64
|
||||||
|
|
||||||
pub struct Aarch64 {
|
pub struct Aarch64 {
|
||||||
|
mut:
|
||||||
// arm64 specific stuff for code generation
|
// arm64 specific stuff for code generation
|
||||||
|
g Gen
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut x Aarch64) allocate_var(mut g Gen, name string, size int, initial_val int) {
|
pub fn (mut x Aarch64) allocate_var(name string, size int, initial_val int) {
|
||||||
eprintln('TODO: allocating var on arm64 ($name) = $size = $initial_val')
|
eprintln('TODO: allocating var on arm64 ($name) = $size = $initial_val')
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,10 @@ import v.token
|
||||||
import v.errors
|
import v.errors
|
||||||
import v.pref
|
import v.pref
|
||||||
import term
|
import term
|
||||||
import strings
|
|
||||||
|
|
||||||
interface CodeGen {
|
interface CodeGen {
|
||||||
allocate_var(g &Gen, name string, size int, initial_val int)
|
g Gen
|
||||||
|
allocate_var(name string, size int, initial_val int)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Gen {
|
pub struct Gen {
|
||||||
|
@ -40,46 +40,6 @@ mut:
|
||||||
nlines int
|
nlines int
|
||||||
}
|
}
|
||||||
|
|
||||||
// string_addr map[string]i64
|
|
||||||
// The registers are ordered for faster generation
|
|
||||||
// push rax => 50
|
|
||||||
// push rcx => 51 etc
|
|
||||||
enum Register {
|
|
||||||
rax
|
|
||||||
rcx
|
|
||||||
rdx
|
|
||||||
rbx
|
|
||||||
rsp
|
|
||||||
rbp
|
|
||||||
rsi
|
|
||||||
rdi
|
|
||||||
eax
|
|
||||||
edi
|
|
||||||
edx
|
|
||||||
r8
|
|
||||||
r9
|
|
||||||
r10
|
|
||||||
r11
|
|
||||||
r12
|
|
||||||
r13
|
|
||||||
r14
|
|
||||||
r15
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
fn_arg_registers = [Register.rdi, .rsi, .rdx, .rcx, .r8, .r9]
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
rax // 0
|
|
||||||
rcx // 1
|
|
||||||
rdx // 2
|
|
||||||
rbx // 3
|
|
||||||
rsp // 4
|
|
||||||
rbp // 5
|
|
||||||
rsi // 6
|
|
||||||
rdi // 7
|
|
||||||
*/
|
|
||||||
enum Size {
|
enum Size {
|
||||||
_8
|
_8
|
||||||
_16
|
_16
|
||||||
|
@ -87,7 +47,7 @@ enum Size {
|
||||||
_64
|
_64
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_backend(pref &pref.Preferences) CodeGen {
|
fn (g Gen) get_backend(pref &pref.Preferences) CodeGen {
|
||||||
if pref.arch == .aarch64 {
|
if pref.arch == .aarch64 {
|
||||||
return Aarch64{}
|
return Aarch64{}
|
||||||
}
|
}
|
||||||
|
@ -100,8 +60,9 @@ pub fn gen(files []ast.File, table &ast.Table, out_name string, pref &pref.Prefe
|
||||||
sect_header_name_pos: 0
|
sect_header_name_pos: 0
|
||||||
out_name: out_name
|
out_name: out_name
|
||||||
pref: pref
|
pref: pref
|
||||||
cgen: get_backend(pref)
|
cgen: &Amd64{}
|
||||||
}
|
}
|
||||||
|
g.cgen = g.get_backend(pref)
|
||||||
g.generate_header()
|
g.generate_header()
|
||||||
for file in files {
|
for file in files {
|
||||||
if file.warnings.len > 0 {
|
if file.warnings.len > 0 {
|
||||||
|
@ -232,30 +193,6 @@ fn (mut g Gen) write_string_with_padding(s string, max int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) inc(reg Register) {
|
|
||||||
g.write16(0xff49)
|
|
||||||
match reg {
|
|
||||||
.r12 { g.write8(0xc4) }
|
|
||||||
else { panic('unhandled inc $reg') }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) cmp(reg Register, size Size, val i64) {
|
|
||||||
g.write8(0x49)
|
|
||||||
// Second byte depends on the size of the value
|
|
||||||
match size {
|
|
||||||
._8 { g.write8(0x83) }
|
|
||||||
._32 { g.write8(0x81) }
|
|
||||||
else { panic('unhandled cmp') }
|
|
||||||
}
|
|
||||||
// Third byte depends on the register being compared to
|
|
||||||
match reg {
|
|
||||||
.r12 { g.write8(0xfc) }
|
|
||||||
else { panic('unhandled cmp') }
|
|
||||||
}
|
|
||||||
g.write8(int(val))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) get_var_offset(var_name string) int {
|
fn (mut g Gen) get_var_offset(var_name string) int {
|
||||||
offset := g.var_offset[var_name]
|
offset := g.var_offset[var_name]
|
||||||
if offset == 0 {
|
if offset == 0 {
|
||||||
|
@ -264,278 +201,6 @@ fn (mut g Gen) get_var_offset(var_name string) int {
|
||||||
return offset
|
return offset
|
||||||
}
|
}
|
||||||
|
|
||||||
// `a == 1`
|
|
||||||
// `cmp DWORD [rbp-0x4],0x1`
|
|
||||||
fn (mut g Gen) cmp_var(var_name string, val int) {
|
|
||||||
g.write8(0x81) // 83 for 1 byte?
|
|
||||||
g.write8(0x7d)
|
|
||||||
offset := g.get_var_offset(var_name)
|
|
||||||
g.write8(0xff - offset + 1)
|
|
||||||
g.write32(val)
|
|
||||||
g.println('cmp var `$var_name` $val')
|
|
||||||
}
|
|
||||||
|
|
||||||
// `add DWORD [rbp-0x4], 1`
|
|
||||||
fn (mut g Gen) inc_var(var_name string) {
|
|
||||||
g.write16(0x4581) // 83 for 1 byte
|
|
||||||
offset := g.get_var_offset(var_name)
|
|
||||||
g.write8(0xff - offset + 1)
|
|
||||||
g.write32(1)
|
|
||||||
g.println('inc_var `$var_name`')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the position of the address to jump to (set later).
|
|
||||||
fn (mut g Gen) jne() int {
|
|
||||||
g.write16(0x850f)
|
|
||||||
pos := g.pos()
|
|
||||||
g.write32(placeholder)
|
|
||||||
g.println('jne')
|
|
||||||
return int(pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) jge() int {
|
|
||||||
g.write16(0x8d0f)
|
|
||||||
pos := g.pos()
|
|
||||||
g.write32(placeholder)
|
|
||||||
g.println('jne')
|
|
||||||
return int(pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) jmp(addr int) {
|
|
||||||
g.write8(0xe9)
|
|
||||||
g.write32(addr) // 0xffffff
|
|
||||||
g.println('jmp')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn abs(a i64) i64 {
|
|
||||||
return if a < 0 { -a } else { a }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) jle(addr i64) {
|
|
||||||
// Calculate the relative offset to jump to
|
|
||||||
// (`addr` is absolute address)
|
|
||||||
offset := 0xff - int(abs(addr - g.buf.len)) - 1
|
|
||||||
g.write8(0x7e)
|
|
||||||
g.write8(offset)
|
|
||||||
g.println('jle')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) println(comment string) {
|
|
||||||
g.nlines++
|
|
||||||
if !g.pref.is_verbose {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
addr := g.debug_pos.hex()
|
|
||||||
// println('$g.debug_pos "$addr"')
|
|
||||||
print(term.red(strings.repeat(`0`, 6 - addr.len) + addr + ' '))
|
|
||||||
for i := g.debug_pos; i < g.buf.len; i++ {
|
|
||||||
s := g.buf[i].hex()
|
|
||||||
if s.len == 1 {
|
|
||||||
print(term.blue('0'))
|
|
||||||
}
|
|
||||||
gbihex := g.buf[i].hex()
|
|
||||||
hexstr := term.blue(gbihex) + ' '
|
|
||||||
print(hexstr)
|
|
||||||
}
|
|
||||||
g.debug_pos = g.buf.len
|
|
||||||
print(' ' + comment)
|
|
||||||
println('')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) jl(addr i64) {
|
|
||||||
offset := 0xff - int(abs(addr - g.buf.len)) - 1
|
|
||||||
g.write8(0x7c)
|
|
||||||
g.write8(offset)
|
|
||||||
g.println('jl')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (g &Gen) abs_to_rel_addr(addr i64) int {
|
|
||||||
return int(abs(addr - g.buf.len)) - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
fn (mut g Gen) jmp(addr i64) {
|
|
||||||
offset := 0xff - g.abs_to_rel_addr(addr)
|
|
||||||
g.write8(0xe9)
|
|
||||||
g.write8(offset)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
fn (mut g Gen) mov64(reg Register, val i64) {
|
|
||||||
match reg {
|
|
||||||
.rsi {
|
|
||||||
g.write8(0x48)
|
|
||||||
g.write8(0xbe)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
println('unhandled mov $reg')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.write64(val)
|
|
||||||
g.println('mov64 $reg, $val')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) mov_reg_to_rbp(var_offset int, reg Register) {
|
|
||||||
// 89 7d fc mov DWORD PTR [rbp-0x4],edi
|
|
||||||
g.write8(0x89)
|
|
||||||
match reg {
|
|
||||||
.eax, .rax { g.write8(0x45) }
|
|
||||||
.edi, .rdi { g.write8(0x7d) }
|
|
||||||
.rsi { g.write8(0x75) }
|
|
||||||
.rdx { g.write8(0x55) }
|
|
||||||
.rcx { g.write8(0x4d) }
|
|
||||||
else { verror('mov_from_reg $reg') }
|
|
||||||
}
|
|
||||||
g.write8(0xff - var_offset + 1)
|
|
||||||
g.println('mov DWORD PTR[rbp-$var_offset.hex2()],$reg')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) mov_var_to_reg(reg Register, var_offset int) {
|
|
||||||
// 8b 7d f8 mov edi,DWORD PTR [rbp-0x8]
|
|
||||||
g.write8(0x8b)
|
|
||||||
match reg {
|
|
||||||
.eax, .rax { g.write8(0x45) }
|
|
||||||
.edi, .rdi { g.write8(0x7d) }
|
|
||||||
.rsi { g.write8(0x75) }
|
|
||||||
.rdx { g.write8(0x55) }
|
|
||||||
.rcx { g.write8(0x4d) }
|
|
||||||
else { verror('mov_var_to_reg $reg') }
|
|
||||||
}
|
|
||||||
g.write8(0xff - var_offset + 1)
|
|
||||||
g.println('mov $reg,DWORD PTR[rbp-$var_offset.hex2()]')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) call(addr int) {
|
|
||||||
// Need to calculate the difference between current position (position after the e8 call)
|
|
||||||
// and the function to call.
|
|
||||||
// +5 is to get the posistion "e8 xx xx xx xx"
|
|
||||||
// Not sure about the -1.
|
|
||||||
rel := 0xffffffff - (g.buf.len + 5 - addr - 1)
|
|
||||||
// println('call addr=$addr.hex2() rel_addr=$rel.hex2() pos=$g.buf.len')
|
|
||||||
g.write8(0xe8)
|
|
||||||
g.write32(rel)
|
|
||||||
// g.println('fn call')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) syscall() {
|
|
||||||
// g.write(0x050f)
|
|
||||||
g.write8(0x0f)
|
|
||||||
g.write8(0x05)
|
|
||||||
g.println('syscall')
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut g Gen) ret() {
|
|
||||||
g.write8(0xc3)
|
|
||||||
g.println('ret')
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut g Gen) push(reg Register) {
|
|
||||||
if int(reg) < int(Register.r8) {
|
|
||||||
g.write8(0x50 + int(reg))
|
|
||||||
} else {
|
|
||||||
g.write8(0x41)
|
|
||||||
g.write8(0x50 + int(reg) - 8)
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
match reg {
|
|
||||||
.rbp { g.write8(0x55) }
|
|
||||||
else {}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
g.println('push $reg')
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut g Gen) pop(reg Register) {
|
|
||||||
g.write8(0x58 + int(reg))
|
|
||||||
// TODO r8...
|
|
||||||
g.println('pop $reg')
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut g Gen) sub32(reg Register, val int) {
|
|
||||||
g.write8(0x48)
|
|
||||||
g.write8(0x81)
|
|
||||||
g.write8(0xe8 + int(reg)) // TODO rax is different?
|
|
||||||
g.write32(val)
|
|
||||||
g.println('sub32 $reg,$val.hex2()')
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut g Gen) sub8(reg Register, val int) {
|
|
||||||
g.write8(0x48)
|
|
||||||
g.write8(0x83)
|
|
||||||
g.write8(0xe8 + int(reg)) // TODO rax is different?
|
|
||||||
g.write8(val)
|
|
||||||
g.println('sub8 $reg,$val.hex2()')
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut g Gen) add(reg Register, val int) {
|
|
||||||
g.write8(0x48)
|
|
||||||
g.write8(0x81)
|
|
||||||
g.write8(0xe8 + int(reg)) // TODO rax is different?
|
|
||||||
g.write32(val)
|
|
||||||
g.println('add $reg,$val.hex2()')
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut g Gen) add8(reg Register, val int) {
|
|
||||||
g.write8(0x48)
|
|
||||||
g.write8(0x83)
|
|
||||||
// g.write8(0xe8 + reg) // TODO rax is different?
|
|
||||||
g.write8(0xc4)
|
|
||||||
g.write8(val)
|
|
||||||
g.println('add8 $reg,$val.hex2()')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) add8_var(reg Register, var_offset int) {
|
|
||||||
g.write8(0x03)
|
|
||||||
match reg {
|
|
||||||
.eax, .rax { g.write8(0x45) }
|
|
||||||
else { verror('add8_var') }
|
|
||||||
}
|
|
||||||
g.write8(0xff - var_offset + 1)
|
|
||||||
g.println('add8 $reg,DWORD PTR[rbp-$var_offset.hex2()]')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) sub8_var(reg Register, var_offset int) {
|
|
||||||
g.write8(0x2b)
|
|
||||||
match reg {
|
|
||||||
.eax, .rax { g.write8(0x45) }
|
|
||||||
else { verror('sub8_var') }
|
|
||||||
}
|
|
||||||
g.write8(0xff - var_offset + 1)
|
|
||||||
g.println('sub8 $reg,DWORD PTR[rbp-$var_offset.hex2()]')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) mul8_var(reg Register, var_offset int) {
|
|
||||||
g.write8(0x0f)
|
|
||||||
g.write8(0xaf)
|
|
||||||
match reg {
|
|
||||||
.eax, .rax { g.write8(0x45) }
|
|
||||||
else { verror('mul8_var') }
|
|
||||||
}
|
|
||||||
g.write8(0xff - var_offset + 1)
|
|
||||||
g.println('mul8 $reg,DWORD PTR[rbp-$var_offset.hex2()]')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) leave() {
|
|
||||||
g.write8(0xc9)
|
|
||||||
g.println('leave')
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns label's relative address
|
|
||||||
pub fn (mut g Gen) gen_loop_start(from int) int {
|
|
||||||
g.mov(.r12, from)
|
|
||||||
label := g.buf.len
|
|
||||||
g.inc(.r12)
|
|
||||||
return label
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut g Gen) gen_loop_end(to int, label int) {
|
|
||||||
g.cmp(.r12, ._8, to)
|
|
||||||
g.jl(label)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut g Gen) save_main_fn_addr() {
|
|
||||||
g.main_fn_addr = i64(g.buf.len)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut g Gen) gen_print_from_expr(expr ast.Expr, newline bool) {
|
pub fn (mut g Gen) gen_print_from_expr(expr ast.Expr, newline bool) {
|
||||||
match expr {
|
match expr {
|
||||||
ast.StringLiteral {
|
ast.StringLiteral {
|
||||||
|
@ -549,149 +214,12 @@ pub fn (mut g Gen) gen_print_from_expr(expr ast.Expr, newline bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut g Gen) gen_print(s string) {
|
|
||||||
//
|
|
||||||
// qq := s + '\n'
|
|
||||||
//
|
|
||||||
g.strings << s
|
|
||||||
// g.string_addr[s] = str_pos
|
|
||||||
g.mov(.eax, 1)
|
|
||||||
g.mov(.edi, 1)
|
|
||||||
str_pos := g.buf.len + 2
|
|
||||||
g.str_pos << str_pos
|
|
||||||
g.mov64(.rsi, 0) // segment_start + 0x9f) // str pos // placeholder
|
|
||||||
g.mov(.edx, s.len) // len
|
|
||||||
g.syscall()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut g Gen) gen_exit() {
|
|
||||||
// Return 0
|
|
||||||
g.mov(.edi, 0) // ret value
|
|
||||||
g.mov(.eax, 60)
|
|
||||||
g.syscall()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) mov(reg Register, val int) {
|
|
||||||
if val == 0 {
|
|
||||||
// Optimise to xor reg, reg when val is 0
|
|
||||||
match reg {
|
|
||||||
.eax, .rax {
|
|
||||||
g.write8(0x31)
|
|
||||||
g.write8(0xc0)
|
|
||||||
}
|
|
||||||
.edi, .rdi {
|
|
||||||
g.write8(0x31)
|
|
||||||
g.write8(0xff)
|
|
||||||
}
|
|
||||||
.edx {
|
|
||||||
g.write8(0x31)
|
|
||||||
g.write8(0xd2)
|
|
||||||
}
|
|
||||||
.rsi {
|
|
||||||
g.write8(0x48)
|
|
||||||
g.write8(0x31)
|
|
||||||
g.write8(0xf6)
|
|
||||||
}
|
|
||||||
.r12 {
|
|
||||||
g.write8(0x4d)
|
|
||||||
g.write8(0x31)
|
|
||||||
g.write8(0xe4)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
panic('unhandled mov $reg')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.println('xor $reg, $reg')
|
|
||||||
} else {
|
|
||||||
match reg {
|
|
||||||
.eax, .rax {
|
|
||||||
g.write8(0xb8)
|
|
||||||
}
|
|
||||||
.edi, .rdi {
|
|
||||||
g.write8(0xbf)
|
|
||||||
}
|
|
||||||
.edx {
|
|
||||||
g.write8(0xba)
|
|
||||||
}
|
|
||||||
.rsi {
|
|
||||||
g.write8(0x48)
|
|
||||||
g.write8(0xbe)
|
|
||||||
}
|
|
||||||
.r12 {
|
|
||||||
g.write8(0x41)
|
|
||||||
g.write8(0xbc) // r11 is 0xbb etc
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
panic('unhandled mov $reg')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.write32(val)
|
|
||||||
g.println('mov $reg, $val')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) mov_reg(a Register, b Register) {
|
|
||||||
match a {
|
|
||||||
.rbp {
|
|
||||||
g.write8(0x48)
|
|
||||||
g.write8(0x89)
|
|
||||||
}
|
|
||||||
else {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// generates `mov rbp, rsp`
|
|
||||||
fn (mut g Gen) mov_rbp_rsp() {
|
|
||||||
g.write8(0x48)
|
|
||||||
g.write8(0x89)
|
|
||||||
g.write8(0xe5)
|
|
||||||
g.println('mov rbp,rsp')
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn (mut g Gen) register_function_address(name string) {
|
pub fn (mut g Gen) register_function_address(name string) {
|
||||||
addr := g.pos()
|
addr := g.pos()
|
||||||
// println('reg fn addr $name $addr')
|
// println('reg fn addr $name $addr')
|
||||||
g.fn_addr[name] = addr
|
g.fn_addr[name] = addr
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut g Gen) call_fn(node ast.CallExpr) {
|
|
||||||
name := node.name
|
|
||||||
// println('call fn $name')
|
|
||||||
addr := g.fn_addr[name]
|
|
||||||
if addr == 0 {
|
|
||||||
verror('fn addr of `$name` = 0')
|
|
||||||
}
|
|
||||||
// Copy values to registers (calling convention)
|
|
||||||
// g.mov(.eax, 0)
|
|
||||||
for i in 0 .. node.args.len {
|
|
||||||
expr := node.args[i].expr
|
|
||||||
match expr {
|
|
||||||
ast.IntegerLiteral {
|
|
||||||
// `foo(2)` => `mov edi,0x2`
|
|
||||||
g.mov(x64.fn_arg_registers[i], expr.val.int())
|
|
||||||
}
|
|
||||||
ast.Ident {
|
|
||||||
// `foo(x)` => `mov edi,DWORD PTR [rbp-0x8]`
|
|
||||||
var_offset := g.get_var_offset(expr.name)
|
|
||||||
if g.pref.is_verbose {
|
|
||||||
println('i=$i fn name= $name offset=$var_offset')
|
|
||||||
println(int(x64.fn_arg_registers[i]))
|
|
||||||
}
|
|
||||||
g.mov_var_to_reg(x64.fn_arg_registers[i], var_offset)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
verror('unhandled call_fn (name=$name) node: ' + expr.type_name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if node.args.len > 6 {
|
|
||||||
verror('more than 6 args not allowed for now')
|
|
||||||
}
|
|
||||||
g.call(int(addr))
|
|
||||||
g.println('fn call `${name}()`')
|
|
||||||
// println('call $name $addr')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) for_in_stmt(node ast.ForInStmt) {
|
fn (mut g Gen) for_in_stmt(node ast.ForInStmt) {
|
||||||
eprintln('for-in statement is not yet implemented')
|
eprintln('for-in statement is not yet implemented')
|
||||||
/*
|
/*
|
||||||
|
@ -791,157 +319,7 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) allocate_var(name string, size int, initial_val int) {
|
fn (mut g Gen) allocate_var(name string, size int, initial_val int) {
|
||||||
g.cgen.allocate_var(g, name, size, initial_val)
|
g.cgen.allocate_var(name, size, initial_val)
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) assign_stmt(node ast.AssignStmt) {
|
|
||||||
// `a := 1` | `a,b := 1,2`
|
|
||||||
for i, left in node.left {
|
|
||||||
right := node.right[i]
|
|
||||||
name := left.str()
|
|
||||||
// if left is ast.Ident {
|
|
||||||
// ident := left as ast.Ident
|
|
||||||
match right {
|
|
||||||
ast.IntegerLiteral {
|
|
||||||
g.allocate_var(name, 4, right.val.int())
|
|
||||||
}
|
|
||||||
ast.InfixExpr {
|
|
||||||
g.infix_expr(right)
|
|
||||||
g.allocate_var(name, 4, 0)
|
|
||||||
// `mov DWORD PTR [rbp-0x8],eax`
|
|
||||||
offset := g.get_var_offset(name)
|
|
||||||
println('infix assignment $name offset=$offset.hex2()')
|
|
||||||
g.mov_reg_to_rbp(offset, .eax)
|
|
||||||
}
|
|
||||||
ast.StructInit {
|
|
||||||
sym := g.table.get_type_symbol(right.typ)
|
|
||||||
// println(sym)
|
|
||||||
// println(typeof(sym.info))
|
|
||||||
info := sym.info as ast.Struct
|
|
||||||
for field in info.fields {
|
|
||||||
field_name := name + '.' + field.name
|
|
||||||
println(field_name)
|
|
||||||
g.allocate_var(field_name, 4, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
g.error_with_pos('x64 assign_stmt unhandled expr: ' + right.type_name(),
|
|
||||||
right.position())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
|
||||||
println('infix expr op=$node.op')
|
|
||||||
if node.left is ast.InfixExpr {
|
|
||||||
verror('only simple expressions are supported right now (not more than 2 operands)')
|
|
||||||
}
|
|
||||||
match mut node.left {
|
|
||||||
ast.Ident { g.mov_var_to_reg(.eax, g.get_var_offset(node.left.name)) }
|
|
||||||
else {}
|
|
||||||
}
|
|
||||||
if mut 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) }
|
|
||||||
.minus { g.sub8_var(.eax, var_offset) }
|
|
||||||
else {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) if_expr(node ast.IfExpr) {
|
|
||||||
branch := node.branches[0]
|
|
||||||
infix_expr := branch.cond as ast.InfixExpr
|
|
||||||
mut jne_addr := 0 // location of `jne *00 00 00 00*`
|
|
||||||
match mut infix_expr.left {
|
|
||||||
ast.Ident {
|
|
||||||
lit := infix_expr.right as ast.IntegerLiteral
|
|
||||||
g.cmp_var(infix_expr.left.name, lit.val.int())
|
|
||||||
jne_addr = g.jne()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
verror('unhandled infix.left')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.stmts(branch.stmts)
|
|
||||||
// Now that we know where we need to jump if the condition is false, update the `jne` call.
|
|
||||||
// The value is the relative address, difference between current position and the location
|
|
||||||
// after `jne 00 00 00 00`
|
|
||||||
// println('after if g.pos=$g.pos() jneaddr=$jne_addr')
|
|
||||||
g.write32_at(jne_addr, int(g.pos() - jne_addr - 4)) // 4 is for "00 00 00 00"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) for_stmt(node ast.ForStmt) {
|
|
||||||
infix_expr := node.cond as ast.InfixExpr
|
|
||||||
// g.mov(.eax, 0x77777777)
|
|
||||||
mut jump_addr := 0 // location of `jne *00 00 00 00*`
|
|
||||||
start := g.pos()
|
|
||||||
match mut infix_expr.left {
|
|
||||||
ast.Ident {
|
|
||||||
lit := infix_expr.right as ast.IntegerLiteral
|
|
||||||
g.cmp_var(infix_expr.left.name, lit.val.int())
|
|
||||||
jump_addr = g.jge()
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
verror('unhandled infix.left')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g.stmts(node.stmts)
|
|
||||||
// Go back to `cmp ...`
|
|
||||||
// Diff between `jmp 00 00 00 00 X` and `cmp`
|
|
||||||
g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1))
|
|
||||||
// Update the jump addr to current pos
|
|
||||||
g.write32_at(jump_addr, int(g.pos() - jump_addr - 4)) // 4 is for "00 00 00 00"
|
|
||||||
g.println('jpm after for')
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut g Gen) fn_decl(node ast.FnDecl) {
|
|
||||||
if g.pref.is_verbose {
|
|
||||||
println(term.green('\n$node.name:'))
|
|
||||||
}
|
|
||||||
g.stack_var_pos = 0
|
|
||||||
is_main := node.name == 'main.main'
|
|
||||||
// println('saving addr $node.name $g.buf.len.hex2()')
|
|
||||||
if is_main {
|
|
||||||
g.save_main_fn_addr()
|
|
||||||
} else {
|
|
||||||
g.register_function_address(node.name)
|
|
||||||
}
|
|
||||||
g.push(.rbp)
|
|
||||||
g.mov_rbp_rsp()
|
|
||||||
// if !is_main {
|
|
||||||
g.sub8(.rsp, 0x10)
|
|
||||||
// }
|
|
||||||
if node.params.len > 0 {
|
|
||||||
// g.mov(.r12, 0x77777777)
|
|
||||||
}
|
|
||||||
// Copy values from registers to local vars (calling convention)
|
|
||||||
mut offset := 0
|
|
||||||
for i in 0 .. node.params.len {
|
|
||||||
name := node.params[i].name
|
|
||||||
// TODO optimize. Right now 2 mov's are used instead of 1.
|
|
||||||
g.allocate_var(name, 4, 0)
|
|
||||||
// `mov DWORD PTR [rbp-0x4],edi`
|
|
||||||
offset += 4
|
|
||||||
g.mov_reg_to_rbp(offset, x64.fn_arg_registers[i])
|
|
||||||
}
|
|
||||||
//
|
|
||||||
g.stmts(node.stmts)
|
|
||||||
if is_main {
|
|
||||||
// println('end of main: gen exit')
|
|
||||||
g.gen_exit()
|
|
||||||
// return
|
|
||||||
}
|
|
||||||
if !is_main {
|
|
||||||
// g.leave()
|
|
||||||
g.add8(.rsp, 0x10)
|
|
||||||
g.pop(.rbp)
|
|
||||||
}
|
|
||||||
g.ret()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g Gen) postfix_expr(node ast.PostfixExpr) {
|
fn (mut g Gen) postfix_expr(node ast.PostfixExpr) {
|
||||||
|
|
Loading…
Reference in New Issue