From 52d25336db081f6f2c4798d1912ef22356930e01 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 27 Nov 2019 09:01:25 +0300 Subject: [PATCH] x64: function calls; http: download_file() --- examples/x64/hello_world.v | 5 ++++ vlib/compiler/fn.v | 11 +++++++ vlib/compiler/main.v | 1 - vlib/compiler/match.v | 2 +- vlib/compiler/x64/elf.v | 8 +++++ vlib/compiler/x64/gen.v | 36 ++++++++++++++++++++-- vlib/http/download.v | 14 +++++++++ vlib/http/download_nix.v | 58 +++++++++++++++++------------------- vlib/http/download_windows.v | 2 ++ 9 files changed, 102 insertions(+), 35 deletions(-) create mode 100644 vlib/http/download.v diff --git a/examples/x64/hello_world.v b/examples/x64/hello_world.v index 5c77122f7f..bd47cd8611 100644 --- a/examples/x64/hello_world.v +++ b/examples/x64/hello_world.v @@ -1,6 +1,11 @@ fn main() { + println('x64 test') for _ in 0..5 { println('Hello world from V x64 machine code generator!') } println('Hello again!') } + +fn test_fn() { + println('test fn') +} diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index 53521ff623..bf89ac1ad5 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -44,6 +44,7 @@ mut: fn_name_token_idx int // used by error reporting comptime_define string is_used bool // so that we can skip unused fns in resulting C code + //x64_addr i64 // address in the generated x64 binary } struct TypeInst { @@ -397,6 +398,9 @@ fn (p mut Parser) fn_decl() { str_args := f.str_args(p.table) // 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() + } 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) } @@ -525,6 +529,12 @@ fn (p mut Parser) fn_decl() { //p.genln('// live_function body end') p.genln('pthread_mutex_unlock(&live_fn_mutex);') } + if p.pref.x64 && f.name == 'main__main' && !p.first_pass() { + p.x64.gen_exit() + } + if p.pref.x64 && !p.first_pass() { + p.x64.ret() + } // {} closed correctly? scope_level should be 0 if p.mod == 'main' { // println(p.cur_fn.scope_level) @@ -541,6 +551,7 @@ fn (p mut Parser) fn_decl() { p.check_unused_variables() p.set_current_fn( EmptyFn ) p.returns = false + } [inline] diff --git a/vlib/compiler/main.v b/vlib/compiler/main.v index 0b1c66d924..c176fde61f 100644 --- a/vlib/compiler/main.v +++ b/vlib/compiler/main.v @@ -367,7 +367,6 @@ pub fn (v mut V) compile_x64() { v.parse(f, .main) } v.x64.generate_elf_footer() - } fn (v mut V) generate_init() { diff --git a/vlib/compiler/match.v b/vlib/compiler/match.v index 6b72943357..65abd2c7ae 100644 --- a/vlib/compiler/match.v +++ b/vlib/compiler/match.v @@ -167,7 +167,7 @@ fn (p mut Parser) match_statement(is_expr bool) string { p.gen(')') if p.tok == .arrow { - p.warn(warn_match_arrow) + p.error(warn_match_arrow) p.check(.arrow) } diff --git a/vlib/compiler/x64/elf.v b/vlib/compiler/x64/elf.v index 277de86dd6..bef5cac00d 100644 --- a/vlib/compiler/x64/elf.v +++ b/vlib/compiler/x64/elf.v @@ -69,6 +69,8 @@ pub fn (g mut Gen) generate_elf_header() { 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() { @@ -87,6 +89,12 @@ pub fn (g mut 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") + // 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 f := os.create(g.out_name) or { panic(err) } os.chmod(g.out_name, 0775) diff --git a/vlib/compiler/x64/gen.v b/vlib/compiler/x64/gen.v index 602a01f95f..6c49f64b17 100644 --- a/vlib/compiler/x64/gen.v +++ b/vlib/compiler/x64/gen.v @@ -12,8 +12,9 @@ mut: offset i64 str_pos []i64 strings []string // TODO use a map and don't duplicate strings - //str string file_size_pos i64 + main_fn_addr i64 + code_start_pos i64 // location of the start of the assembly instructions //string_addr map[string]i64 } @@ -120,11 +121,29 @@ fn (g mut Gen) cmp(reg Register, size Size, val i64) { 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 { @@ -137,7 +156,9 @@ fn (g mut Gen) mov64(reg Register, val i64) { } fn (g mut Gen) call(val int) { + //println('call val=$val') g.write8(0xe8) + g.write32(val) } fn (g mut Gen) syscall() { @@ -146,7 +167,7 @@ fn (g mut Gen) syscall() { g.write8(0x05) } -fn (g mut Gen) ret() { +pub fn (g mut Gen) ret() { g.write8(0xc3) } @@ -163,6 +184,10 @@ pub fn (g mut Gen) gen_loop_end(to int, label int) { g.jle(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 @@ -175,6 +200,13 @@ pub fn (g mut Gen) gen_print(s string) { 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) } diff --git a/vlib/http/download.v b/vlib/http/download.v new file mode 100644 index 0000000000..388177f27a --- /dev/null +++ b/vlib/http/download.v @@ -0,0 +1,14 @@ +// 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 http + +import os + +fn download_file(url, out string) bool { + s := http.get(url) or { return false } + os.write_file(out, s.text) + return true + //download_file_with_progress(url, out, empty, empty) +} diff --git a/vlib/http/download_nix.v b/vlib/http/download_nix.v index 07b0a2b67a..e0b22da3dd 100644 --- a/vlib/http/download_nix.v +++ b/vlib/http/download_nix.v @@ -5,57 +5,53 @@ module http type downloadfn fn (written int) -type download_finished_fn fn () +type download_finished_fn fn () -/* +/* struct DownloadStruct { -mut: +mut: stream voidptr written int cb downloadfn } -*/ +*/ -fn download_cb(ptr voidptr, size, nmemb size_t, userp voidptr) { -/* +fn download_cb(ptr voidptr, size, nmemb size_t, userp voidptr) { +/* mut data := &DownloadStruct(userp) - written := C.fwrite(ptr, size, nmemb, data.stream) - data.written += written - data.cb(data.written) - //#data->cb(data->written); // TODO - return written -*/ + written := C.fwrite(ptr, size, nmemb, data.stream) + data.written += written + data.cb(data.written) + //#data->cb(data->written); // TODO + return written +*/ } -fn download_file_with_progress(url, out string, cb downloadfn, cb_finished fn()) { -/* +fn download_file_with_progress(url, out string, cb downloadfn, cb_finished fn()) { +/* curl := C.curl_easy_init() if isnil(curl) { return } - cout := out.str - fp := C.fopen(cout, 'wb') - C.curl_easy_setopt(curl, CURLOPT_URL, url.str) + cout := out.str + fp := C.fopen(cout, 'wb') + C.curl_easy_setopt(curl, CURLOPT_URL, url.str) C.curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download_cb) data := &DownloadStruct { stream:fp cb: cb } - C.curl_easy_setopt(curl, CURLOPT_WRITEDATA, data) - mut d := 0.0 - C.curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d) - C.curl_easy_perform(curl) - C.curl_easy_cleanup(curl) - C.fclose(fp) - cb_finished() -*/ -} - -fn download_file(url, out string) { - //download_file_with_progress(url, out, empty, empty) + C.curl_easy_setopt(curl, CURLOPT_WRITEDATA, data) + mut d := 0.0 + C.curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d) + C.curl_easy_perform(curl) + C.curl_easy_cleanup(curl) + C.fclose(fp) + cb_finished() +*/ } fn empty() { - -} + +} diff --git a/vlib/http/download_windows.v b/vlib/http/download_windows.v index d64b88dd07..dc1f05ad4e 100644 --- a/vlib/http/download_windows.v +++ b/vlib/http/download_windows.v @@ -11,6 +11,7 @@ module http fn download_file_with_progress(url, out string, cb, cb_finished voidptr) { } +/* pub fn download_file(url, out string) { C.URLDownloadToFile(0, url.to_wide(), out.to_wide(), 0, 0) /* @@ -25,3 +26,4 @@ pub fn download_file(url, out string) { # } */ } +*/