x64: handle -arch amd64/arm64 and -os for raw/linux/macos options (#9844)

pull/9848/head
pancake 2021-04-22 14:44:25 +02:00 committed by GitHub
parent 59e23dbb57
commit b951d679ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 158 additions and 57 deletions

View File

@ -7,10 +7,8 @@ import v.gen.x64
import v.markused
pub fn (mut b Builder) build_x64(v_files []string, out_file string) {
$if !linux {
println('v -x64 can only generate Linux binaries for now')
println('You are not on a Linux system, so you will not ' +
'be able to run the resulting executable')
$if !linux && !macos {
eprintln('Warning: v -x64 can only generate macOS and Linux binaries for now')
}
util.timing_start('PARSE')
b.parsed_files = parser.parse_files(v_files, b.table, b.pref, b.global_scope)

View File

@ -0,0 +1,40 @@
module x64
pub struct Amd64 {
// arm64 specific stuff for code generation
}
pub fn (mut x Amd64) allocate_var(mut g Gen, name string, size int, initial_val int) {
// `a := 3` =>
// `move DWORD [rbp-0x4],0x3`
match size {
1 {
// BYTE
g.write8(0xc6)
g.write8(0x45)
}
4 {
// DWORD
g.write8(0xc7)
g.write8(0x45)
}
8 {
// QWORD
g.write8(0x48)
g.write8(0xc7)
g.write8(0x45)
}
else {
verror('allocate_var: bad size $size')
}
}
// Generate N in `[rbp-N]`
n := g.stack_var_pos + size
g.write8(0xff - n + 1)
g.stack_var_pos += size
g.var_offset[name] = g.stack_var_pos
// Generate the value assigned to the variable
g.write32(initial_val)
// println('allocate_var(size=$size, initial_val=$initial_val)')
g.println('mov DWORD [rbp-$n.hex2()],$initial_val (Allocate var `$name`)')
}

View File

@ -0,0 +1,9 @@
module x64
pub struct Aarch64 {
// arm64 specific stuff for code generation
}
pub fn (mut x Aarch64) allocate_var(mut g Gen, name string, size int, initial_val int) {
eprintln('TODO: allocating var on arm64 ($name) = $size = $initial_val')
}

View File

@ -22,7 +22,8 @@ const (
et_rel = 1
et_exec = 2
et_dyn = 3
e_machine = 0x3e
e_machine_amd64 = 0x3e
e_machine_aarch64 = 183
shn_xindex = 0xffff
sht_null = 0
)
@ -41,7 +42,11 @@ pub fn (mut g Gen) generate_elf_header() {
g.buf << 1 // elf_osabi
g.write64(0) // et_rel) // et_rel for .o
g.write16(2) // e_type
g.write16(x64.e_machine) //
if g.pref.arch == .aarch64 {
g.write16(x64.e_machine_aarch64)
} else {
g.write16(x64.e_machine_amd64)
}
g.write32(x64.ev_current) // e_version
eh_size := 0x40
phent_size := 0x38

View File

@ -11,10 +11,15 @@ import v.pref
import term
import strings
interface CodeGen {
allocate_var(g &Gen, 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
@ -82,24 +87,65 @@ enum Size {
_64
}
fn get_backend(pref &pref.Preferences) CodeGen {
if pref.arch == .aarch64 {
return Aarch64{}
}
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: get_backend(pref)
}
if !pref.is_verbose {
println('use `v -x64 -v ...` to print resulting asembly/machine code')
}
g.generate_elf_header()
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_elf_footer()
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 {}
else {
verror('Error: only `raw`, `linux` and `macos` are supported for -os in -x64')
}
}
}
pub fn (mut g Gen) generate_footer() {
match g.pref.os {
.macos {
g.generate_macho_footer()
}
.linux {
g.generate_elf_footer()
}
else {
g.generate_macho_footer()
}
}
}
pub fn (mut g Gen) stmts(stmts []ast.Stmt) {
for stmt in stmts {
g.stmt(stmt)
@ -174,6 +220,7 @@ 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) {
@ -645,6 +692,27 @@ pub fn (mut g Gen) call_fn(node ast.CallExpr) {
// println('call $name $addr')
}
fn (mut g Gen) for_in_stmt(node ast.ForInStmt) {
eprintln('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 {
@ -660,6 +728,9 @@ fn (mut g Gen) stmt(node ast.Stmt) {
ast.FnDecl {
g.fn_decl(node)
}
ast.ForInStmt {
g.for_in_stmt(node)
}
ast.ForStmt {
g.for_stmt(node)
}
@ -690,7 +761,6 @@ fn (mut g Gen) stmt(node ast.Stmt) {
fn C.strtol(str &char, endptr &&char, base int) int
fn (mut g Gen) expr(node ast.Expr) {
// println('cgen expr()')
match node {
ast.ArrayInit {}
ast.BoolLiteral {}
@ -721,38 +791,7 @@ fn (mut g Gen) expr(node ast.Expr) {
}
fn (mut g Gen) allocate_var(name string, size int, initial_val int) {
// `a := 3` =>
// `move DWORD [rbp-0x4],0x3`
match size {
1 {
// BYTE
g.write8(0xc6)
g.write8(0x45)
}
4 {
// DWORD
g.write8(0xc7)
g.write8(0x45)
}
8 {
// QWORD
g.write8(0x48)
g.write8(0xc7)
g.write8(0x45)
}
else {
verror('allocate_var: bad size $size')
}
}
// Generate N in `[rbp-N]`
n := g.stack_var_pos + size
g.write8(0xff - n + 1)
g.stack_var_pos += size
g.var_offset[name] = g.stack_var_pos
// Generate the value assigned to the variable
g.write32(initial_val)
// println('allocate_var(size=$size, initial_val=$initial_val)')
g.println('mov DWORD [rbp-$n.hex2()],$initial_val (Allocate var `$name`)')
g.cgen.allocate_var(g, name, size, initial_val)
}
fn (mut g Gen) assign_stmt(node ast.AssignStmt) {

View File

@ -37,9 +37,15 @@ struct Reloc {
}
pub fn (mut g Gen) generate_macho_header() {
if g.pref.arch == .aarch64 {
g.write32(0xfeedfacf) // MH_MAGIC_64
g.write32(0x0100000c) // CPU_TYPE_ARM64
g.write32(0x00000000) // CPU_SUBTYPE_ARM64_ALL
} else {
g.write32(0xfeedfacf) // MH_MAGIC_64
g.write32(0x01000007) // CPU_TYPE_X64
g.write32(0x00000003) // CPU_SUBTYPE_X64
}
g.write32(0x00000001) // MH_OBJECT
g.write32(0x00000004) // # of load commands
g.write32(0x118) // size of load commands
@ -76,7 +82,7 @@ pub fn (mut g Gen) generate_macho_header() {
g.write32(0x18)
g.write32(0x01)
g.write32(0x000b0000)
g.write32(0x000a0000) // minOS 10.0
g.write32(0)
g.write32(0)
// lc_symtab
@ -93,6 +99,7 @@ pub fn (mut g Gen) generate_macho_header() {
for _ in 0 .. 12 {
g.write32(0)
}
// ADD THE CODE HERE THIS GOES INTO THE STMTS THING
// g.write32(0x77777777)
// assembly
g.mov_arm(.x0, 1)
@ -102,7 +109,7 @@ pub fn (mut g Gen) generate_macho_header() {
g.mov_arm(.x16, 1)
g.svc()
//
g.write_string('Hello WorlD!\n')
g.write_string('Hello World!\n')
g.write8(0) // padding?
g.write8(0)
g.write8(0)
@ -113,9 +120,9 @@ pub fn (mut g Gen) generate_macho_header() {
}
pub fn (mut g Gen) generate_macho_footer() {
// Create the binary
// Create the binary // should be .o ?
mut f := os.create(g.out_name) or { panic(err) }
os.chmod(g.out_name, 0o775) // make it an executable
os.chmod(g.out_name, 0o775) // make it executable
unsafe { f.write_ptr(g.buf.data, g.buf.len) }
f.close()
// println('\narm64 mach-o binary has been successfully generated')

View File

@ -17,6 +17,7 @@ pub enum OS {
android
solaris
haiku
raw
all
}
@ -35,6 +36,7 @@ pub fn os_from_string(os_str string) ?OS {
'solaris' { return .solaris }
'android' { return .android }
'haiku' { return .haiku }
'raw' { return .raw }
'linux_or_macos', 'nix' { return .linux }
'' { return ._auto }
else { return error('bad OS $os_str') }
@ -56,6 +58,7 @@ pub fn (o OS) str() string {
.android { return 'Android' }
.solaris { return 'Solaris' }
.haiku { return 'Haiku' }
.raw { return 'Raw' }
.all { return 'all' }
}
}