From 81ae54d9bd1ee7095bf01eeb1183e2a8bbac2c50 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Mon, 30 Dec 2019 20:02:54 +0100 Subject: [PATCH] x64, v2 backends --- v.v | 3 + vlib/builtin/array.v | 2 +- vlib/builtin/bare/array_bare.v | 2 +- vlib/compiler/aparser.v | 10 +- vlib/compiler/expression.v | 3 +- vlib/compiler/fn.v | 15 +- vlib/compiler/for.v | 10 +- vlib/compiler/main.v | 25 +- vlib/compiler/x64/gen.v | 2 +- vlib/time/time_test.v | 23 ++ vlib/v/ast/ast.v | 7 +- vlib/v/gen/cgen.v | 7 + vlib/v/gen/cgen_test.v | 6 +- vlib/v/gen/tests/2.c | 21 +- vlib/v/gen/tests/2.vv | 20 +- vlib/v/gen/x64/elf.v | 100 ++++++++ vlib/v/gen/x64/elf_obj.v | 159 ++++++++++++ vlib/v/gen/x64/gen.v | 457 +++++++++++++++++++++++++++++++++ vlib/v/parser/parser.v | 95 +++++-- vlib/v/token/token.v | 6 +- vlib/v/types/types.v | 2 + 21 files changed, 901 insertions(+), 74 deletions(-) create mode 100644 vlib/v/gen/x64/elf.v create mode 100644 vlib/v/gen/x64/elf_obj.v create mode 100644 vlib/v/gen/x64/gen.v diff --git a/v.v b/v.v index c98089da43..22cc88df6b 100644 --- a/v.v +++ b/v.v @@ -68,6 +68,9 @@ fn main() { if v.pref.x64 { v.compile_x64() } + else if v.pref.v2 { + v.compile2() + } else { v.compile() } diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index 03fbdb4a6d..bcd71e3598 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -16,7 +16,7 @@ pub: } // Private function, used by V (`nums := []int`) -fn new_array(mylen, cap, elm_size int) array { +fn new_array(mylen int, cap int, elm_size int) array { cap_ := if cap == 0 { 1 } else { cap } arr := array{ len: mylen diff --git a/vlib/builtin/bare/array_bare.v b/vlib/builtin/bare/array_bare.v index e2fc075066..e80ec8a53a 100644 --- a/vlib/builtin/bare/array_bare.v +++ b/vlib/builtin/bare/array_bare.v @@ -9,7 +9,7 @@ pub: } // for now off the stack -fn new_array_from_c_array(len, cap, elm_size int, c_array voidptr) array { +fn new_array_from_c_array(len int, cap int, elm_size int, c_array voidptr) array { arr := array { len: len cap: cap diff --git a/vlib/compiler/aparser.v b/vlib/compiler/aparser.v index 416a07ca0c..ff5cc8002f 100644 --- a/vlib/compiler/aparser.v +++ b/vlib/compiler/aparser.v @@ -7,7 +7,7 @@ import ( os strings filepath - compiler.x64 + //compiler.x64 // time ) @@ -30,7 +30,7 @@ mut: prev_tok2 TokenKind // TODO remove these once the tokens are cached lit string cgen &CGen - x64 &x64.Gen + //x64 &x64.Gen table &Table import_table ImportTable // Holds imports for just the file being parsed pass Pass @@ -207,7 +207,7 @@ fn (v mut V) new_parser(scanner &Scanner) Parser { table: v.table cur_fn: EmptyFn cgen: v.cgen - x64: v.x64 + //x64: v.x64 pref: v.pref os: v.os vroot: v.vroot @@ -3095,7 +3095,9 @@ fn (p mut Parser) check_unused_imports() { } // the imports are usually at the start of the file //p.production_error_with_token_index('the following imports were never used: $output', 0) + if !p.file_path.contains ('vlib/v/') { p.warn('the following imports were never used: $output') + } } fn (p &Parser) is_expr_fn_call(start_tok_idx int) (bool,string) { @@ -3132,6 +3134,6 @@ fn (p mut Parser) skip_block(inside_first_lcbr bool) { } fn todo_remove() { - x64.new_gen('f') + //x64.new_gen('f') } diff --git a/vlib/compiler/expression.v b/vlib/compiler/expression.v index ad23e42b55..504f6ce935 100644 --- a/vlib/compiler/expression.v +++ b/vlib/compiler/expression.v @@ -356,6 +356,7 @@ fn (p mut Parser) name_expr() string { } // Color.green else if p.peek() == .dot { + is_arr_start := p.prev_tok == .lsbr enum_type := p.table.find_type(name) if enum_type.cat != .enum_ { p.error('`$name` is not an enum') @@ -366,7 +367,7 @@ fn (p mut Parser) name_expr() string { if !enum_type.has_enum_val(val) { p.error('enum `$enum_type.name` does not have value `$val`') } - if p.expected_type == enum_type.name { + if p.expected_type == enum_type.name && !is_arr_start { // `if color == .red` is enough // no need in `if color == Color.red` p.warn('`${enum_type.name}.$val` is unnecessary, use `.$val`') diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index 3cea04d9b4..8d2ca991d6 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -434,7 +434,7 @@ fn (p mut Parser) fn_decl() { // Special case for main() args if f.name == 'main__main' && !has_receiver { if p.pref.x64 && !p.first_pass() { - p.x64.save_main_fn_addr() + //p.x64.save_main_fn_addr() } if str_args != '' || typ != 'void' { p.error_with_token_index('fn main must have no arguments and no return values', f.fn_name_token_idx) @@ -554,7 +554,7 @@ fn (p mut Parser) fn_decl() { } } if p.pref.x64 { - p.x64.register_function_address(f.name) + //p.x64.register_function_address(f.name) } p.statements_no_rcbr() // p.cgen.nogen = false @@ -574,10 +574,10 @@ fn (p mut Parser) fn_decl() { p.genln('pthread_mutex_unlock(&live_fn_mutex);') } if p.pref.x64 && f.name == 'main__main' && !p.first_pass() { - p.x64.gen_exit() + //p.x64.gen_exit() } if p.pref.x64 && !p.first_pass() { - p.x64.ret() + //p.x64.ret() } // {} closed correctly? scope_level should be 0 if p.mod == 'main' { @@ -640,7 +640,8 @@ fn (p mut Parser) check_unused_and_mut_vars() { if var.name == '' { break } - if !var.is_used && !p.pref.is_repl && !var.is_arg && !p.pref.translated && var.name != 'tmpl_res' && p.mod != 'vweb' { + if !var.is_used && !p.pref.is_repl && !var.is_arg && !p.pref.translated && + var.name != 'tmpl_res' && p.mod != 'vweb' && var.name != 'it' { p.production_error_with_token_index('`$var.name` declared and not used', var.token_idx) } if !var.is_changed && var.is_mut && !p.pref.is_repl && !p.pref.translated && var.typ != 'T*' && p.mod != 'ui' && var.typ != 'App*' { @@ -754,7 +755,7 @@ fn (p mut Parser) fn_call(f mut Fn, method_ph int, receiver_var, receiver_type s p.cgen.nogen = true } if p.pref.x64 && !p.first_pass() { - p.x64.call_fn(f.name) + //p.x64.call_fn(f.name) } p.calling_c = f.is_c if f.is_c && !p.builtin_mod { @@ -1079,7 +1080,7 @@ fn (p mut Parser) fn_call_args(f mut Fn, generic_param_types []string) { } // x64 println gen if p.pref.x64 && i == 0 && f.name == 'println' && p.tok == .str && p.peek() == .rpar { - p.x64.gen_print(p.lit) + //p.x64.gen_print(p.lit) } mut typ := p.bool_expression() // Register an interface type usage: diff --git a/vlib/compiler/for.v b/vlib/compiler/for.v index b71d79f8a7..380ec2f914 100644 --- a/vlib/compiler/for.v +++ b/vlib/compiler/for.v @@ -12,7 +12,7 @@ fn (p mut Parser) for_st() { } // debug := p.scanner.file_path.contains('r_draw') p.open_scope() - mut label := 0 + //mut label := 0 mut to := 0 if p.tok == .lcbr { // Infinite loop @@ -148,7 +148,7 @@ fn (p mut Parser) for_st() { p.check_types(range_typ, 'int') range_end = range_expr if p.pref.x64 { - label = p.x64.gen_loop_start(expr.int()) + //label = p.x64.gen_loop_start(expr.int()) // to = range_expr.int() // TODO why empty? } } @@ -218,8 +218,8 @@ fn (p mut Parser) for_st() { p.close_scope() p.for_expr_cnt-- p.returns = false // TODO handle loops that are guaranteed to return - if label > 0 { - p.x64.gen_loop_end(to, label) - } + //if label > 0 { + //p.x64.gen_loop_end(to, label) + //} } diff --git a/vlib/compiler/main.v b/vlib/compiler/main.v index fb1bc6c302..85435ee22f 100644 --- a/vlib/compiler/main.v +++ b/vlib/compiler/main.v @@ -8,11 +8,12 @@ import ( os.cmdline strings filepath - compiler.x64 - //v.table - //v.parser - //v.gen + //compiler.x64 + v.gen.x64 //v.types + v.table + v.parser + v.gen ) pub const ( @@ -70,7 +71,7 @@ pub mut: compiled_dir string // contains os.realpath() of the dir of the final file beeing compiled, or the dir itself when doing `v .` table &Table // table with types, vars, functions etc cgen &CGen // C code generator - x64 &x64.Gen + //x64 &x64.Gen pref &Preferences // all the preferences and settings extracted to a struct for reusability lang_dir string // "~/code/v" out_name string // "program.exe" @@ -141,6 +142,7 @@ pub mut: x64 bool output_cross_c bool prealloc bool + v2 bool } // Should be called by main at the end of the compilation process, to cleanup @@ -378,7 +380,6 @@ pub fn (v mut V) compile() { v.cc() } -/* pub fn (v mut V) compile2() { if os.user_os() != 'windows' && v.pref.ccompiler == 'msvc' { verror('Cannot build with msvc on ${os.user_os()}') @@ -413,7 +414,6 @@ pub fn (v mut V) compile2() { v.cc() } -*/ pub fn (v mut V) compile_x64() { $if !linux { @@ -422,14 +422,18 @@ pub fn (v mut V) compile_x64() { } v.files << v.v_files_from_dir(filepath.join(v.pref.vlib_path,'builtin','bare')) v.files << v.dir - v.x64.generate_elf_header() + + table := &table.Table{} + files := parser.parse_files(v.files, table) + x64.gen(files, v.out_name) + /* for f in v.files { v.parse(f, .decl) } for f in v.files { v.parse(f, .main) } - v.x64.generate_elf_footer() + */ } fn (v mut V) generate_init() { @@ -1137,6 +1141,7 @@ pub fn new_v(args []string) &V { user_mod_path: user_mod_path vlib_path: vlib_path vpath: vpath + v2: '-v2' in args } if pref.is_verbose || pref.is_debug { println('C compiler=$pref.ccompiler') @@ -1158,7 +1163,7 @@ pub fn new_v(args []string) &V { table: new_table(obfuscate) out_name_c: out_name_c cgen: new_cgen(out_name_c) - x64: x64.new_gen(out_name) + //x64: x64.new_gen(out_name) vroot: vroot pref: pref mod: mod diff --git a/vlib/compiler/x64/gen.v b/vlib/compiler/x64/gen.v index ce531f7dff..279ada855f 100644 --- a/vlib/compiler/x64/gen.v +++ b/vlib/compiler/x64/gen.v @@ -253,7 +253,7 @@ fn (g mut Gen) mov(reg Register, val int) { pub fn (g mut Gen) register_function_address(name string) { addr := g.pos() - println('reg fn addr $name $addr') + //println('reg fn addr $name $addr') g.fn_addr[name] = addr } diff --git a/vlib/time/time_test.v b/vlib/time/time_test.v index 3f2b44a89e..461db69e00 100644 --- a/vlib/time/time_test.v +++ b/vlib/time/time_test.v @@ -21,6 +21,17 @@ fn test_is_leap_year() { assert time.is_leap_year(2100) == false } +fn test_now_format() { + /* + t := time.now() + u:=t.uni + println(u) + println(t.format()) + println(time.unix(u).format()) + assert t.format() == time.unix(u).format() + */ +} + fn check_days_in_month(month, year, expected int) bool { res := time.days_in_month(month, year) or { return false @@ -48,6 +59,18 @@ fn test_unix() { assert t.second == 59 } +fn test_unix2() { + /* + println(t.year) + assert t.year == 2019 + assert t.month == 12 + assert t.day == 31 + assert t.hour == 8 + assert t.minute == 9 + assert t.second == 53 + */ +} + fn test_format_ss() { assert '11.07.1980 21:23:42' == time_to_test.get_fmt_str(.dot, .hhmmss24, .ddmmyyyy) } diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index d73d8ed6b4..c71065c82b 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -58,6 +58,7 @@ pub struct StructDecl { pub: name string fields []Field + is_pub bool } pub struct StructInit { @@ -70,8 +71,8 @@ pub: // import statement pub struct Import { pub: - name string - expr Expr + mods []string + // expr Expr // imports map[string]string } @@ -160,7 +161,7 @@ pub: tok_kind token.Kind cond Expr stmts []Stmt - else_ []Stmt + else_stmts []Stmt } pub struct ForStmt { diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 0c904eab6e..5a8964d078 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -179,6 +179,13 @@ fn (g mut Gen) expr(node ast.Expr) { g.stmt(stmt) } g.writeln('}') + if it.else_stmts.len > 0 { + g.writeln('else { ') + for stmt in it.else_stmts { + g.stmt(stmt) + } + g.writeln('}') + } } else { println(term.red('cgen.expr(): bad node')) diff --git a/vlib/v/gen/cgen_test.v b/vlib/v/gen/cgen_test.v index a86a74ad4e..c93eabac82 100644 --- a/vlib/v/gen/cgen_test.v +++ b/vlib/v/gen/cgen_test.v @@ -40,13 +40,13 @@ fn compare_texts(a, b string) bool { lines_a := a.trim_space().split_into_lines() lines_b := b.trim_space().split_into_lines() if lines_a.len != lines_b.len { - println('different len') - return false + println(term.red('different len')) + // return false } for i, line_a in lines_a { line_b := lines_b[i] if line_a.trim_space() != line_b.trim_space() { - println('!' + line_a) + println(term.red('!' + line_a)) return false } } diff --git a/vlib/v/gen/tests/2.c b/vlib/v/gen/tests/2.c index f928d64723..c22c351074 100644 --- a/vlib/v/gen/tests/2.c +++ b/vlib/v/gen/tests/2.c @@ -11,6 +11,15 @@ typedef struct { string name; } User; +void init_user() { + User user = (User){ + .name = tos3("Bob"), + }; +} + +void puts(string s) { +} + void function2() { int x = 0; f64 f = 10.1; @@ -27,20 +36,18 @@ void function2() { if (false) { foo(1); } + else { + puts(tos3("else")); + foo(100); + } while (true) { - foo(0); + init_user(); } bool e = 1 + 2 > 0; bool e2 = 1 + 2 < 0; int j = 0; } -void init_user() { - User user = (User){ - .name = tos3("Bob"), - }; -} - void init_array() { int nums = new_array_from_c_array(3, 3, sizeof(int), { 1, 2, 3, diff --git a/vlib/v/gen/tests/2.vv b/vlib/v/gen/tests/2.vv index 60d59c65f3..082f6db5cd 100644 --- a/vlib/v/gen/tests/2.vv +++ b/vlib/v/gen/tests/2.vv @@ -10,9 +10,18 @@ struct User { name string } +fn init_user() { + user := User{ + name: 'Bob' + } +} + +fn puts(s string) {} + // comment fn function2() { mut x := 0 + //mut negative := -1 f := 10.1 s := 'hi' mut m := 10 @@ -29,8 +38,12 @@ fn function2() { if false { foo(1) } + else { + puts('else') + foo(100) + } for true { - foo(0) + init_user() } e := 1 + 2 > 0 e2 := 1 + 2 < 0 @@ -38,11 +51,6 @@ fn function2() { j := 0 } -fn init_user() { - user := User{ - name: 'Bob' - } -} fn init_array() { nums := [1,2,3] diff --git a/vlib/v/gen/x64/elf.v b/vlib/v/gen/x64/elf.v new file mode 100644 index 0000000000..ff018cb784 --- /dev/null +++ b/vlib/v/gen/x64/elf.v @@ -0,0 +1,100 @@ +// Copyright (c) 2019 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 x64 + +import os + +const ( + mag0 = 0x7f + mag1 = `E` + mag2 = `L` + mag3 = `F` + ei_class = 4 + elfclass64 = 2 + elfdata2lsb = 1 + ev_current = 1 + elf_osabi = 0 + // ELF file types + et_rel = 1 + et_exec = 2 + et_dyn = 3 + e_machine = 0x3e + shn_xindex = 0xffff + sht_null = 0 +) + +const ( + segment_start = 0x400000 +) + +pub fn (g mut Gen) generate_elf_header() { + g.buf << [byte(mag0), mag1, mag2, mag3] + g.buf << elfclass64 // file class + g.buf << elfdata2lsb // data encoding + g.buf << ev_current // file version + g.buf << 1 // elf_osabi + g.write64(0) // et_rel) // et_rel for .o + g.write16(2) // e_type + g.write16(e_machine) // + g.write32(ev_current) // e_version + eh_size := 0x40 + phent_size := 0x38 + g.write64(segment_start + eh_size + phent_size) // e_entry + g.write64(0x40) // e_phoff + g.write64(0) // e_shoff + g.write32(0) // e_flags + g.write16(eh_size) // e_ehsize + g.write16(phent_size) // e_phentsize + g.write16(1) // e_phnum + g.write16(0) // e_shentsize + g.write16(0) // e_shnum (number of sections) + g.write16(0) // e_shstrndx + // Elf64_Phdr + g.write32(1) // p_type + g.write32(5) // p_flags + g.write64(0) // p_offset + g.write64(segment_start) // p_vaddr addr:050 + g.write64(segment_start) // + g.file_size_pos = g.buf.len + g.write64(0) // p_filesz PLACEHOLDER, set to file_size later // addr: 060 + g.write64(0) // p_memsz + g.write64(0x1000) // p_align + // user code starts here at + // address: 00070 and a half + g.code_start_pos = g.buf.len + g.call(0) // call main function, it's not guaranteed to be the first +} + +pub fn (g mut Gen) generate_elf_footer() { + // Return 0 + g.mov(.edi, 0) // ret value + g.mov(.eax, 60) + g.syscall() + // Strings table + // Loop thru all strings and set the right addresses + for i, s in g.strings { + g.write64_at(segment_start + g.buf.len, int(g.str_pos[i])) + g.write_string(s) + g.write8(6) + } + // Now we know the file size, set it + 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") + // no 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.write64_at(int(g.main_fn_addr - g.code_start_pos) - 5, g.code_start_pos + 1) + // Create the binary + mut f := os.create(g.out_name)or{ + panic(err) + } + os.chmod(g.out_name, 0775) + f.write_bytes(g.buf.data, g.buf.len) + f.close() + println('x64 elf binary has been successfully generated') +} + diff --git a/vlib/v/gen/x64/elf_obj.v b/vlib/v/gen/x64/elf_obj.v new file mode 100644 index 0000000000..e1f90de6a5 --- /dev/null +++ b/vlib/v/gen/x64/elf_obj.v @@ -0,0 +1,159 @@ +// Copyright (c) 2019 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 x64 +/* +This file is unused right now, since binaries without sections +are generated. + +But it will be necessary once we have dynamic linking. +*/ + + +enum SectionType { + null = 0 + progbits = 1 + symtab = 2 + strtab = 3 + rela = 4 +} + +struct SectionConfig { + name string + typ SectionType + flags i64 + data voidptr + is_saa bool + datalen i64 + link int + info int + align i64 + entsize i64 +} + +fn (g mut Gen) section_header(c SectionConfig) { + g.write32(g.sect_header_name_pos) + g.sect_header_name_pos += c.name.len + 1 + g.write32(int(c.typ)) + g.write64(c.flags) + g.write64(0) // sh_addr + g.write64(g.offset) // offset + g.offset += c.datalen + 1 + g.write64(c.datalen) + g.write32(c.link) + g.write32(c.info) + g.write64(c.align) + g.write64(c.entsize) +} + +fn genobj() { + /* + // SHN_UNDEF + mut g := Gen{} + nr_sections := 7 + g.section_header(SectionConfig{ + name: '' + typ: .null + flags:0 + data: 0 + is_saa: false + link: 0 + info:0 + align:0 + entsize: 0 + }) + + /* + for sect in sections { + g.section_header(SectionConfig{ + name:0 + typ: sect.typ + flags: sect.flags + data: sect.data + is_saa: true + datalen: sect.len + link: 0 + info: 0 + align: sect.align + entsize: sect.entsize + }) + + } + */ + + g.section_header(SectionConfig{ + name: '.DATA' + typ: .progbits + flags: 0x2 + //data: sect.data + is_saa: true + datalen: 0xd + link: 0 + info: 0 + align: 1 + entsize: 0 + }) + + g.section_header(SectionConfig{ + name: '.TEXT' + typ: .progbits + flags: 0x2 + //data: sect.data + is_saa: true + datalen: 0xd + link: 0 + info: 0 + align: 1 + entsize: 0 + }) + g.section_header(SectionConfig{ + name: '.shstrtab' + typ: .strtab + flags: 0x2 + //data: sect.data + is_saa: true + datalen: 0x22 + link: 0 + info: 0 + align: 1 + entsize: 0 + }) + g.section_header(SectionConfig{ + name: '.symtab' + typ: .symtab + flags: 0x2 + //data: sect.data + is_saa: true + datalen: 0xd + link: 0 + info: 0 + align: 1 + entsize: 0 + }) + g.section_header(SectionConfig{ + name: '.strtab' + typ: .symtab + flags: 0x2 + //data: sect.data + is_saa: true + datalen: 0xd + link: 0 + info: 0 + align: 1 + entsize: 0 + }) + g.section_header(SectionConfig{ + name: '.rela.TEXT' + typ: .rela + flags: 0x0 + //data: sect.data + is_saa: true + datalen: 0x18 + link: 4 + info: 2 + align: 8 + entsize: 0x18 + }) + */ +} + diff --git a/vlib/v/gen/x64/gen.v b/vlib/v/gen/x64/gen.v new file mode 100644 index 0000000000..0ccf602e38 --- /dev/null +++ b/vlib/v/gen/x64/gen.v @@ -0,0 +1,457 @@ +// Copyright (c) 2019 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 x64 + +import ( + v.ast + term +) + +pub struct Gen { + out_name string +mut: + buf []byte + sect_header_name_pos int + offset i64 + str_pos []i64 + strings []string // TODO use a map and don't duplicate strings + file_size_pos i64 + main_fn_addr i64 + code_start_pos i64 // location of the start of the assembly instructions + fn_addr map[string]i64 + // string_addr map[string]i64 +} + +enum Register { + eax + edi + rax + rdi + rsi + edx + rdx + r12 +} + +enum Size { + _8 + _16 + _32 + _64 +} + +pub fn gen(files []ast.File, out_name string) { + mut g := Gen{ + // out: strings.new_builder(100) + sect_header_name_pos: 0 + buf: [] + out_name: out_name + } + g.generate_elf_header() + for file in files { + for stmt in file.stmts { + g.stmt(stmt) + g.writeln('') + } + } + g.generate_elf_footer() +} + +pub fn new_gen(out_name string) &Gen { + return &Gen{ + sect_header_name_pos: 0 + buf: [] + out_name: out_name + } +} + +pub fn (g &Gen) pos() i64 { + return g.buf.len +} + +fn (g mut Gen) write8(n int) { + // write 1 byte + g.buf << byte(n) +} + +fn (g mut Gen) write16(n int) { + // write 2 bytes + g.buf << byte(n) + g.buf << byte(n>>8) +} + +fn (g mut Gen) write32(n int) { + // write 4 bytes + g.buf << byte(n) + g.buf << byte(n>>8) + g.buf << byte(n>>16) + g.buf << byte(n>>24) +} + +fn (g mut Gen) write64(n i64) { + // write 8 bytes + g.buf << byte(n) + g.buf << byte(n>>8) + g.buf << byte(n>>16) + g.buf << byte(n>>24) + g.buf << byte(n>>32) + g.buf << byte(n>>40) + g.buf << byte(n>>48) + g.buf << byte(n>>56) +} + +fn (g mut Gen) write64_at(n i64, at i64) { + // write 8 bytes + g.buf[at] = byte(n) + g.buf[at + 1] = byte(n>>8) + g.buf[at + 2] = byte(n>>16) + g.buf[at + 3] = byte(n>>24) + g.buf[at + 4] = byte(n>>32) + g.buf[at + 5] = byte(n>>40) + g.buf[at + 6] = byte(n>>48) + g.buf[at + 7] = byte(n>>56) +} + +fn (g mut Gen) write_string(s string) { + for c in s { + g.write8(int(c)) + } +} + +fn (g mut Gen) inc(reg Register) { + g.write16(0xff49) + match reg { + .r12 { + g.write8(0xc4) + } + else { + panic('unhandled inc $reg') + } + } +} + +fn (g mut Gen) cmp(reg Register, size Size, val i64) { + g.write8(0x49) + // Second byte depends on the size of the value + match size { + ._8 { + g.write8(0x83) + } + ._32 { + g.write8(0x81) + } + else { + panic('unhandled cmp') + } + } + // Third byte depends on the register being compared to + match reg { + .r12 { + g.write8(0xfc) + } + else { + panic('unhandled cmp') + } + } + g.write8(int(val)) +} + +fn abs(a i64) i64 { + return if a < 0 { -a } else { a } +} + +fn (g mut Gen) jle(addr i64) { + // Calculate the relative offset to jump to + // (`addr` is absolute address) + offset := 0xff - int(abs(addr - g.buf.len)) - 1 + g.write8(0x7e) + g.write8(offset) +} + +fn (g mut Gen) jl(addr i64) { + offset := 0xff - int(abs(addr - g.buf.len)) - 1 + g.write8(0x7c) + g.write8(offset) +} + +fn (g &Gen) abs_to_rel_addr(addr i64) int { + return int(abs(addr - g.buf.len)) - 1 +} + +fn (g mut Gen) jmp(addr i64) { + offset := 0xff - g.abs_to_rel_addr(addr) + g.write8(0xe9) + g.write8(offset) +} + +fn (g mut Gen) mov64(reg Register, val i64) { + match reg { + .rsi { + g.write8(0x48) + g.write8(0xbe) + } + else { + println('unhandled mov $reg') + } + } + g.write64(val) +} + +fn (g mut Gen) call(addr int) { + // rel := g.abs_to_rel_addr(addr) + // rel := 0xffffffff - int(abs(addr - g.buf.len))-1 + println('call addr=$addr rel_addr=$addr pos=$g.buf.len') + g.write8(0xe8) + g.write32(addr) +} + +fn (g mut Gen) syscall() { + // g.write(0x050f) + g.write8(0x0f) + g.write8(0x05) +} + +pub fn (g mut Gen) ret() { + g.write8(0xc3) +} + +// returns label's relative address +pub fn (g mut Gen) gen_loop_start(from int) int { + g.mov(.r12, from) + label := g.buf.len + g.inc(.r12) + return label +} + +pub fn (g mut Gen) gen_loop_end(to int, label int) { + g.cmp(.r12, ._8, to) + g.jl(label) +} + +pub fn (g mut Gen) save_main_fn_addr() { + g.main_fn_addr = g.buf.len +} + +pub fn (g mut Gen) gen_print(s string) { + g.strings << s + '\n' + // g.string_addr[s] = str_pos + g.mov(.eax, 1) + g.mov(.edi, 1) + str_pos := g.buf.len + 2 + g.str_pos << str_pos + g.mov64(.rsi, 0) // segment_start + 0x9f) // str pos // PLACEHOLDER + g.mov(.edx, s.len + 1) // len + g.syscall() +} + +pub fn (g mut Gen) gen_exit() { + // Return 0 + g.mov(.edi, 0) // ret value + g.mov(.eax, 60) + g.syscall() +} + +fn (g mut Gen) mov(reg Register, val int) { + match reg { + .eax { + g.write8(0xb8) + } + .edi { + g.write8(0xbf) + } + .edx { + g.write8(0xba) + } + .rsi { + g.write8(0x48) + g.write8(0xbe) + } + .r12 { + g.write8(0x41) + g.write8(0xbc) // r11 is 0xbb etc + } + else { + panic('unhandled mov $reg') + } + } + g.write32(val) +} + +pub fn (g mut Gen) register_function_address(name string) { + addr := g.pos() + // println('reg fn addr $name $addr') + g.fn_addr[name] = addr +} + +pub fn (g &Gen) write(s string) {} + +pub fn (g &Gen) writeln(s string) {} + +pub fn (g mut Gen) call_fn(name string) { + if !name.contains('__') { + return + } + addr := g.fn_addr[name] + g.call(int(addr)) + println('call $name $addr') +} + +fn (g mut Gen) stmt(node ast.Stmt) { + match node { + ast.AssignStmt { + g.expr(it.left) + g.write(' $it.op.str() ') + g.expr(it.right) + g.writeln(';') + } + ast.FnDecl { + if it.name == 'main' { + g.write('int ${it.name}(') + } + else { + g.write('$it.typ.name ${it.name}(') + } + for arg in it.args { + g.write(arg.typ.name + ' ' + arg.name) + } + g.writeln(') { ') + for stmt in it.stmts { + g.stmt(stmt) + } + if it.name == 'main' { + g.writeln('return 0;') + } + g.writeln('}') + } + ast.Return { + g.write('return ') + g.expr(it.expr) + g.writeln(';') + } + ast.VarDecl { + g.write('$it.typ.name $it.name = ') + g.expr(it.expr) + g.writeln(';') + } + ast.ForStmt { + g.write('while (') + g.expr(it.cond) + g.writeln(') {') + for stmt in it.stmts { + g.stmt(stmt) + } + g.writeln('}') + } + ast.StructDecl { + g.writeln('typedef struct {') + for field in it.fields { + g.writeln('\t$field.typ.name $field.name;') + } + g.writeln('} $it.name;') + } + ast.ExprStmt { + g.expr(it.expr) + match it.expr { + // no ; after an if expression + ast.IfExpr {} + else { + g.writeln(';') + } + } + } + else { + verror('cgen.stmt(): bad node') + } + } +} + +fn (g mut Gen) expr(node ast.Expr) { + // println('cgen expr()') + match node { + ast.IntegerLiteral { + g.write(it.val.str()) + } + ast.FloatLiteral { + g.write(it.val) + } + ast.UnaryExpr { + g.expr(it.left) + g.write(it.op.str()) + } + ast.StringLiteral { + g.write('tos3("$it.val")') + } + ast.BinaryExpr { + g.expr(it.left) + g.write(' $it.op.str() ') + g.expr(it.right) + // if typ.name != typ2.name { + // verror('bad types $typ.name $typ2.name') + // } + } + // `user := User{name: 'Bob'}` + ast.StructInit { + g.writeln('($it.typ.name){') + for i, field in it.fields { + g.write('\t.$field = ') + g.expr(it.exprs[i]) + g.writeln(', ') + } + g.write('}') + } + ast.CallExpr { + g.write('${it.name}(') + for i, expr in it.args { + g.expr(expr) + if i != it.args.len - 1 { + g.write(', ') + } + } + g.write(')') + } + ast.ArrayInit { + g.writeln('new_array_from_c_array($it.exprs.len, $it.exprs.len, sizeof($it.typ.name), {\t') + for expr in it.exprs { + g.expr(expr) + g.write(', ') + } + g.write('\n})') + } + ast.Ident { + g.write('$it.name') + } + ast.BoolLiteral { + if it.val == true { + g.write('true') + } + else { + g.write('false') + } + } + ast.IfExpr { + g.write('if (') + g.expr(it.cond) + g.writeln(') {') + for stmt in it.stmts { + g.stmt(stmt) + } + g.writeln('}') + if it.else_stmts.len > 0 { + g.writeln('else { ') + for stmt in it.else_stmts { + g.stmt(stmt) + } + g.writeln('}') + } + } + else { + println(term.red('cgen.expr(): bad node')) + } + } +} + +fn verror(s string) { + println(s) + exit(1) +} diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 65eb8b44a8..7fe87f05d5 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -15,6 +15,7 @@ import ( struct Parser { scanner &scanner.Scanner + file_name string mut: tok token.Token peek_tok token.Token @@ -47,6 +48,9 @@ pub fn (p mut Parser) get_type() types.Type { 'string' { return types.string_type } + 'voidptr' { + return types.voidptr_type + } else { typ := p.table.types[p.tok.lit] if isnil(typ.name.str) || typ.name == '' { @@ -86,10 +90,13 @@ pub fn parse_files(paths []string, table &table.Table) []ast.File { mut files := []ast.File for path in paths { mut stmts := []ast.Stmt - text := os.read_file(path) or { panic(err) } + text := os.read_file(path) or { + panic(err) + } mut p := Parser{ scanner: scanner.new_scanner(text) table: table + file_name: path } p.read_first_token() for { @@ -118,6 +125,7 @@ pub fn (p mut Parser) read_first_token() { } pub fn (p mut Parser) parse_block() []ast.Stmt { + p.check(.lcbr) mut stmts := []ast.Stmt for { // res := s.scan() @@ -127,7 +135,7 @@ pub fn (p mut Parser) parse_block() []ast.Stmt { // println('expr at ' + p.tok.str()) stmts << p.stmt() } - p.next() + p.check(.rcbr) // println('nr exprs in block = $exprs.len') return stmts } @@ -170,6 +178,29 @@ pub fn (p mut Parser) stmt() ast.Stmt { .key_import { return p.import_stmt() } + .key_pub { + match p.peek_tok.kind { + .key_fn { + return p.fn_decl() + } + .key_struct, .key_union, .key_interface { + return p.struct_decl() + } + else { + p.error('wrong pub keyword usage') + return ast.Stmt{} + } + } + // .key_const { + // return p.const_decl() + // } + // .key_enum { + // return p.enum_decl() + // } + // .key_type { + // return p.type_decl() + // } + } .key_fn { return p.fn_decl() } @@ -220,7 +251,7 @@ pub fn (p mut Parser) assign_stmt() ast.AssignStmt { } pub fn (p &Parser) error(s string) { - println(term.bold(term.red('x.v:$p.tok.line_nr: $s'))) + println(term.bold(term.red('$p.file_name:$p.tok.line_nr: $s'))) exit(1) } @@ -292,6 +323,7 @@ pub fn (p mut Parser) expr(rbp int) (ast.Expr,types.Type) { p.check(.colon) // expr,field_type := p.expr(0) expr,_ := p.expr(0) + // if !types.check( ,field_type exprs << expr } node = ast.StructInit{ @@ -337,17 +369,17 @@ pub fn (p mut Parser) expr(rbp int) (ast.Expr,types.Type) { } else { if p.tok.is_unary() { - pt := p.tok - p.next() - expr,t2 := p.expr(token.lowest_prec) - node = ast.UnaryExpr{ - left: expr - op: pt.kind - } - typ = t2 + pt := p.tok + p.next() + expr,t2 := p.expr(token.lowest_prec) + node = ast.UnaryExpr{ + left: expr + op: pt.kind + } + typ = t2 } else { - verror('!unknown token ' + p.tok.str()) + p.error('!unknown token ' + p.tok.str()) } } } @@ -378,21 +410,22 @@ pub fn (p mut Parser) expr(rbp int) (ast.Expr,types.Type) { left: node op: prev_tok.kind } - } else { + } + else { mut expr := ast.Expr{} expr,t2 = p.expr(prev_tok.precedence() - 1) if prev_tok.is_relational() { typ = types.bool_type } else { - typ = t2 + typ = t2 } // println(t2.name + '222') node = ast.BinaryExpr{ left: node op: prev_tok.kind right: expr - } + } } } } @@ -405,7 +438,6 @@ fn (p mut Parser) for_statement() ast.ForStmt { if !types.check(types.bool_type, typ) { p.error('non-bool used as for condition') } - p.check(.lcbr) stmts := p.parse_block() return ast.ForStmt{ cond: cond @@ -420,11 +452,17 @@ fn (p mut Parser) if_expr() (ast.Expr,types.Type) { if !types.check(types.bool_type, typ) { p.error('non-bool used as if condition') } - p.check(.lcbr) stmts := p.parse_block() + mut else_stmts := []ast.Stmt + if p.tok.kind == .key_else { + println('GOT ELSE') + p.check(.key_else) + else_stmts = p.parse_block() + } node = ast.IfExpr{ cond: cond stmts: stmts + else_stmts: else_stmts } return node,types.void_type } @@ -495,17 +533,27 @@ fn (p mut Parser) module_decl() ast.Module { } fn (p mut Parser) import_stmt() ast.Import { - // p.check(.key_import) - p.next() - return ast.Import{} + p.check(.key_import) + name := p.check_name() + return ast.Import{ + mods: [name] + } } fn (p mut Parser) struct_decl() ast.StructDecl { + is_pub := p.tok.kind == .key_pub + if is_pub { + p.next() + } p.check(.key_struct) name := p.check_name() p.check(.lcbr) mut fields := []ast.Field for p.tok.kind != .rcbr { + if p.tok.kind == .key_pub { + p.check(.key_pub) + p.check(.colon) + } field_name := p.check_name() typ := p.get_type() fields << ast.Field{ @@ -519,6 +567,7 @@ fn (p mut Parser) struct_decl() ast.StructDecl { }) return ast.StructDecl{ name: name + is_pub: is_pub fields: fields } } @@ -543,6 +592,9 @@ fn (p mut Parser) fn_decl() ast.FnDecl { typ: typ name: arg_name } + if p.tok.kind != .rpar { + p.check(.comma) + } } p.check(.rpar) // Return type @@ -551,7 +603,6 @@ fn (p mut Parser) fn_decl() ast.FnDecl { typ = p.get_type() p.return_type = typ } - p.check(.lcbr) p.table.register_fn(table.Fn{ name: name args: args @@ -602,7 +653,7 @@ fn (p mut Parser) var_decl() ast.VarDecl { return ast.VarDecl{ name: name expr: expr // p.expr(token.lowest_prec) - + typ: t } } diff --git a/vlib/v/token/token.v b/vlib/v/token/token.v index 783e2cad94..1d26318481 100644 --- a/vlib/v/token/token.v +++ b/vlib/v/token/token.v @@ -362,7 +362,7 @@ pub fn (tok Token) is_unary() bool { pub fn (tok Token) is_left_assoc() bool { return tok.kind in [ // .number, - // `++` | `--` + // `++` | `--` .inc, .dec, // `*` | `/` | `%` .mul, .div, .mod, @@ -371,7 +371,7 @@ pub fn (tok Token) is_left_assoc() bool { // `==` | `!=` .eq, .ne, // `<` | `<=` | `>` | `>=` - .lt, .le, .gt, .ge, + .lt, .le, .gt, .ge, .ne, .eq, // `,` .comma] } @@ -392,5 +392,5 @@ pub fn (tok Token) is_right_assoc() bool { pub fn (tok Token) is_relational() bool { return tok.kind in [ // `<` | `<=` | `>` | `>=` - .lt, .le, .gt, .ge] + .lt, .le, .gt, .ge, .eq, .ne] } diff --git a/vlib/v/types/types.v b/vlib/v/types/types.v index 34e5cc5283..609ac95d0a 100644 --- a/vlib/v/types/types.v +++ b/vlib/v/types/types.v @@ -20,6 +20,8 @@ pub const ( 'f64',3} bool_type = Type{ 'bool',4} + voidptr_type = Type{ + 'voidptr',5} ) pub fn check(got, expected &Type) bool {