From efa07cbcbfef736c6c3e73ad647498d4a144d700 Mon Sep 17 00:00:00 2001 From: pancake Date: Sun, 23 May 2021 04:54:28 +0200 Subject: [PATCH] native: initial support for linux-arm64 (hello world only for now) (#10176) --- cmd/v/help/build.txt | 2 + cmd/v/help/default.txt | 2 +- vlib/v/gen/native/amd64.v | 8 ++++ vlib/v/gen/native/arm64.v | 80 +++++++++++++++++++++++++++++---------- vlib/v/gen/native/elf.v | 20 ++++++---- vlib/v/gen/native/gen.v | 2 +- 6 files changed, 84 insertions(+), 30 deletions(-) diff --git a/cmd/v/help/build.txt b/cmd/v/help/build.txt index ed1f713c95..2f31846dc4 100644 --- a/cmd/v/help/build.txt +++ b/cmd/v/help/build.txt @@ -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`. diff --git a/cmd/v/help/default.txt b/cmd/v/help/default.txt index b3cc93426b..1746a9e20f 100644 --- a/cmd/v/help/default.txt +++ b/cmd/v/help/default.txt @@ -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 " for more information about a command, example: `v help build`, `v help build-c` +Use "v help " 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. diff --git a/vlib/v/gen/native/amd64.v b/vlib/v/gen/native/amd64.v index 090edd54bc..1f421e6085 100644 --- a/vlib/v/gen/native/amd64.v +++ b/vlib/v/gen/native/amd64.v @@ -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] diff --git a/vlib/v/gen/native/arm64.v b/vlib/v/gen/native/arm64.v index c309049fdd..c0fceece35 100644 --- a/vlib/v/gen/native/arm64.v +++ b/vlib/v/gen/native/arm64.v @@ -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 { diff --git a/vlib/v/gen/native/elf.v b/vlib/v/gen/native/elf.v index 7e6239dbfc..88084fe544 100644 --- a/vlib/v/gen/native/elf.v +++ b/vlib/v/gen/native/elf.v @@ -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() } diff --git a/vlib/v/gen/native/gen.v b/vlib/v/gen/native/gen.v index d53ecc03f1..ad1a07b1d3 100644 --- a/vlib/v/gen/native/gen.v +++ b/vlib/v/gen/native/gen.v @@ -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()