v/vlib/v/gen/native/gen.v

377 lines
7.5 KiB
V

// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
module native
import os
import v.ast
import v.util
import v.token
import v.errors
import v.pref
import term
interface CodeGen {
g Gen
allocate_var(name string, size int, initial_val int)
}
pub struct Gen {
out_name string
pref &pref.Preferences // Preferences shared from V struct
mut:
cgen CodeGen
table &ast.Table
buf []byte
sect_header_name_pos int
offset i64
str_pos []i64
strings []string // TODO use a map and don't duplicate strings
file_size_pos i64
main_fn_addr i64
code_start_pos i64 // location of the start of the assembly instructions
fn_addr map[string]i64
var_offset map[string]int // local var stack offset
stack_var_pos int
debug_pos int
errors []errors.Error
warnings []errors.Warning
syms []Symbol
relocs []Reloc
nlines int
}
enum Size {
_8
_16
_32
_64
}
fn (g Gen) get_backend(pref &pref.Preferences) CodeGen {
if pref.arch == .arm64 {
return Arm64{}
}
return Amd64{}
}
pub fn gen(files []ast.File, table &ast.Table, out_name string, pref &pref.Preferences) (int, int) {
mut g := Gen{
table: table
sect_header_name_pos: 0
out_name: out_name
pref: pref
cgen: &Amd64{}
}
g.cgen = g.get_backend(pref)
g.generate_header()
for file in files {
if file.warnings.len > 0 {
eprintln('Warning: ${file.warnings[0]}')
}
if file.errors.len > 0 {
eprintln('Error ${file.errors[0]}')
// verror('Error ${file.errors[0]}')
}
g.stmts(file.stmts)
}
g.generate_footer()
return g.nlines, g.buf.len
}
pub fn (mut g Gen) generate_header() {
match g.pref.os {
.macos {
g.generate_macho_header()
}
.linux {
g.generate_elf_header()
}
.raw {
if g.pref.arch == .arm64 {
g.gen_arm64_helloworld()
}
}
else {
verror('Error: only `raw`, `linux` and `macos` are supported for -os in -native')
}
}
}
pub fn (mut g Gen) create_executable() {
// Create the binary // should be .o ?
mut f := os.create(g.out_name) or { panic(err) }
os.chmod(g.out_name, 0o775) // make it executable
unsafe { f.write_ptr(g.buf.data, g.buf.len) }
f.close()
if g.pref.is_verbose {
println('\narm64 mach-o binary has been successfully generated')
}
}
pub fn (mut g Gen) generate_footer() {
match g.pref.os {
.macos {
g.generate_macho_footer()
}
.linux {
g.generate_elf_footer()
}
.raw {
g.create_executable()
}
else {}
}
}
pub fn (mut g Gen) stmts(stmts []ast.Stmt) {
for stmt in stmts {
g.stmt(stmt)
}
}
/*
pub fn new_gen(out_name string) &Gen {
return &Gen{
sect_header_name_pos: 0
buf: []
out_name: out_name
}
}
*/
pub fn (g &Gen) pos() i64 {
return g.buf.len
}
fn (mut g Gen) write8(n int) {
// write 1 byte
g.buf << byte(n)
}
fn (mut g Gen) write16(n int) {
// write 2 bytes
g.buf << byte(n)
g.buf << byte(n >> 8)
}
fn (mut g Gen) write32(n int) {
// write 4 bytes
g.buf << byte(n)
g.buf << byte(n >> 8)
g.buf << byte(n >> 16)
g.buf << byte(n >> 24)
}
fn (mut g Gen) write64(n i64) {
// write 8 bytes
g.buf << byte(n)
g.buf << byte(n >> 8)
g.buf << byte(n >> 16)
g.buf << byte(n >> 24)
g.buf << byte(n >> 32)
g.buf << byte(n >> 40)
g.buf << byte(n >> 48)
g.buf << byte(n >> 56)
}
fn (mut g Gen) write64_at(n i64, at i64) {
// write 8 bytes
g.buf[at] = byte(n)
g.buf[at + 1] = byte(n >> 8)
g.buf[at + 2] = byte(n >> 16)
g.buf[at + 3] = byte(n >> 24)
g.buf[at + 4] = byte(n >> 32)
g.buf[at + 5] = byte(n >> 40)
g.buf[at + 6] = byte(n >> 48)
g.buf[at + 7] = byte(n >> 56)
}
fn (mut g Gen) write32_at(at i64, n int) {
// write 4 bytes
g.buf[at] = byte(n)
g.buf[at + 1] = byte(n >> 8)
g.buf[at + 2] = byte(n >> 16)
g.buf[at + 3] = byte(n >> 24)
}
fn (mut g Gen) write_string(s string) {
for c in s {
g.write8(int(c))
}
// g.write8(0) // null terminated strings
}
fn (mut g Gen) write_string_with_padding(s string, max int) {
for c in s {
g.write8(int(c))
}
for _ in 0 .. max - s.len {
g.write8(0)
}
}
fn (mut g Gen) get_var_offset(var_name string) int {
offset := g.var_offset[var_name]
if offset == 0 {
panic('0 offset for var `$var_name`')
}
return offset
}
pub fn (mut g Gen) gen_print_from_expr(expr ast.Expr, newline bool) {
match expr {
ast.StringLiteral {
if newline {
g.gen_print(expr.val + '\n')
} else {
g.gen_print(expr.val)
}
}
else {}
}
}
pub fn (mut g Gen) register_function_address(name string) {
addr := g.pos()
// println('reg fn addr $name $addr')
g.fn_addr[name] = addr
}
fn (mut g Gen) for_in_stmt(node ast.ForInStmt) {
verror('for-in statement is not yet implemented')
/*
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 = ')
g.expr(node.cond)
g.write32(0x3232) // ; $i < ')
g.expr(node.high)
g.write32(0x3333) // '; ++$i) {')
} 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_ {
}
*/
}
fn (mut g Gen) stmt(node ast.Stmt) {
match node {
ast.AssignStmt {
g.assign_stmt(node)
}
ast.Block {
g.stmts(node.stmts)
}
ast.ConstDecl {}
ast.ExprStmt {
g.expr(node.expr)
}
ast.FnDecl {
g.fn_decl(node)
}
ast.ForInStmt {
g.for_in_stmt(node)
}
ast.ForStmt {
g.for_stmt(node)
}
ast.HashStmt {
words := node.val.split(' ')
for word in words {
if word.len != 2 {
verror('opcodes format: xx xx xx xx')
}
b := unsafe { C.strtol(&char(word.str), 0, 16) }
// b := word.byte()
// println('"$word" $b')
g.write8(b)
}
}
ast.Module {}
ast.Return {
g.gen_exit()
g.ret()
}
ast.StructDecl {}
else {
println('native.stmt(): bad node: ' + node.type_name())
}
}
}
fn C.strtol(str &char, endptr &&char, base int) int
fn (mut g Gen) expr(node ast.Expr) {
match node {
ast.ArrayInit {}
ast.BoolLiteral {}
ast.CallExpr {
if node.name in ['println', 'print', 'eprintln', 'eprint'] {
expr := node.args[0].expr
g.gen_print_from_expr(expr, node.name in ['println', 'eprintln'])
return
}
g.call_fn(node)
}
ast.FloatLiteral {}
ast.Ident {}
ast.IfExpr {
g.if_expr(node)
}
ast.InfixExpr {}
ast.IntegerLiteral {}
ast.PostfixExpr {
g.postfix_expr(node)
}
ast.StringLiteral {}
ast.StructInit {}
else {
println(term.red('native.expr(): unhandled node: ' + node.type_name()))
}
}
}
/*
fn (mut g Gen) allocate_var(name string, size int, initial_val int) {
g.cgen.allocate_var(name, size, initial_val)
}
*/
fn (mut g Gen) postfix_expr(node ast.PostfixExpr) {
if node.expr !is ast.Ident {
return
}
ident := node.expr as ast.Ident
var_name := ident.name
if node.op == .inc {
g.inc_var(var_name)
}
}
fn verror(s string) {
util.verror('native gen error', s)
}
pub fn (mut g Gen) error_with_pos(s string, pos token.Position) {
// TODO: store a file index in the Position too,
// so that the file path can be retrieved from the pos, instead
// of guessed from the pref.path ...
mut kind := 'error:'
if g.pref.output_mode == .stdout {
ferror := util.formatted_error(kind, s, g.pref.path, pos)
eprintln(ferror)
exit(1)
} else {
g.errors << errors.Error{
file_path: g.pref.path
pos: pos
reporter: .gen
message: s
}
}
}