native: initial support for linux-arm64 (hello world only for now) (#10176)

pull/10179/head
pancake 2021-05-23 04:54:28 +02:00 committed by GitHub
parent 4b11e59bb0
commit efa07cbcbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 84 additions and 30 deletions

View File

@ -154,6 +154,8 @@ NB: the build flags are shared with the run command too:
and will become an error, after vlib modules are cleaned up.
For C-specific build flags, use `v help build-c`.
For JS-specific build flags, use `v help build-js`.
For Native-specific build flags, use `v help build-native`.
For Native-specific build flags, use `v help build-native`.

View File

@ -50,7 +50,7 @@ V supports the following commands:
Use `tracev yourfile.v` when the compiler panics.
NB: `tracev` is much slower and more verbose than ordinary `v`
Use "v help <command>" for more information about a command, example: `v help build`, `v help build-c`
Use "v help <command>" for more information about a command, example: `v help build`, `v help build-c`, `v help build-native`
Use "v help other" to see less frequently used commands.
Note: Help is required to write more help topics.

View File

@ -192,6 +192,10 @@ fn (mut g Gen) mov_var_to_reg(reg Register, var_offset int) {
}
fn (mut g Gen) call(addr int) {
if g.pref.arch == .arm64 {
g.bl()
return
}
// 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"
@ -472,6 +476,10 @@ fn (mut g Gen) mov_rbp_rsp() {
}
pub fn (mut g Gen) call_fn(node ast.CallExpr) {
if g.pref.arch == .arm64 {
g.call_fn_arm64(node)
return
}
name := node.name
// println('call fn $name')
addr := g.fn_addr[name]

View File

@ -43,12 +43,9 @@ fn (mut g Gen) mov_arm(reg Arm64Register, val u64) {
if r == 0 && val == 1 {
g.write32(0xd2800020)
g.println('mov x0, 1')
} else if r == 0 {
g.write32(0xd2800000)
g.println('mov x0, 0')
} else if r == 16 {
g.write32(0xd2800030)
g.println('mov x16, 1')
} else if r >= 0 && r <= 16 {
g.write32(0xd2800000 + int(r) + (int(val) << 5))
g.println('mov x$r, $val')
} else {
verror('mov_arm unsupported values')
}
@ -67,33 +64,73 @@ fn (mut g Gen) mov_arm(reg Arm64Register, val u64) {
}
pub fn (mut g Gen) fn_decl_arm64(node ast.FnDecl) {
g.gen_arm64_helloworld()
// TODO
}
fn (mut g Gen) gen_arm64_helloworld() {
// g.write32(0x77777777)
// assembly
g.mov_arm(.x0, 1)
g.adr()
g.bl()
pub fn (mut g Gen) call_fn_arm64(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_arm(.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_arm(native.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(native.fn_arg_registers[i]))
}
g.mov_var_to_reg(native.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) gen_arm64_helloworld() {
if g.pref.os == .linux {
g.mov_arm(.x0, 1)
g.adr(.x1, 0x10)
g.mov_arm(.x2, 13)
g.mov_arm(.x8, 64) // write (linux-arm64)
g.svc()
} else {
g.mov_arm(.x0, 0)
g.mov_arm(.x16, 1)
g.svc()
}
zero := ast.IntegerLiteral{}
g.gen_exit(zero)
/*
g.mov_arm(.x0, 0)
g.mov_arm(.x16, 1)
g.svc()
*/
//
g.write_string('Hello World!\n')
g.write8(0) // padding?
g.write8(0)
g.write8(0)
}
fn (mut g Gen) adr() {
g.write32(0x100000a0)
g.println('adr x0, 0x14')
fn (mut g Gen) adr(r Arm64Register, delta int) {
g.write32(0x10000000 | int(r) | (delta << 4))
g.println('adr $r, $delta')
}
fn (mut g Gen) bl() {
@ -124,6 +161,7 @@ pub fn (mut c Arm64) gen_exit(mut g Gen, expr ast.Expr) {
}
.linux {
c.g.mov_arm(.x16, return_code)
c.g.mov_arm(.x8, 93)
c.g.mov_arm(.x0, 0)
}
else {

View File

@ -37,7 +37,7 @@ pub fn (mut g Gen) generate_elf_header() {
g.buf << native.elfclass64 // file class
g.buf << native.elfdata2lsb // data encoding
g.buf << native.ev_current // file version
g.buf << 1 // elf_osabi
g.buf << native.elf_osabi
g.write64(0) // et_rel) // et_rel for .o
g.write16(2) // e_type
if g.pref.arch == .arm64 {
@ -97,11 +97,17 @@ pub fn (mut g Gen) generate_elf_footer() {
file_size := g.buf.len
g.write64_at(file_size, g.file_size_pos) // set file size 64 bit value
g.write64_at(file_size, g.file_size_pos + 8)
// call main function, it's not guaranteed to be the first
// we generated call(0) ("e8 0")
// now need to replace "0" with a relative address of the main function
// +1 is for "e8"
// -5 is for "e8 00 00 00 00"
g.write32_at(g.code_start_pos + 1, int(g.main_fn_addr - g.code_start_pos) - 5)
if g.pref.arch == .arm64 {
bl_next := u32(0x94000001)
g.write32_at(g.code_start_pos, int(bl_next))
} else {
// amd64
// call main function, it's not guaranteed to be the first
// we generated call(0) ("e8 0")
// now need to replace "0" with a relative address of the main function
// +1 is for "e8"
// -5 is for "e8 00 00 00 00"
g.write32_at(g.code_start_pos + 1, int(g.main_fn_addr - g.code_start_pos) - 5)
}
g.create_executable()
}

View File

@ -75,7 +75,7 @@ pub fn gen(files []&ast.File, table &ast.Table, out_name string, pref &pref.Pref
pref: pref
}
g.cgen = g.get_backend() or {
eprintln('No available backend for this configuration')
eprintln('No available backend for this configuration. Use `-a arm64` or `-a amd64`.')
exit(1)
}
g.generate_header()