From b951d679ca3935f15b90e62b5fa7b8c0f08d3447 Mon Sep 17 00:00:00 2001 From: pancake Date: Thu, 22 Apr 2021 14:44:25 +0200 Subject: [PATCH] x64: handle -arch amd64/arm64 and -os for raw/linux/macos options (#9844) --- vlib/v/builder/x64.v | 6 +-- vlib/v/gen/x64/amd64.v | 40 ++++++++++++++ vlib/v/gen/x64/arm64.v | 9 ++++ vlib/v/gen/x64/elf.v | 21 +++++--- vlib/v/gen/x64/gen.v | 115 +++++++++++++++++++++++++++-------------- vlib/v/gen/x64/macho.v | 21 +++++--- vlib/v/pref/os.v | 3 ++ 7 files changed, 158 insertions(+), 57 deletions(-) create mode 100644 vlib/v/gen/x64/amd64.v create mode 100644 vlib/v/gen/x64/arm64.v diff --git a/vlib/v/builder/x64.v b/vlib/v/builder/x64.v index 6e8fb1c817..ba13199eb4 100644 --- a/vlib/v/builder/x64.v +++ b/vlib/v/builder/x64.v @@ -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) diff --git a/vlib/v/gen/x64/amd64.v b/vlib/v/gen/x64/amd64.v new file mode 100644 index 0000000000..7d42eda99f --- /dev/null +++ b/vlib/v/gen/x64/amd64.v @@ -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`)') +} diff --git a/vlib/v/gen/x64/arm64.v b/vlib/v/gen/x64/arm64.v new file mode 100644 index 0000000000..3319cb0b4e --- /dev/null +++ b/vlib/v/gen/x64/arm64.v @@ -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') +} diff --git a/vlib/v/gen/x64/elf.v b/vlib/v/gen/x64/elf.v index b25b1a0c9b..75c0700deb 100644 --- a/vlib/v/gen/x64/elf.v +++ b/vlib/v/gen/x64/elf.v @@ -18,13 +18,14 @@ const ( // ELF file types const ( - elf_osabi = 0 - et_rel = 1 - et_exec = 2 - et_dyn = 3 - e_machine = 0x3e - shn_xindex = 0xffff - sht_null = 0 + elf_osabi = 0 + et_rel = 1 + et_exec = 2 + et_dyn = 3 + e_machine_amd64 = 0x3e + e_machine_aarch64 = 183 + shn_xindex = 0xffff + sht_null = 0 ) const ( @@ -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 diff --git a/vlib/v/gen/x64/gen.v b/vlib/v/gen/x64/gen.v index ae7e199b71..daf7f4a4cd 100644 --- a/vlib/v/gen/x64/gen.v +++ b/vlib/v/gen/x64/gen.v @@ -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) { diff --git a/vlib/v/gen/x64/macho.v b/vlib/v/gen/x64/macho.v index ab51a58ea3..7800bc41ba 100644 --- a/vlib/v/gen/x64/macho.v +++ b/vlib/v/gen/x64/macho.v @@ -37,9 +37,15 @@ struct Reloc { } pub fn (mut g Gen) generate_macho_header() { - g.write32(0xfeedfacf) // MH_MAGIC_64 - g.write32(0x0100000c) // CPU_TYPE_ARM64 - g.write32(0x00000000) // CPU_SUBTYPE_ARM64_ALL + 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') diff --git a/vlib/v/pref/os.v b/vlib/v/pref/os.v index fea2d5ecab..048757b5b3 100644 --- a/vlib/v/pref/os.v +++ b/vlib/v/pref/os.v @@ -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' } } }