1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

native: make code generation logic platform-independant (#18057)

This commit is contained in:
Spydr 2023-04-28 15:00:23 +02:00 committed by GitHub
parent 27e1c20e3d
commit 433208ea7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 2982 additions and 2755 deletions

File diff suppressed because it is too large Load Diff

View File

@ -42,11 +42,16 @@ mut:
// arm64 specific stuff for code generation
}
fn (mut x Arm64) allocate_var(name string, size int, initial_val int) {
fn (mut x Arm64) allocate_var(name string, size int, initial_val int) int {
eprintln('TODO: allocating var on arm64 (${name}) = ${size} = ${initial_val}')
return 0
}
fn (mut g Gen) mov_arm(reg Arm64Register, val u64) {
fn (mut c Arm64) mov(reg Register, val int) {
c.mov_arm(reg as Arm64Register, u64(val))
}
fn (mut c Arm64) mov_arm(reg Arm64Register, val u64) {
// m := u64(0xffff)
// x := u64(val)
// println('========')
@ -55,10 +60,10 @@ fn (mut g Gen) mov_arm(reg Arm64Register, val u64) {
// g.write32(0x777777)
r := int(reg)
if r >= 0 && r <= 16 {
g.write32(int(u32(0xd2800000 + u32(r) + (u32(val) << 5))))
g.println('mov x${r}, ${val}')
c.g.write32(int(u32(0xd2800000 + u32(r) + (u32(val) << 5))))
c.g.println('mov x${r}, ${val}')
} else {
g.n_error('mov_arm unsupported values')
c.g.n_error('mov_arm unsupported values')
}
/*
if 1 ^ (x & ~m) != 0 {
@ -74,34 +79,34 @@ fn (mut g Gen) mov_arm(reg Arm64Register, val u64) {
*/
}
fn (mut g Gen) neg_arm(r Arm64Register) {
g.neg_regs_arm(r, r)
fn (mut c Arm64) neg(r Arm64Register) {
c.neg_regs(r, r)
}
fn (mut g Gen) neg_regs_arm(a Arm64Register, b Arm64Register) {
fn (mut c Arm64) neg_regs(a Arm64Register, b Arm64Register) {
if u32(a) < 0x0f && u32(b) < 0x0f {
g.write32(int(0xe2600000 | (u32(a) << 16) | u32(b) << 12))
g.println('neg ${a}, ${b}')
c.g.write32(int(0xe2600000 | (u32(a) << 16) | u32(b) << 12))
c.g.println('neg ${a}, ${b}')
} else {
g.n_error('unhandled neg ${a}, ${b}')
c.g.n_error('unhandled neg ${a}, ${b}')
}
}
fn (mut g Gen) sub_sp(v int) {
if g.pref.arch != .arm64 {
g.n_error('sub_sp is arm64-specifig')
fn (mut c Arm64) sub_sp(v int) {
if c.g.pref.arch != .arm64 {
c.g.n_error('sub_sp is arm64-specifig')
return
}
// this is for 0x20 only
if v < 0 {
g.write32(i32(0x910083ff)) // add sp, X
c.g.write32(i32(0x910083ff)) // add sp, X
} else {
g.write32(i32(0xd10083ff)) // sub sp, X
c.g.write32(i32(0xd10083ff)) // sub sp, X
}
}
pub fn (mut g Gen) fn_decl_arm64(node ast.FnDecl) {
g.gen_arm64_helloworld()
pub fn (mut c Arm64) fn_decl(node ast.FnDecl) {
c.g.gen_arm64_helloworld()
/*
0x100003f6c ff8300d1 sub sp, sp, 0x20 ; [00] -r-x section size 52 named 0.__TEXT.__text
0x100003f70 fd7b01a9 stp x29, x30, [sp, 0x10]
@ -162,7 +167,7 @@ pub fn (mut g Gen) fn_decl_arm64(node ast.FnDecl) {
g.push(.rax)
for defer_stmt in g.defer_stmts.reverse() {
defer_var := g.get_var_offset('_defer$defer_stmt.idx_in_fn')
g.mov_var_to_reg(.rax, defer_var)
g.code_gen.mov_var_to_reg(.rax, defer_var)
g.cmp_zero(.rax)
label := g.labels.new_label()
jump_addr := g.cjmp(.je)
@ -181,132 +186,327 @@ pub fn (mut g Gen) fn_decl_arm64(node ast.FnDecl) {
*/
}
pub fn (mut g Gen) call_fn_arm64(node ast.CallExpr) {
pub fn (mut c Arm64) call_fn(node ast.CallExpr) {
name := node.name
// println('call fn $name')
addr := g.fn_addr[name]
addr := c.g.fn_addr[name]
if addr == 0 {
g.n_error('fn addr of `${name}` = 0')
c.g.n_error('fn addr of `${name}` = 0')
}
// Copy values to registers (calling convention)
// g.mov_arm(.eax, 0)
// c.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())
// c.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 {
var_offset := c.g.get_var_offset(expr.name)
if c.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)
c.g.code_gen.mov_var_to_reg(native.fn_arg_registers[i], var_offset)
}
*/
else {
g.n_error('unhandled call_fn (name=${name}) node: ' + expr.type_name())
c.g.n_error('unhandled call_fn (name=${name}) node: ' + expr.type_name())
}
}
}
if node.args.len > 6 {
g.n_error('more than 6 args not allowed for now')
c.g.n_error('more than 6 args not allowed for now')
}
g.call(int(addr))
g.println('fn call `${name}()`')
c.call(int(addr))
c.g.println('fn call `${name}()`')
// println('call $name $addr')
}
fn (mut g Gen) gen_arm64_helloworld() {
mut c := g.code_gen
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()
c.mov(Arm64Register.x0, 1)
c.adr(Arm64Register.x1, 0x10)
c.mov(Arm64Register.x2, 13)
c.mov(Arm64Register.x8, 64) // write (linux-arm64)
c.svc()
} else {
g.mov_arm(.x0, 1)
g.adr(.x1, 0x10 + 4)
g.mov_arm(.x2, 13)
g.mov_arm(.x16, 4) // write
g.svc()
g.mov_arm(.x0, 0)
g.mov_arm(.x16, 1)
g.svc()
c.mov(Arm64Register.x0, 1)
c.adr(Arm64Register.x1, 0x10 + 4)
c.mov(Arm64Register.x2, 13)
c.mov(Arm64Register.x16, 4) // write
c.svc()
c.mov(Arm64Register.x0, 0)
c.mov(Arm64Register.x16, 1)
c.svc()
}
zero := ast.IntegerLiteral{}
g.gen_exit(zero)
g.code_gen.gen_exit(zero)
g.write_string('Hello World!\n')
g.write8(0) // padding?
g.write8(0)
g.write8(0)
}
fn (mut g Gen) adr(r Arm64Register, delta int) {
g.write32(int(0x10000000 | int(r) | int(u32(delta) << 4)))
g.println('adr ${r}, ${delta}')
fn (mut c Arm64) adr(r Arm64Register, delta int) {
c.g.write32(int(0x10000000 | int(r) | int(u32(delta) << 4)))
c.g.println('adr ${r}, ${delta}')
}
fn (mut g Gen) bl() {
fn (mut c Arm64) bl() {
// g.write32(0xa9400000)
g.write32(0x94000000)
g.println('bl 0')
c.g.write32(0x94000000)
c.g.println('bl 0')
}
fn (mut g Gen) svc() {
if g.pref.os == .linux {
g.write32(0xd4001001)
g.println('svc 0x80')
fn (mut c Arm64) svc() {
if c.g.pref.os == .linux {
c.g.write32(0xd4001001)
c.g.println('svc 0x80')
} else {
g.write32(0xd4000001)
g.println('svc 0')
c.g.write32(0xd4000001)
c.g.println('svc 0')
}
}
pub fn (mut c Arm64) gen_exit(mut g Gen, expr ast.Expr) {
fn (mut c Arm64) syscall() {
panic('the `syscall` instruction is not available with arm64')
}
pub fn (mut c Arm64) gen_exit(expr ast.Expr) {
mut return_code := u64(0)
match expr {
ast.IntegerLiteral {
return_code = expr.val.u64()
}
else {
g.n_error('native builtin exit expects a numeric argument')
c.g.n_error('native builtin exit expects a numeric argument')
}
}
match c.g.pref.os {
.macos {
c.g.mov_arm(.x0, return_code)
c.g.mov_arm(.x16, 1) // syscall exit
c.mov_arm(.x0, return_code)
c.mov_arm(.x16, 1) // syscall exit
}
.linux {
c.g.mov_arm(.x16, return_code)
c.g.mov_arm(.x8, 93)
c.g.mov_arm(.x0, 0)
c.mov_arm(.x16, return_code)
c.mov_arm(.x8, 93)
c.mov_arm(.x0, 0)
}
else {
g.n_error('unsupported os ${c.g.pref.os}')
c.g.n_error('unsupported os ${c.g.pref.os}')
}
}
g.svc()
c.svc()
}
pub fn (mut g Gen) gen_arm64_exit(expr ast.Expr) {
pub fn (mut c Arm64) gen_arm64_exit(expr ast.Expr) {
match expr {
ast.IntegerLiteral {
g.mov_arm(.x16, expr.val.u64())
c.mov_arm(.x16, expr.val.u64())
}
else {
g.n_error('native builtin exit expects a numeric argument')
c.g.n_error('native builtin exit expects a numeric argument')
}
}
g.mov_arm(.x0, 0)
g.svc()
c.mov_arm(.x0, 0)
c.svc()
}
fn (mut c Arm64) gen_print(s string, fd int) {
panic('Arm64.gen_print() is not implemented')
}
fn (mut c Arm64) gen_print_reg(r Register, n int, fd int) {
panic('Arm64.gen_print_reg() is not implemented')
}
fn (mut g Gen) gen_asm_stmt_arm64(asm_node ast.AsmStmt) {
g.v_error('The asm statement for arm64 not yet implemented', asm_node.pos)
}
fn (mut c Arm64) learel(reg Register, val int) {
panic('Arm64.learel() not implemented')
}
fn (mut c Arm64) lea_var_to_reg(reg Register, var_offset int) {
panic('Arm64.lea_var_to_reg() not implemented')
}
fn (mut c Arm64) main_reg() Register {
panic('Arm64.main_reg() not implemented')
}
fn (mut c Arm64) gen_match_expr(expr ast.MatchExpr) {
panic('Arm64.gen_match_expr() not implemented')
}
fn (mut c Arm64) convert_int_to_string(a Register, b Register) {
panic('Arm64.convert_int_to_string() not implemented')
}
fn (mut c Arm64) convert_bool_to_string(r Register) {
panic('Arm64.convert_bool_to_string() not implemented')
}
fn (mut c Arm64) reverse_string(r Register) {
panic('Arm64.reverse_string() not implemented')
}
fn (mut c Arm64) mov_var_to_reg(reg Register, var Var, config VarConfig) {
panic('Arm64.mov_var_to_reg() not implemented')
}
fn (mut c Arm64) mov_reg(r1 Register, r2 Register) {
panic('Arm64.mov_reg() not implemented')
}
fn (mut c Arm64) mov64(r Register, val i64) {
panic('Arm64.mov64() not implemented')
}
fn (mut c Arm64) convert_rune_to_string(r Register, buffer int, var Var, config VarConfig) {
panic('Arm64.convert_rune_to_string() not implemented')
}
fn (mut c Arm64) apicall(call ApiCall) {
panic('Arm64.apicall() not implemented')
}
fn (mut c Arm64) trap() {
panic('Arm64.trap() not implemented')
}
fn (mut c Arm64) leave() {
panic('Arm64.leave() not implemented')
}
fn (mut c Arm64) ret() {
c.g.write32(0xd65f03c0)
c.g.println('ret')
}
fn (mut c Arm64) assign_stmt(node ast.AssignStmt) {
panic('Arm64.assign_stmt() not implemented')
}
fn (mut c Arm64) builtin_decl(builtin BuiltinFn) {
panic('Arm64.builtin_decl() not implemented')
}
fn (mut c Arm64) infix_expr(node ast.InfixExpr) {
panic('Arm64.infix_expr() not implemented')
}
fn (mut c Arm64) return_stmt(node ast.Return) {
panic('Arm64.return_stmt() not implemented')
}
fn (mut c Arm64) gen_cast_expr(expr ast.CastExpr) {
panic('Arm64.gen_cast_expr() not implemented')
}
fn (mut c Arm64) prefix_expr(expr ast.PrefixExpr) {
panic('Arm64.prefix_expr() not implemented')
}
fn (mut c Arm64) call_builtin(name Builtin) i64 {
panic('Arm64.call_builtin() not implemented')
}
fn (mut c Arm64) gen_asm_stmt(asm_node ast.AsmStmt) {
panic('Arm64.gen_asm_stmt() not implemented')
}
fn (mut c Arm64) gen_assert(assert_node ast.AssertStmt) {
panic('Arm64.gen_assert() not implemented')
}
fn (mut c Arm64) infloop() {
c.g.write32(u8(0x14))
c.g.println('jmp $$')
}
fn (mut c Arm64) jmp_back(start i64) {
panic('Arm64.jmp_back() not implemented')
}
fn (mut c Arm64) init_struct(var Var, init ast.StructInit) {
panic('Arm64.init_struct() not implemented')
}
fn (mut c Arm64) load_fp_var(var Var, config VarConfig) {
panic('Arm64.load_fp_var() not implemented')
}
fn (mut c Arm64) load_fp(val f64) {
panic('Arm64.load_fp() not implemented')
}
fn (mut c Arm64) for_in_stmt(node ast.ForInStmt) {
panic('Arm64.for_in_stmt() not implemented')
}
fn (mut c Arm64) cmp_zero(reg Register) {
panic('Arm64.cmp_zero() not implemented')
}
fn (mut c Arm64) cmp_var_reg(var Var, reg Register, config VarConfig) {
panic('Arm64.cmp_var_reg() not implemented')
}
fn (mut c Arm64) cmp_var(var Var, val int, config VarConfig) {
panic('Arm64.cmp_var() not implemented')
}
fn (mut c Arm64) dec_var(var Var, config VarConfig) {
panic('Arm64.dec_var() not implemented')
}
fn (mut c Arm64) inc_var(var Var, config VarConfig) {
panic('Arm64.inc_var() not implemented')
}
fn (mut c Arm64) cjmp(op JumpOp) int {
panic('Arm64.cjmp() not implemented')
}
fn (mut c Arm64) jmp(addr int) int {
panic('Arm64.jmp() not implemented')
}
fn (mut c Arm64) gen_syscall(node ast.CallExpr) {
panic('Arm64.gen_syscall() not implemented')
}
fn (mut c Arm64) movabs(reg Register, val i64) {
panic('Arm64.movabs() not implemented')
}
fn (mut c Arm64) gen_selector_expr(expr ast.SelectorExpr) {
panic('Arm64.gen_selector_expr() not implemented')
}
fn (mut c Arm64) mov_reg_to_var(var Var, reg Register, config VarConfig) {
panic('Arm64.mov_reg_to_var() not implemented')
}
fn (mut c Arm64) mov_int_to_var(var Var, integer int, config VarConfig) {
panic('Arm64.mov_int_to_var() not implemented')
}
fn (mut c Arm64) call(addr int) i64 {
panic('Arm64.call() not implemented')
}
fn (mut c Arm64) call_addr_at(addr int, at i64) i64 {
panic('Arm64.call_addr_at() not implemented')
}
fn (mut c Arm64) gen_concat_expr(expr ast.ConcatExpr) {
panic('Arm64.gen_concat_expr() not implemented')
}

View File

@ -28,21 +28,21 @@ pub fn (mut g Gen) init_builtins() {
.int_to_string: BuiltinFn{
// 32-bit signed integer to string conversion
body: fn (builtin BuiltinFn, mut g Gen) {
g.convert_int_to_string(builtin.arg_regs[0], builtin.arg_regs[1])
g.code_gen.convert_int_to_string(builtin.arg_regs[0], builtin.arg_regs[1])
}
arg_regs: [.rcx, .rdi]
arg_regs: [Amd64Register.rcx, Amd64Register.rdi]
}
.bool_to_string: BuiltinFn{
body: fn (builtin BuiltinFn, mut g Gen) {
g.convert_bool_to_string(builtin.arg_regs[0])
g.code_gen.convert_bool_to_string(builtin.arg_regs[0])
}
arg_regs: [.rax]
arg_regs: [Amd64Register.rax]
}
.reverse_string: BuiltinFn{
body: fn (builtin BuiltinFn, mut g Gen) {
g.reverse_string(builtin.arg_regs[0])
g.code_gen.reverse_string(builtin.arg_regs[0])
}
arg_regs: [.rdi]
arg_regs: [Amd64Register.rdi]
}
}
}
@ -62,17 +62,13 @@ pub fn (mut g Gen) generate_builtins() {
g.defer_stmts.clear()
g.labels = &LabelTable{}
if g.pref.arch == .arm64 {
g.n_error('builtins are not implemented for arm64')
} else {
g.builtin_decl_amd64(builtin)
}
g.code_gen.builtin_decl(builtin)
g.patch_labels()
// patch all call addresses where this builtin gets called
for call in builtin.calls {
rel := g.call_addr_at(int(call_addr), call)
rel := g.code_gen.call_addr_at(int(call_addr), call)
g.write32_at(call + 1, int(rel))
}
}
@ -87,9 +83,5 @@ pub fn (mut g Gen) get_builtin_arg_reg(name Builtin, index int) Register {
}
pub fn (mut g Gen) call_builtin(name Builtin) {
if g.pref.arch == .arm64 {
g.n_error('builtin calls are not implemented for arm64')
} else {
g.builtins[name].calls << g.call_builtin_amd64(name)
}
g.builtins[name].calls << g.code_gen.call_builtin(name)
}

View File

@ -760,13 +760,10 @@ pub fn (mut g Gen) generate_linkable_elf_header() {
g.elf_text_header_addr = text_section.header.offset
g.write64_at(g.elf_text_header_addr + 24, g.pos()) // write the code start pos to the text section
g.call(native.placeholder)
g.code_gen.call(native.placeholder)
g.println('; call main.main')
if g.pref.arch == .arm64 {
} else {
g.mov64(.rax, 0)
}
g.ret()
g.code_gen.mov64(g.code_gen.main_reg(), 0)
g.code_gen.ret()
g.println('; return 0')
g.debug_pos = g.buf.len
@ -801,20 +798,20 @@ pub fn (mut g Gen) generate_simple_elf_header() {
g.code_start_pos = g.pos()
g.debug_pos = int(g.pos())
g.call(native.placeholder)
g.code_gen.call(native.placeholder)
g.println('; call main.main')
// generate exit syscall
match g.pref.arch {
.arm64 {
g.mov_arm(.x16, 0)
g.mov_arm(.x0, 0)
g.svc()
g.code_gen.mov(Arm64Register.x16, 0)
g.code_gen.mov(Arm64Register.x0, 0)
g.code_gen.svc()
}
.amd64 {
g.mov(.edi, 0)
g.mov(.eax, g.nsyscall_exit())
g.syscall()
g.code_gen.mov(Amd64Register.edi, 0)
g.code_gen.mov(Amd64Register.eax, g.nsyscall(.exit))
g.code_gen.syscall()
}
else {
g.n_error('unsupported platform ${g.pref.arch}')

File diff suppressed because it is too large Load Diff

View File

@ -256,7 +256,7 @@ pub fn (mut g Gen) generate_macho_header() {
g.write32_at(cmdsize_offset, g.buf.len - 24)
g.write_nulls(pagesize - g.buf.len)
g.call(0)
g.code_gen.call(0)
}
fn (mut g Gen) get_pagesize() int {

View File

@ -1,21 +1,12 @@
import os
import v.gen.native
import v.pref
import v.ast
fn test_macho() {
os.chdir(os.temp_dir()) or {}
mut g := native.Gen{
pref: &pref.Preferences{
arch: .amd64
}
out_name: 'test.bin'
table: ast.new_table()
code_gen: native.Amd64{
g: 0
}
labels: 0
}
mut g := native.macho_test_new_gen(&pref.Preferences{
arch: .amd64
}, 'test.bin')
g.generate_macho_header()
g.generate_macho_footer()
}

View File

@ -208,8 +208,8 @@ pub fn (mut g Gen) generate_pe_header() {
pad_to(mut g.buf, 0x400)
g.code_start_pos = g.buf.len
g.call(0x18e)
g.ret()
g.code_gen.call(0x18e)
g.code_gen.ret()
g.main_fn_addr = g.buf.len
}

View File

@ -0,0 +1,54 @@
// Copyright (c) 2019-2023 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
// windows apicall names
enum ApiCall {
write_file
get_std_handle
exit_process
}
// unix syscall names
enum SysCall {
write
exit
}
fn (mut g Gen) nsyscall_macos(syscall SysCall) int {
return match syscall {
.write {
0x2000004
}
.exit {
0x2000001
}
}
}
fn (mut g Gen) nsyscall_linux(syscall SysCall) int {
return match syscall {
.write {
1
}
.exit {
60
}
}
}
fn (mut g Gen) nsyscall(syscall SysCall) int {
match g.pref.os {
.linux {
return g.nsyscall_linux(syscall)
}
.macos {
return g.nsyscall_macos(syscall)
}
else {
g.n_error('syscall is unsupported on platform ${g.pref.os}')
}
}
return 0
}