From 14434cc86aa1ca2a98c023313db3f0ca2a136537 Mon Sep 17 00:00:00 2001 From: crthpl <56052645+crthpl@users.noreply.github.com> Date: Tue, 13 Apr 2021 22:50:50 -0700 Subject: [PATCH] all: bare metal support (fix `-freestanding`) (#9624) --- cmd/tools/vfmt.v | 2 +- cmd/tools/vrepl.v | 2 +- cmd/tools/vtest-cleancode.v | 1 + cmd/tools/vtest-fmt.v | 4 +- cmd/v/help/build-c.txt | 41 ++ cmd/v/v.v | 2 +- examples/binary_search_tree.v | 58 +- vlib/builtin/array.v | 6 +- vlib/builtin/bare/builtin.v | 5 - vlib/builtin/builtin.c.v | 119 ++-- vlib/builtin/builtin.v | 10 +- vlib/builtin/builtin_nix.c.v | 94 +-- vlib/builtin/builtin_windows.c.v | 9 +- vlib/builtin/linux_bare/libc_impl.v | 130 ++++ vlib/builtin/linux_bare/linux_syscalls.v | 431 +++++++++++++ vlib/builtin/linux_bare/memory_managment.v | 29 + .../linux_bare/{ => old}/.checks/.gitignore | 0 .../linux_bare/{ => old}/.checks/checks.v | 0 .../{ => old}/.checks/consts/consts.v | 0 .../{ => old}/.checks/forkedtest/forkedtest.v | 0 .../{ => old}/.checks/linuxsys/linuxsys.v | 0 .../linux_bare/{ => old}/.checks/readme.md | 0 .../{ => old}/.checks/sample_text1.txt | 0 .../{ => old}/.checks/string/string.v | 0 .../{ => old}/.checks/structs/structs.v | 0 .../builtin/linux_bare/{ => old}/array_bare.v | 0 .../linux_bare/{ => old}/builtin_bare.v | 0 .../linux_bare/{ => old}/linuxsys_bare.v | 0 vlib/builtin/linux_bare/{ => old}/mm_bare.v | 0 .../linux_bare/{ => old}/string_bare.v | 0 .../{ => old}/syscallwrapper_test.v | 0 vlib/builtin/string.v | 18 +- vlib/os/os.v | 12 + vlib/term/term.v | 4 +- vlib/term/term_nix.c.v | 6 +- vlib/term/term_windows.c.v | 4 +- vlib/term/ui/termios_nix.c.v | 2 +- vlib/v/checker/checker.v | 29 +- .../tests/asm_alias_does_not_exist.out | 5 - .../checker/tests/asm_alias_does_not_exist.vv | 3 - vlib/v/gen/c/cgen.v | 7 +- vlib/v/gen/c/cheaders.v | 591 ++++++++++++------ vlib/v/gen/c/str.v | 131 +--- vlib/v/parser/parser.v | 6 + vlib/v/pref/pref.v | 3 + vlib/v/pref/should_compile.v | 34 +- 46 files changed, 1316 insertions(+), 482 deletions(-) delete mode 100644 vlib/builtin/bare/builtin.v create mode 100644 vlib/builtin/linux_bare/libc_impl.v create mode 100644 vlib/builtin/linux_bare/linux_syscalls.v create mode 100644 vlib/builtin/linux_bare/memory_managment.v rename vlib/builtin/linux_bare/{ => old}/.checks/.gitignore (100%) rename vlib/builtin/linux_bare/{ => old}/.checks/checks.v (100%) rename vlib/builtin/linux_bare/{ => old}/.checks/consts/consts.v (100%) rename vlib/builtin/linux_bare/{ => old}/.checks/forkedtest/forkedtest.v (100%) rename vlib/builtin/linux_bare/{ => old}/.checks/linuxsys/linuxsys.v (100%) rename vlib/builtin/linux_bare/{ => old}/.checks/readme.md (100%) rename vlib/builtin/linux_bare/{ => old}/.checks/sample_text1.txt (100%) rename vlib/builtin/linux_bare/{ => old}/.checks/string/string.v (100%) rename vlib/builtin/linux_bare/{ => old}/.checks/structs/structs.v (100%) rename vlib/builtin/linux_bare/{ => old}/array_bare.v (100%) rename vlib/builtin/linux_bare/{ => old}/builtin_bare.v (100%) rename vlib/builtin/linux_bare/{ => old}/linuxsys_bare.v (100%) rename vlib/builtin/linux_bare/{ => old}/mm_bare.v (100%) rename vlib/builtin/linux_bare/{ => old}/string_bare.v (100%) rename vlib/builtin/linux_bare/{ => old}/syscallwrapper_test.v (100%) delete mode 100644 vlib/v/checker/tests/asm_alias_does_not_exist.out delete mode 100644 vlib/v/checker/tests/asm_alias_does_not_exist.vv diff --git a/cmd/tools/vfmt.v b/cmd/tools/vfmt.v index ececbe9f6c..c99860217e 100644 --- a/cmd/tools/vfmt.v +++ b/cmd/tools/vfmt.v @@ -80,7 +80,7 @@ fn main() { verror(err.msg) return } - if is_atty(0) == 0 && files.len == 0 { + if os.is_atty(0) == 0 && files.len == 0 { foptions.format_pipe() exit(0) } diff --git a/cmd/tools/vrepl.v b/cmd/tools/vrepl.v index 43bbb94aa0..faa09aeba3 100644 --- a/cmd/tools/vrepl.v +++ b/cmd/tools/vrepl.v @@ -26,7 +26,7 @@ mut: vstartup_lines []string // lines in the `VSTARTUP` file } -const is_stdin_a_pipe = (is_atty(0) == 0) +const is_stdin_a_pipe = (os.is_atty(0) == 0) const vexe = os.getenv('VEXE') diff --git a/cmd/tools/vtest-cleancode.v b/cmd/tools/vtest-cleancode.v index 82da52603c..176f5993f3 100644 --- a/cmd/tools/vtest-cleancode.v +++ b/cmd/tools/vtest-cleancode.v @@ -35,6 +35,7 @@ const ( 'vlib/v/tests/generics_test.v', /* multi_generic_args, Foo >(...) becomes .... Foo>(...) which does not parse */ 'vlib/v/tests/string_interpolation_test.v' /* TODO byteptr: &byte.str() behaves differently than byteptr.str() */, 'vlib/v/gen/js/tests/js.v', /* local `hello` fn, gets replaced with module `hello` aliased as `hl` */ + 'vlib/v/gen/c/cheaders.v' /* the preprocessor directives are formated to the V standard, even though they are in a string literal */, ] vfmt_verify_list = [ 'cmd/', diff --git a/cmd/tools/vtest-fmt.v b/cmd/tools/vtest-fmt.v index e83d6827af..5dcdf0c4ab 100644 --- a/cmd/tools/vtest-fmt.v +++ b/cmd/tools/vtest-fmt.v @@ -15,12 +15,12 @@ const ( 'vlib/builtin/js/jsfns_node.js.v', 'vlib/builtin/js/jsfns.js.v', 'vlib/builtin/js/jsfns_browser.js.v', - // error: expr(): bad token `asm`, on `asm {}` - 'vlib/builtin/bare/linuxsys_bare.v', // total chaos (duplicated code several times) in array_eq_test.v 'vlib/builtin/array_eq_test.v', // the fn args are removed, then `cb fn (picohttpparser.Request, mut picohttpparser.Response)` can not be reparsed 'vlib/picoev/picoev.v', + // the preprocessor directives are formated to the V standard, even though they are in a string literal + 'vlib/v/gen/c/cheaders.v', ] ) diff --git a/cmd/v/help/build-c.txt b/cmd/v/help/build-c.txt index d6890e6cbe..ce432d6269 100644 --- a/cmd/v/help/build-c.txt +++ b/cmd/v/help/build-c.txt @@ -24,6 +24,47 @@ see also `v help build`. Build the executable without dependency on libc. Supported only on `linux` targets currently. + -bare-builtin-dir + Use with `-freestanding`. This specifies the directory to the + implementation of some basic builtin functions. The list is as follows: + memcpy(dest &byte, src &byte, n int) &byte + Moves n bytes from dest to src, and returns dest + [export: 'malloc'] + __malloc(n int) &byte + Allocates n bytes of memory and returns the pointer to the first byte + strlen(s &byte) int + Returns the amount of bytes until the first `0`, starting at s + realloc(old_area &byte, new_size int) &byte + Allocates a new area of size new_size, copies old_area + to the new area, and returns a pointer to the new area. + memset(s &byte, c int, n int) &byte + Sets n bytes starting at s to c (c is casted to a char) + and returns s. + memmove(dest &byte, src &byte, n int) &byte + Like memcpy, but guaranteed to work if dest and src overlap. + [export: 'calloc'] + __calloc(nmemb int, size int) &byte + Like malloc, but sets all the bytes to `0` first. + getchar() int + Read one character from stdin and return it. + memcmp(a &byte, b &byte, n int) int + Compare two buffers of length n. If a and b are equal, return 0. + Otherwise, return the difference between the first different letter. + [export: 'free'] + __free(ptr &byte) + Free the block of memory ptr allocated by malloc. + vsprintf(str &char, format &char, ap va_list) int + See `man vsprintf`. + vsnprintf(str &char, size int, format &char, ap va_list) int + See `man vsnprintf`. + bare_print(buf &byte, len u64) + Print len charecters from the buffer pointed to by buf to stdout. + bare_eprint(buf &byte, len u64) + Print len charecters from the buffer pointed to by buf to stderr. + + The module decleration should be `builtin`. The default linux + implementation can be found in `vlib/builtin/linux_bare`. + -os , -target-os Change the target OS that V tries to compile for. By default, the target OS is the host system. diff --git a/cmd/v/v.v b/cmd/v/v.v index 40bf682d9f..3550c53d29 100644 --- a/cmd/v/v.v +++ b/cmd/v/v.v @@ -58,7 +58,7 @@ fn main() { if args.len == 0 || args[0] in ['-', 'repl'] { // Running `./v` without args launches repl if args.len == 0 { - if is_atty(0) != 0 { + if os.is_atty(0) != 0 { println('Welcome to the V REPL (for help with V itself, type `exit`, then run `v help`).') } else { mut args_and_flags := util.join_env_vflags_and_os_args()[1..].clone() diff --git a/examples/binary_search_tree.v b/examples/binary_search_tree.v index 34dbe6ab13..bf8b38dc78 100644 --- a/examples/binary_search_tree.v +++ b/examples/binary_search_tree.v @@ -1,7 +1,7 @@ // Binary Search Tree example by @SleepyRoy // TODO: make Node.value generic once it's robust enough -// TODO: `return match` instead of returns everywhere inside match +// TODO: `return match` instead of returns everywhere inside match struct Empty {} @@ -121,22 +121,46 @@ fn delete(tree Tree, x f64) Tree { } fn main() { - mut tree := Tree(Empty{}) - input := [0.3, 0.2, 0.5, 0.0, 0.6, 0.8, 0.9, 1.0, 0.1, 0.4, 0.7] - for i in input { - tree = insert(tree, i) - } - println('[1] after insertion tree size is ${size(tree)}') // 11 - del := [-0.3, 0.0, 0.3, 0.6, 1.0, 1.5] - for i in del { - tree = delete(tree, i) - } - print('[2] after deletion tree size is ${size(tree)}, ') // 7 - print('and these elements were deleted: ') // 0.0 0.3 0.6 1.0 - for i in input { - if !search(tree, i) { - print('$i ') + $if !freestanding { + mut tree := Tree(Empty{}) + input := [0.3, 0.2, 0.5, 0.0, 0.6, 0.8, 0.9, 1.0, 0.1, 0.4, 0.7] + for i in input { + tree = insert(tree, i) } + println('[1] after insertion tree size is ${size(tree)}') // 11 + del := [-0.3, 0.0, 0.3, 0.6, 1.0, 1.5] + for i in del { + tree = delete(tree, i) + } + print('[2] after deletion tree size is ${size(tree)}, ') // 7 + print('and these elements were deleted: ') // 0.0 0.3 0.6 1.0 + for i in input { + if !search(tree, i) { + print('$i ') + } + } + println('') + } $else { + mut tree := Tree(Empty{}) + input := [0.3, 0.2, 0.5, 0.0, 0.6, 0.8, 0.9, 1.0, 0.1, 0.4, 0.7] + for i in input { + tree = insert(tree, i) + } + print('[1] after insertion tree size is ') // 11 + println(size(tree)) + del := [-0.3, 0.0, 0.3, 0.6, 1.0, 1.5] + for i in del { + tree = delete(tree, i) + } + print('[2] after deletion tree size is ') // 7 + print(size(tree)) + print(', and these elements were deleted: ') // 0.0 0.3 0.6 1.0 + for i in input { + if !search(tree, i) { + print(i) + print(' ') + } + } + println('') } - println('') } diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index 0ade62c176..cf503c77e0 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -136,7 +136,11 @@ pub fn (a array) repeat(count int) array { // sort_with_compare sorts array in-place using given `compare` function as comparator. pub fn (mut a array) sort_with_compare(compare voidptr) { - C.qsort(mut a.data, a.len, a.element_size, compare) + $if freestanding { + panic('sort does not work with -freestanding') + } $else { + C.qsort(mut a.data, a.len, a.element_size, compare) + } } // insert inserts a value in the array at index `i` diff --git a/vlib/builtin/bare/builtin.v b/vlib/builtin/bare/builtin.v deleted file mode 100644 index d64aa2be1b..0000000000 --- a/vlib/builtin/bare/builtin.v +++ /dev/null @@ -1,5 +0,0 @@ -module builtin - -struct string { - len int -} diff --git a/vlib/builtin/builtin.c.v b/vlib/builtin/builtin.c.v index 40f29c3421..af2a872674 100644 --- a/vlib/builtin/builtin.c.v +++ b/vlib/builtin/builtin.c.v @@ -18,12 +18,17 @@ fn panic_debug(line_no int, file string, mod string, fn_name string, s string) { // module is less likely to change than function, etc... // During edits, the line number will change most frequently, // so it is last - eprintln('================ V panic ================') - eprintln(' module: $mod') - eprintln(' function: ${fn_name}()') - eprintln(' message: $s') - eprintln(' file: $file:$line_no') - eprintln('=========================================') + $if !freestanding { + eprintln('================ V panic ================') + eprintln(' module: $mod') + eprintln(' function: ${fn_name}()') + eprintln(' message: $s') + eprintln(' file: $file:$line_no') + eprintln('=========================================') + } $else { + eprint('V panic: ') + eprintln(s) + } $if exit_after_panic_message ? { C.exit(1) } $else { @@ -38,7 +43,9 @@ fn panic_debug(line_no int, file string, mod string, fn_name string, s string) { } C.exit(1) } - print_backtrace_skipping_top_frames(1) + $if !freestanding { + print_backtrace_skipping_top_frames(1) + } $if panics_break_into_debugger ? { break_if_debugger_attached() } @@ -54,7 +61,8 @@ pub fn panic_optional_not_set(s string) { // panic prints a nice error message, then exits the process with exit code of 1. // It also shows a backtrace on most platforms. pub fn panic(s string) { - eprintln('V panic: $s') + eprint('V panic: ') + eprintln(s) $if exit_after_panic_message ? { C.exit(1) } $else { @@ -69,7 +77,9 @@ pub fn panic(s string) { } C.exit(1) } - print_backtrace_skipping_top_frames(1) + $if !freestanding { + print_backtrace_skipping_top_frames(1) + } $if panics_break_into_debugger ? { break_if_debugger_attached() } @@ -80,14 +90,13 @@ pub fn panic(s string) { // eprintln prints a message with a line end, to stderr. Both stderr and stdout are flushed. pub fn eprintln(s string) { - C.fflush(C.stdout) - C.fflush(C.stderr) - // eprintln is used in panics, so it should not fail at all - $if android { + $if freestanding { + // flushing is only a thing with C.FILE from stdio.h, not on the syscall level if s.str == 0 { - C.fprintf(C.stderr, c'eprintln(NIL)\n') + bare_eprint(c'eprintln(NIL)\n', 14) } else { - C.fprintf(C.stderr, c'%.*s\n', s.len, s.str) + bare_eprint(s.str, u64(s.len)) + bare_eprint(c'\n', 1) } } $else $if ios { if s.str == 0 { @@ -96,25 +105,35 @@ pub fn eprintln(s string) { C.WrappedNSLog(s.str) } } $else { - if s.str == 0 { - C.write(2, c'eprintln(NIL)\n', 14) - } else { - C.write(2, s.str, s.len) - C.write(2, c'\n', 1) + C.fflush(C.stdout) + C.fflush(C.stderr) + // eprintln is used in panics, so it should not fail at all + $if android { + if s.str == 0 { + C.fprintf(C.stderr, c'eprintln(NIL)\n') + } else { + C.fprintf(C.stderr, c'%.*s\n', s.len, s.str) + } + } $else { + if s.str == 0 { + C.write(2, c'eprintln(NIL)\n', 14) + } else { + C.write(2, s.str, s.len) + C.write(2, c'\n', 1) + } } + C.fflush(C.stderr) } - C.fflush(C.stderr) } // eprint prints a message to stderr. Both stderr and stdout are flushed. pub fn eprint(s string) { - C.fflush(C.stdout) - C.fflush(C.stderr) - $if android { + $if freestanding { + // flushing is only a thing with C.FILE from stdio.h, not on the syscall level if s.str == 0 { - C.fprintf(C.stderr, c'eprint(NIL)') + bare_eprint(c'eprint(NIL)\n', 12) } else { - C.fprintf(C.stderr, c'%.*s', s.len, s.str) + bare_eprint(s.str, u64(s.len)) } } $else $if ios { // TODO: Implement a buffer as NSLog doesn't have a "print" @@ -124,13 +143,23 @@ pub fn eprint(s string) { C.WrappedNSLog(s.str) } } $else { - if s.str == 0 { - C.write(2, c'eprint(NIL)', 11) - } else { - C.write(2, s.str, s.len) + C.fflush(C.stdout) + C.fflush(C.stderr) + $if android { + if s.str == 0 { + C.fprintf(C.stderr, c'eprint(NIL)') + } else { + C.fprintf(C.stderr, c'%.*s', s.len, s.str) + } + } $else { + if s.str == 0 { + C.write(2, c'eprint(NIL)', 11) + } else { + C.write(2, s.str, s.len) + } } + C.fflush(C.stderr) } - C.fflush(C.stderr) } // print prints a message to stdout. Unlike `println` stdout is not automatically flushed. @@ -141,6 +170,8 @@ pub fn print(s string) { } $else $if ios { // TODO: Implement a buffer as NSLog doesn't have a "print" C.WrappedNSLog(s.str) + } $else $if freestanding { + bare_print(s.str, u64(s.len)) } $else { C.write(1, s.str, s.len) } @@ -160,6 +191,9 @@ pub fn println(s string) { C.fprintf(C.stdout, c'println(NIL)\n') } $else $if ios { C.WrappedNSLog(c'println(NIL)') + } $else $if freestanding { + bare_print(s.str, u64(s.len)) + bare_print(c'println(NIL)\n', 13) } $else { C.write(1, c'println(NIL)\n', 13) } @@ -169,6 +203,9 @@ pub fn println(s string) { C.fprintf(C.stdout, c'%.*s\n', s.len, s.str) } $else $if ios { C.WrappedNSLog(s.str) + } $else $if freestanding { + bare_print(s.str, u64(s.len)) + bare_print(c'\n', 1) } $else { C.write(1, s.str, s.len) C.write(1, c'\n', 1) @@ -205,6 +242,14 @@ pub fn malloc(n int) &byte { unsafe { res = C.GC_MALLOC(n) } + } $else $if freestanding { + mut e := Errno{} + res, e = mm_alloc(u64(n)) + if e != .enoerror { + eprint('malloc() failed: ') + eprintln(e.str()) + panic('malloc() failed') + } } $else { res = unsafe { C.malloc(n) } } @@ -365,18 +410,6 @@ pub fn memdup(src voidptr, sz int) voidptr { } } -// is_atty returns 1 if the `fd` file descriptor is open and refers to a terminal -pub fn is_atty(fd int) int { - $if windows { - mut mode := u32(0) - osfh := voidptr(C._get_osfhandle(fd)) - C.GetConsoleMode(osfh, voidptr(&mode)) - return int(mode) - } $else { - return C.isatty(fd) - } -} - [inline] fn v_fixed_index(i int, len int) int { $if !no_bounds_checking ? { diff --git a/vlib/builtin/builtin.v b/vlib/builtin/builtin.v index 48c1a3839c..a5768a40fa 100644 --- a/vlib/builtin/builtin.v +++ b/vlib/builtin/builtin.v @@ -13,6 +13,12 @@ pub fn isnil(v voidptr) bool { return v == 0 } +[deprecated: 'use os.is_atty(x) instead'] +pub fn is_atty(fd int) int { + panic('use os.is_atty(x) instead') + return 0 +} + /* fn on_panic(f fn(int)int) { // TODO @@ -26,7 +32,9 @@ pub fn print_backtrace() { // 1 frame for print_backtrace itself // ... print the rest of the backtrace frames ... // => top 2 frames should be skipped, since they will not be informative to the developer - print_backtrace_skipping_top_frames(2) + $if !freestanding { + print_backtrace_skipping_top_frames(2) + } } struct VCastTypeIndexName { diff --git a/vlib/builtin/builtin_nix.c.v b/vlib/builtin/builtin_nix.c.v index 165cced966..7b5860d426 100644 --- a/vlib/builtin/builtin_nix.c.v +++ b/vlib/builtin/builtin_nix.c.v @@ -85,55 +85,57 @@ fn print_backtrace_skipping_top_frames_linux(skipframes int) bool { $if no_backtrace ? { return false } $else { - $if tinyc { - C.tcc_backtrace(c'Backtrace') - return false - } - buffer := [100]voidptr{} - nr_ptrs := C.backtrace(&buffer[0], 100) - if nr_ptrs < 2 { - eprintln('C.backtrace returned less than 2 frames') - return false - } - nr_actual_frames := nr_ptrs - skipframes - mut sframes := []string{} - //////csymbols := backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames) - csymbols := C.backtrace_symbols(voidptr(&buffer[skipframes]), nr_actual_frames) - for i in 0 .. nr_actual_frames { - sframes << unsafe { tos2(&byte(csymbols[i])) } - } - for sframe in sframes { - executable := sframe.all_before('(') - addr := sframe.all_after('[').all_before(']') - beforeaddr := sframe.all_before('[') - cmd := 'addr2line -e $executable $addr' - // taken from os, to avoid depending on the os module inside builtin.v - f := C.popen(&char(cmd.str), c'r') - if isnil(f) { - eprintln(sframe) - continue + $if !freestanding { + $if tinyc { + C.tcc_backtrace(c'Backtrace') + return false } - buf := [1000]byte{} - mut output := '' - unsafe { - bp := &buf[0] - for C.fgets(&char(bp), 1000, f) != 0 { - output += tos(bp, vstrlen(bp)) + buffer := [100]voidptr{} + nr_ptrs := C.backtrace(&buffer[0], 100) + if nr_ptrs < 2 { + eprintln('C.backtrace returned less than 2 frames') + return false + } + nr_actual_frames := nr_ptrs - skipframes + mut sframes := []string{} + //////csymbols := backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames) + csymbols := C.backtrace_symbols(voidptr(&buffer[skipframes]), nr_actual_frames) + for i in 0 .. nr_actual_frames { + sframes << unsafe { tos2(&byte(csymbols[i])) } + } + for sframe in sframes { + executable := sframe.all_before('(') + addr := sframe.all_after('[').all_before(']') + beforeaddr := sframe.all_before('[') + cmd := 'addr2line -e $executable $addr' + // taken from os, to avoid depending on the os module inside builtin.v + f := C.popen(&char(cmd.str), c'r') + if isnil(f) { + eprintln(sframe) + continue } + buf := [1000]byte{} + mut output := '' + unsafe { + bp := &buf[0] + for C.fgets(&char(bp), 1000, f) != 0 { + output += tos(bp, vstrlen(bp)) + } + } + output = output.trim_space() + ':' + if C.pclose(f) != 0 { + eprintln(sframe) + continue + } + if output in ['??:0:', '??:?:'] { + output = '' + } + // See http://wiki.dwarfstd.org/index.php?title=Path_Discriminators + // NB: it is shortened here to just d. , just so that it fits, and so + // that the common error file:lineno: line format is enforced. + output = output.replace(' (discriminator', ': (d.') + eprintln('${output:-55s} | ${addr:14s} | $beforeaddr') } - output = output.trim_space() + ':' - if C.pclose(f) != 0 { - eprintln(sframe) - continue - } - if output in ['??:0:', '??:?:'] { - output = '' - } - // See http://wiki.dwarfstd.org/index.php?title=Path_Discriminators - // NB: it is shortened here to just d. , just so that it fits, and so - // that the common error file:lineno: line format is enforced. - output = output.replace(' (discriminator', ': (d.') - eprintln('${output:-55s} | ${addr:14s} | $beforeaddr') } } return true diff --git a/vlib/builtin/builtin_windows.c.v b/vlib/builtin/builtin_windows.c.v index 90ad590f77..be7bfb8e12 100644 --- a/vlib/builtin/builtin_windows.c.v +++ b/vlib/builtin/builtin_windows.c.v @@ -77,11 +77,18 @@ fn restore_codepage() { C.SetConsoleOutputCP(g_original_codepage) } +fn is_terminal(fd int) int { + mut mode := u32(0) + osfh := voidptr(C._get_osfhandle(fd)) + C.GetConsoleMode(osfh, voidptr(&mode)) + return int(mode) +} + fn builtin_init() { g_original_codepage = C.GetConsoleOutputCP() C.SetConsoleOutputCP(C.CP_UTF8) C.atexit(restore_codepage) - if is_atty(1) > 0 { + if is_terminal(1) > 0 { C.SetConsoleMode(C.GetStdHandle(C.STD_OUTPUT_HANDLE), C.ENABLE_PROCESSED_OUTPUT | C.ENABLE_WRAP_AT_EOL_OUTPUT | 0x0004) // enable_virtual_terminal_processing C.SetConsoleMode(C.GetStdHandle(C.STD_ERROR_HANDLE), C.ENABLE_PROCESSED_OUTPUT | C.ENABLE_WRAP_AT_EOL_OUTPUT | 0x0004) // enable_virtual_terminal_processing unsafe { diff --git a/vlib/builtin/linux_bare/libc_impl.v b/vlib/builtin/linux_bare/libc_impl.v new file mode 100644 index 0000000000..dfe8de0e52 --- /dev/null +++ b/vlib/builtin/linux_bare/libc_impl.v @@ -0,0 +1,130 @@ +module builtin + +[unsafe] +pub fn memcpy(dest &byte, src &byte, n int) &byte { + unsafe { + for i in 0 .. n { + dest[i] = src[i] + } + return dest + } +} + +[export: 'malloc'] +[unsafe] +fn __malloc(n int) &byte { + return unsafe { malloc(n) } +} + +[unsafe] +fn strlen(s &byte) int { + mut i := 0 + for ; unsafe { s[i] } != 0; i++ {} + return i +} + +[unsafe] +fn realloc(old_area &byte, new_size int) &byte { + if old_area == 0 { + return unsafe { malloc(new_size) } + } + if new_size == 0 { + unsafe { free(old_area) } + return 0 + } + old_size := unsafe { *(&u64(old_area - sizeof(u64))) } + if new_size <= old_size { + return old_area + } else { + new_area := unsafe { malloc(new_size) } + unsafe { memmove(new_area, old_area, int(old_size)) } + unsafe { free(old_area) } + return new_area + } +} + +[unsafe] +fn memset(_s &byte, _c int, n int) &byte { + c := char(_c) + mut s := unsafe { &char(_s) } + for i in 0 .. int(n) { + unsafe { + s[i] = c + } + } + return s +} + +[unsafe] +fn memmove(dest &byte, src &byte, n int) &byte { + mut temp_buf := unsafe { malloc(n) } + for i in 0 .. int(n) { + unsafe { + temp_buf[i] = src[i] + } + } + + for i in 0 .. int(n) { + unsafe { + dest[i] = temp_buf[i] + } + } + unsafe { free(temp_buf) } + return dest +} + +[export: 'calloc'] +[unsafe] +fn __calloc(nmemb int, size int) &byte { + new_area := unsafe { malloc(nmemb * size) } + unsafe { memset(new_area, 0, nmemb * size) } + return new_area +} + +fn getchar() int { + x := byte(0) + sys_read(C.stdin, &x, 1) + return int(x) +} + +fn memcmp(a &byte, b &byte, n int) int { + for i in 0 .. int(n) { + if unsafe { a[i] != b[i] } { + unsafe { + return a[i] - b[i] + } + } + } + return 0 +} + +[export: 'free'] +[unsafe] +fn __free(ptr &byte) { + err := mm_free(ptr) + if err != .enoerror { + eprintln('free error:') + panic(err) + } +} + +fn vsprintf(str &char, format &char, ap &byte) int { + panic('vsprintf(): string interpolation is not supported in `-freestanding`') +} + +fn vsnprintf(str &char, size int, format &char, ap &byte) int { + panic('vsnprintf(): string interpolation is not supported in `-freestanding`') +} + +// not really needed +fn bare_read(buf &byte, count u64) (i64, Errno) { + return sys_read(0, buf, count) +} + +fn bare_print(buf &byte, len u64) { + sys_write(1, buf, len) +} + +fn bare_eprint(buf &byte, len u64) { + sys_write(2, buf, len) +} diff --git a/vlib/builtin/linux_bare/linux_syscalls.v b/vlib/builtin/linux_bare/linux_syscalls.v new file mode 100644 index 0000000000..9f18531627 --- /dev/null +++ b/vlib/builtin/linux_bare/linux_syscalls.v @@ -0,0 +1,431 @@ +module builtin + +enum SigIndex { + si_signo = 0x00 + si_code = 0x02 + si_pid = 0x04 + si_uid = 0x05 + si_status = 0x06 + si_size = 0x80 +} + +enum Signo { + sighup = 1 // Hangup. + sigint = 2 // Interactive attention signal. + sigquit = 3 // Quit. + sigill = 4 // Illegal instruction. + sigtrap = 5 // Trace/breakpoint trap. + sigabrt = 6 // Abnormal termination. + sigbus = 7 + sigfpe = 8 // Erroneous arithmetic operation. + sigkill = 9 // Killed. + sigusr1 = 10 + sigsegv = 11 // Invalid access to memory. + sigusr2 = 12 + sigpipe = 13 // Broken pipe. + sigalrm = 14 // Alarm clock. + sigterm = 15 // Termination request. + sigstkflt = 16 + sigchld = 17 + sigcont = 18 + sigstop = 19 + sigtstp = 20 + sigttin = 21 // Background read from control terminal. + sigttou = 22 // Background write to control terminal. + sigurg = 23 + sigxcpu = 24 // CPU time limit exceeded. + sigxfsz = 25 // File size limit exceeded. + sigvtalrm = 26 // Virtual timer expired. + sigprof = 27 // Profiling timer expired. + sigwinch = 28 + sigpoll = 29 + sigsys = 31 +} + +// List of all the errors returned by syscalls +enum Errno { + enoerror = 0x00000000 + eperm = 0x00000001 + enoent = 0x00000002 + esrch = 0x00000003 + eintr = 0x00000004 + eio = 0x00000005 + enxio = 0x00000006 + e2big = 0x00000007 + enoexec = 0x00000008 + ebadf = 0x00000009 + echild = 0x0000000a + eagain = 0x0000000b + enomem = 0x0000000c + eacces = 0x0000000d + efault = 0x0000000e + enotblk = 0x0000000f + ebusy = 0x00000010 + eexist = 0x00000011 + exdev = 0x00000012 + enodev = 0x00000013 + enotdir = 0x00000014 + eisdir = 0x00000015 + einval = 0x00000016 + enfile = 0x00000017 + emfile = 0x00000018 + enotty = 0x00000019 + etxtbsy = 0x0000001a + efbig = 0x0000001b + enospc = 0x0000001c + espipe = 0x0000001d + erofs = 0x0000001e + emlink = 0x0000001f + epipe = 0x00000020 + edom = 0x00000021 + erange = 0x00000022 +} + +enum MemProt { + prot_read = 0x1 + prot_write = 0x2 + prot_exec = 0x4 + prot_none = 0x0 + prot_growsdown = 0x01000000 + prot_growsup = 0x02000000 +} + +enum MapFlags { + map_shared = 0x01 + map_private = 0x02 + map_shared_validate = 0x03 + map_type = 0x0f + map_fixed = 0x10 + map_file = 0x00 + map_anonymous = 0x20 + map_huge_shift = 26 + map_huge_mask = 0x3f +} + +// const ( +// fcntlf_dupfd = 0x00000000 +// fcntlf_exlck = 0x00000004 +// fcntlf_getfd = 0x00000001 +// fcntlf_getfl = 0x00000003 +// fcntlf_getlk = 0x00000005 +// fcntlf_getlk64 = 0x0000000c +// fcntlf_getown = 0x00000009 +// fcntlf_getowner_uids = 0x00000011 +// fcntlf_getown_ex = 0x00000010 +// fcntlf_getsig = 0x0000000b +// fcntlf_ofd_getlk = 0x00000024 +// fcntlf_ofd_setlk = 0x00000025 +// fcntlf_ofd_setlkw = 0x00000026 +// fcntlf_owner_pgrp = 0x00000002 +// fcntlf_owner_pid = 0x00000001 +// fcntlf_owner_tid = 0x00000000 +// fcntlf_rdlck = 0x00000000 +// fcntlf_setfd = 0x00000002 +// fcntlf_setfl = 0x00000004 +// fcntlf_setlk = 0x00000006 +// fcntlf_setlk64 = 0x0000000d +// fcntlf_setlkw = 0x00000007 +// fcntlf_setlkw64 = 0x0000000e +// fcntlf_setown = 0x00000008 +// fcntlf_setown_ex = 0x0000000f +// fcntlf_setsig = 0x0000000a +// fcntlf_shlck = 0x00000008 +// fcntlf_unlck = 0x00000002 +// fcntlf_wrlck = 0x00000001 +// fcntllock_ex = 0x00000002 +// fcntllock_mand = 0x00000020 +// fcntllock_nb = 0x00000004 +// fcntllock_read = 0x00000040 +// fcntllock_rw = 0x000000c0 +// fcntllock_sh = 0x00000001 +// fcntllock_un = 0x00000008 +// fcntllock_write = 0x00000080 +// fcntlo_accmode = 0x00000003 +// fcntlo_append = 0x00000400 +// fcntlo_cloexec = 0x00080000 +// fcntlo_creat = 0x00000040 +// fcntlo_direct = 0x00004000 +// fcntlo_directory = 0x00010000 +// fcntlo_dsync = 0x00001000 +// fcntlo_excl = 0x00000080 +// fcntlo_largefile = 0x00008000 +// fcntlo_ndelay = 0x00000800 +// fcntlo_noatime = 0x00040000 +// fcntlo_noctty = 0x00000100 +// fcntlo_nofollow = 0x00020000 +// fcntlo_nonblock = 0x00000800 +// fcntlo_path = 0x00200000 +// fcntlo_rdonly = 0x00000000 +// fcntlo_rdwr = 0x00000002 +// fcntlo_trunc = 0x00000200 +// fcntlo_wronly = 0x00000001 +// ) + +/* +Paraphrased from "man 2 waitid" on Linux + + Upon successful return, waitid() fills in the + following fields of the siginfo_t structure + pointed to by infop: + + si_pid, offset 0x10, int index 0x04: + The process ID of the child. + + si_uid: offset 0x14, int index 0x05 + The real user ID of the child. + + si_signo: offset 0x00, int index 0x00 + Always set to SIGCHLD. + + si_status: ofset 0x18, int index 0x06 + 1 the exit status of the child, as given to _exit(2) + (or exit(3)) (sc_sys.cld_exited) + 2 the signal that caused the child to terminate, stop, + or continue. + 3 The si_code field can be used to determine how to + interpret this field. + + si_code, set to one of (enum Wi_si_code), offset 0x08, int index 0x02: + CLD_EXITED (child called _exit(2)); + CLD_KILLED (child killed by signal); + CLD_DUMPED (child killed by signal, and dumped core); + CLD_STOPPED (child stopped by signal); + CLD_TRAPPED (traced child has trapped); + CLD_CONTINUED (child continued by SIGCONT). +*/ + +const ( + wp_sys_wnohang = u64(0x00000001) + wp_sys_wuntraced = u64(0x00000002) + wp_sys_wstopped = u64(0x00000002) + wp_sys_wexited = u64(0x00000004) + wp_sys_wcontinued = u64(0x00000008) + wp_sys_wnowait = u64(0x01000000) // don't reap, just poll status. + wp_sys___wnothread = u64(0x20000000) // don't wait on children of other threads in this group + wp_sys___wall = u64(0x40000000) // wait on all children, regardless of type + wp_sys___wclone = u64(0x80000000) // wait only on non-sigchld children +) + +// First argument to waitid: +enum WiWhich { + p_all = 0 + p_pid = 1 + p_pgid = 2 +} + +enum WiSiCode { + cld_exited = 1 // child has exited + cld_killed = 2 // child was killed + cld_dumped = 3 // child terminated abnormally + cld_trapped = 4 // traced child has trapped + cld_stopped = 5 // child has stopped + cld_continued = 6 // stopped child has continued +} + +fn split_int_errno(rc_in u64) (i64, Errno) { + rc := i64(rc_in) + if rc < 0 { + return i64(-1), Errno(-rc) + } + return rc, Errno.enoerror +} + +// 0 sys_read +fn sys_read(fd i64, buf &byte, count u64) (i64, Errno) { + return split_int_errno(sys_call3(0, u64(fd), u64(buf), count)) +} + +// 1 sys_write +fn sys_write(fd i64, buf &byte, count u64) (i64, Errno) { + return split_int_errno(sys_call3(1, u64(fd), u64(buf), count)) +} + +// 2 sys_open +fn sys_open(filename &byte, flags i64, mode int) (i64, Errno) { + return split_int_errno(sys_call3(2, u64(filename), u64(flags), u64(mode))) +} + +// 3 sys_close +fn sys_close(fd i64) Errno { + return Errno(-i64(sys_call1(3, u64(fd)))) +} + +// 9 sys_mmap +fn sys_mmap(addr &byte, len u64, prot MemProt, flags MapFlags, fildes u64, off u64) (&byte, Errno) { + rc := sys_call6(9, u64(addr), len, u64(prot), u64(flags), fildes, off) + a, e := split_int_errno(rc) + return &byte(a), e +} + +// 11 sys_munmap +fn sys_munmap(addr voidptr, len u64) Errno { + return Errno(-sys_call2(11, u64(addr), len)) +} + +// 22 sys_pipe +fn sys_pipe(filedes &int) Errno { + return Errno(sys_call1(22, u64(filedes))) +} + +// 24 sys_sched_yield +fn sys_sched_yield() Errno { + return Errno(sys_call0(24)) +} + +// 28 sys_madvise +fn sys_madvise(addr voidptr, len u64, advice int) Errno { + return Errno(sys_call3(28, u64(addr), len, u64(advice))) +} + +// 39 sys_getpid +fn sys_getpid() int { + return int(sys_call0(39)) +} + +// 57 sys_fork +fn sys_fork() int { + return int(sys_call0(57)) +} + +// 58 sys_vfork +fn sys_vfork() int { + return int(sys_call0(58)) +} + +// 33 sys_dup2 +fn sys_dup2(oldfd int, newfd int) (i64, Errno) { + return split_int_errno(sys_call2(33, u64(oldfd), u64(newfd))) +} + +// 59 sys_execve +fn sys_execve(filename &byte, argv []&byte, envp []&byte) int { + return int(sys_call3(59, u64(filename), argv.data, envp.data)) +} + +// 60 sys_exit +fn sys_exit(ec int) { + sys_call1(60, u64(ec)) +} + +// 102 sys_getuid +fn sys_getuid() int { + return int(sys_call0(102)) +} + +// 247 sys_waitid +fn sys_waitid(which WiWhich, pid int, infop &int, options int, ru voidptr) Errno { + return Errno(sys_call5(247, u64(which), u64(pid), u64(infop), u64(options), u64(ru))) +} + +fn sys_call0(scn u64) u64 { + mut res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + } + return res +} + +fn sys_call1(scn u64, arg1 u64) u64 { + mut res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + } + return res +} + +fn sys_call2(scn u64, arg1 u64, arg2 u64) u64 { + mut res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + } + return res +} + +fn sys_call3(scn u64, arg1 u64, arg2 u64, arg3 u64) u64 { + mut res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + } + return res +} + +fn sys_call4(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64) u64 { + mut res := u64(0) + asm amd64 { + mov r10, arg4 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + ; r10 + } + return res +} + +fn sys_call5(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64) u64 { + mut res := u64(0) + asm amd64 { + mov r10, arg4 + mov r8, arg5 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + r (arg5) + ; r10 + r8 + } + return res +} + +fn sys_call6(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64, arg6 u64) u64 { + mut res := u64(0) + asm amd64 { + mov r10, arg4 + mov r8, arg5 + mov r9, arg6 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + r (arg5) + r (arg6) + ; r10 + r8 + r9 + } + return res +} + +asm amd64 { + .globl _start + _start: + call main + mov rax, 60 + xor rdi, rdi + syscall + ret +} diff --git a/vlib/builtin/linux_bare/memory_managment.v b/vlib/builtin/linux_bare/memory_managment.v new file mode 100644 index 0000000000..9c23dcfc32 --- /dev/null +++ b/vlib/builtin/linux_bare/memory_managment.v @@ -0,0 +1,29 @@ +module builtin + +fn mm_alloc(size u64) (&byte, Errno) { + // BEGIN CONSTS + // the constants need to be here, since the initialization of other constants, + // which happen before these ones would, require malloc + mem_prot := MemProt(int(MemProt.prot_read) | int(MemProt.prot_write)) + map_flags := MapFlags(int(MapFlags.map_private) | int(MapFlags.map_anonymous)) + // END CONSTS + + a, e := sys_mmap(&byte(0), size + sizeof(u64), mem_prot, map_flags, -1, 0) + if e == .enoerror { + unsafe { + mut ap := &u64(a) + *ap = size + x2 := &byte(a + sizeof(u64)) + return x2, e + } + } + return &byte(0), e +} + +fn mm_free(addr &byte) Errno { + unsafe { + ap := &u64(addr - sizeof(u64)) + size := *ap + return sys_munmap(addr - sizeof(u64), size + sizeof(u64)) + } +} diff --git a/vlib/builtin/linux_bare/.checks/.gitignore b/vlib/builtin/linux_bare/old/.checks/.gitignore similarity index 100% rename from vlib/builtin/linux_bare/.checks/.gitignore rename to vlib/builtin/linux_bare/old/.checks/.gitignore diff --git a/vlib/builtin/linux_bare/.checks/checks.v b/vlib/builtin/linux_bare/old/.checks/checks.v similarity index 100% rename from vlib/builtin/linux_bare/.checks/checks.v rename to vlib/builtin/linux_bare/old/.checks/checks.v diff --git a/vlib/builtin/linux_bare/.checks/consts/consts.v b/vlib/builtin/linux_bare/old/.checks/consts/consts.v similarity index 100% rename from vlib/builtin/linux_bare/.checks/consts/consts.v rename to vlib/builtin/linux_bare/old/.checks/consts/consts.v diff --git a/vlib/builtin/linux_bare/.checks/forkedtest/forkedtest.v b/vlib/builtin/linux_bare/old/.checks/forkedtest/forkedtest.v similarity index 100% rename from vlib/builtin/linux_bare/.checks/forkedtest/forkedtest.v rename to vlib/builtin/linux_bare/old/.checks/forkedtest/forkedtest.v diff --git a/vlib/builtin/linux_bare/.checks/linuxsys/linuxsys.v b/vlib/builtin/linux_bare/old/.checks/linuxsys/linuxsys.v similarity index 100% rename from vlib/builtin/linux_bare/.checks/linuxsys/linuxsys.v rename to vlib/builtin/linux_bare/old/.checks/linuxsys/linuxsys.v diff --git a/vlib/builtin/linux_bare/.checks/readme.md b/vlib/builtin/linux_bare/old/.checks/readme.md similarity index 100% rename from vlib/builtin/linux_bare/.checks/readme.md rename to vlib/builtin/linux_bare/old/.checks/readme.md diff --git a/vlib/builtin/linux_bare/.checks/sample_text1.txt b/vlib/builtin/linux_bare/old/.checks/sample_text1.txt similarity index 100% rename from vlib/builtin/linux_bare/.checks/sample_text1.txt rename to vlib/builtin/linux_bare/old/.checks/sample_text1.txt diff --git a/vlib/builtin/linux_bare/.checks/string/string.v b/vlib/builtin/linux_bare/old/.checks/string/string.v similarity index 100% rename from vlib/builtin/linux_bare/.checks/string/string.v rename to vlib/builtin/linux_bare/old/.checks/string/string.v diff --git a/vlib/builtin/linux_bare/.checks/structs/structs.v b/vlib/builtin/linux_bare/old/.checks/structs/structs.v similarity index 100% rename from vlib/builtin/linux_bare/.checks/structs/structs.v rename to vlib/builtin/linux_bare/old/.checks/structs/structs.v diff --git a/vlib/builtin/linux_bare/array_bare.v b/vlib/builtin/linux_bare/old/array_bare.v similarity index 100% rename from vlib/builtin/linux_bare/array_bare.v rename to vlib/builtin/linux_bare/old/array_bare.v diff --git a/vlib/builtin/linux_bare/builtin_bare.v b/vlib/builtin/linux_bare/old/builtin_bare.v similarity index 100% rename from vlib/builtin/linux_bare/builtin_bare.v rename to vlib/builtin/linux_bare/old/builtin_bare.v diff --git a/vlib/builtin/linux_bare/linuxsys_bare.v b/vlib/builtin/linux_bare/old/linuxsys_bare.v similarity index 100% rename from vlib/builtin/linux_bare/linuxsys_bare.v rename to vlib/builtin/linux_bare/old/linuxsys_bare.v diff --git a/vlib/builtin/linux_bare/mm_bare.v b/vlib/builtin/linux_bare/old/mm_bare.v similarity index 100% rename from vlib/builtin/linux_bare/mm_bare.v rename to vlib/builtin/linux_bare/old/mm_bare.v diff --git a/vlib/builtin/linux_bare/string_bare.v b/vlib/builtin/linux_bare/old/string_bare.v similarity index 100% rename from vlib/builtin/linux_bare/string_bare.v rename to vlib/builtin/linux_bare/old/string_bare.v diff --git a/vlib/builtin/linux_bare/syscallwrapper_test.v b/vlib/builtin/linux_bare/old/syscallwrapper_test.v similarity index 100% rename from vlib/builtin/linux_bare/syscallwrapper_test.v rename to vlib/builtin/linux_bare/old/syscallwrapper_test.v diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index c485363a5e..bc224b59ab 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -997,7 +997,11 @@ pub fn (s string) to_lower() string { unsafe { mut b := malloc(s.len + 1) for i in 0 .. s.len { - b[i] = byte(C.tolower(s.str[i])) + if s.str[i] >= `A` && s.str[i] <= `Z` { + b[i] = s.str[i] + 32 + } else { + b[i] = s.str[i] + } } b[s.len] = 0 return tos(b, s.len) @@ -1021,7 +1025,11 @@ pub fn (s string) to_upper() string { unsafe { mut b := malloc(s.len + 1) for i in 0 .. s.len { - b[i] = byte(C.toupper(s.str[i])) + if s.str[i] >= `a` && s.str[i] <= `z` { + b[i] = s.str[i] - 32 + } else { + b[i] = s.str[i] + } } b[s.len] = 0 return tos(b, s.len) @@ -1539,7 +1547,11 @@ pub fn (s &string) free() { return } if s.is_lit == -98761234 { - C.printf(c'double string.free() detected\n') + $if freestanding { + bare_eprint(c'double string.free() detected\n', u64(unsafe { C.strlen(c'double string.free() detected\n') })) + } $else { + C.printf(c'double string.free() detected\n') + } return } if s.is_lit == 1 || s.len == 0 { diff --git a/vlib/os/os.v b/vlib/os/os.v index 0caa5bb015..2dcda1127d 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -629,3 +629,15 @@ pub fn execute_or_panic(cmd string) Result { } return res } + +// is_atty returns 1 if the `fd` file descriptor is open and refers to a terminal +pub fn is_atty(fd int) int { + $if windows { + mut mode := u32(0) + osfh := voidptr(C._get_osfhandle(fd)) + C.GetConsoleMode(osfh, voidptr(&mode)) + return int(mode) + } $else { + return C.isatty(fd) + } +} diff --git a/vlib/term/term.v b/vlib/term/term.v index 9f8dcf53d6..c2bf67f5af 100644 --- a/vlib/term/term.v +++ b/vlib/term/term.v @@ -125,8 +125,8 @@ fn supports_escape_sequences(fd int) bool { return true } // 4 is enable_virtual_terminal_processing - return (is_atty(fd) & 0x0004) > 0 + return (os.is_atty(fd) & 0x0004) > 0 } $else { - return is_atty(fd) > 0 + return os.is_atty(fd) > 0 } } diff --git a/vlib/term/term_nix.c.v b/vlib/term/term_nix.c.v index ce96ca15c5..45a0a9b841 100644 --- a/vlib/term/term_nix.c.v +++ b/vlib/term/term_nix.c.v @@ -17,7 +17,7 @@ fn C.ioctl(fd int, request u64, arg voidptr) int // get_terminal_size returns a number of colums and rows of terminal window. pub fn get_terminal_size() (int, int) { - if is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' { + if os.is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' { return default_columns_size, default_rows_size } w := C.winsize{} @@ -27,7 +27,7 @@ pub fn get_terminal_size() (int, int) { // get_cursor_position returns a Coord containing the current cursor position pub fn get_cursor_position() Coord { - if is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' { + if os.is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' { return Coord{ x: 0 y: 0 @@ -89,7 +89,7 @@ pub fn get_cursor_position() Coord { // set_terminal_title change the terminal title pub fn set_terminal_title(title string) bool { - if is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' { + if os.is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' { return true } print('\033]0') diff --git a/vlib/term/term_windows.c.v b/vlib/term/term_windows.c.v index 74d259de08..bb65f123d0 100644 --- a/vlib/term/term_windows.c.v +++ b/vlib/term/term_windows.c.v @@ -57,7 +57,7 @@ fn C.ScrollConsoleScreenBuffer(output C.HANDLE, scroll_rect &C.SMALL_RECT, clip_ // get_terminal_size returns a number of colums and rows of terminal window. pub fn get_terminal_size() (int, int) { - if is_atty(1) > 0 && os.getenv('TERM') != 'dumb' { + if os.is_atty(1) > 0 && os.getenv('TERM') != 'dumb' { info := C.CONSOLE_SCREEN_BUFFER_INFO{} if C.GetConsoleScreenBufferInfo(C.GetStdHandle(C.STD_OUTPUT_HANDLE), &info) { columns := int(info.srWindow.Right - info.srWindow.Left + 1) @@ -71,7 +71,7 @@ pub fn get_terminal_size() (int, int) { // get_cursor_position returns a Coord containing the current cursor position pub fn get_cursor_position() Coord { mut res := Coord{} - if is_atty(1) > 0 && os.getenv('TERM') != 'dumb' { + if os.is_atty(1) > 0 && os.getenv('TERM') != 'dumb' { info := C.CONSOLE_SCREEN_BUFFER_INFO{} if C.GetConsoleScreenBufferInfo(C.GetStdHandle(C.STD_OUTPUT_HANDLE), &info) { res.x = info.dwCursorPosition.X diff --git a/vlib/term/ui/termios_nix.c.v b/vlib/term/ui/termios_nix.c.v index 28eb8385ac..a39c3d0d63 100644 --- a/vlib/term/ui/termios_nix.c.v +++ b/vlib/term/ui/termios_nix.c.v @@ -60,7 +60,7 @@ fn (mut ctx Context) termios_setup() ? { // store the current title, so restore_terminal_state can get it back save_title() - if !ctx.cfg.skip_init_checks && !(is_atty(C.STDIN_FILENO) != 0 && is_atty(C.STDOUT_FILENO) != 0) { + if !ctx.cfg.skip_init_checks && !(os.is_atty(C.STDIN_FILENO) != 0 && os.is_atty(C.STDOUT_FILENO) != 0) { return error('not running under a TTY') } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 15c616868a..1b1008c4db 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -3804,7 +3804,7 @@ fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) { stmt.pos) } if c.pref.backend == .js { - c.error('inline assembly is not supported in js backend', stmt.pos) + c.error('inline assembly is not supported in the js backend', stmt.pos) } if c.pref.backend == .c && c.pref.ccompiler_type == .msvc { c.error('msvc compiler does not support inline assembly', stmt.pos) @@ -3857,16 +3857,7 @@ fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) { fn (mut c Checker) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt, aliases []string) { match mut arg { - ast.AsmAlias { - name := arg.name - if name !in aliases && name !in stmt.local_labels && name !in c.file.global_labels { - mut possible := aliases.clone() - possible << stmt.local_labels - possible << c.file.global_labels - suggestion := util.new_suggestion(name, possible) - c.error(suggestion.say('alias or label `$arg.name` does not exist'), arg.pos) - } - } + ast.AsmAlias {} ast.AsmAddressing { if arg.scale !in [-1, 1, 2, 4, 8] { c.error('scale must be one of 1, 2, 4, or 8', arg.pos) @@ -5368,7 +5359,19 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { } else if !is_comptime_type_is_expr { found_branch = true // If a branch wasn't skipped, the rest must be } - if !c.skip_flags || c.pref.output_cross_c { + if !c.skip_flags { + c.stmts(branch.stmts) + } else if c.pref.output_cross_c { + mut is_freestanding_block := false + if branch.cond is ast.Ident { + if branch.cond.name == 'freestanding' { + is_freestanding_block = true + } + } + if is_freestanding_block { + branch.stmts = [] + node.branches[i].stmts = [] + } c.stmts(branch.stmts) } else if !is_comptime_type_is_expr { node.branches[i].stmts = [] @@ -5603,7 +5606,7 @@ fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool { 'glibc' { return false } // TODO 'prealloc' { return !c.pref.prealloc } 'no_bounds_checking' { return cond.name !in c.pref.compile_defines_all } - 'freestanding' { return !c.pref.is_bare } + 'freestanding' { return !c.pref.is_bare || c.pref.output_cross_c } else { return false } } } else if cond.name !in c.pref.compile_defines_all { diff --git a/vlib/v/checker/tests/asm_alias_does_not_exist.out b/vlib/v/checker/tests/asm_alias_does_not_exist.out deleted file mode 100644 index 344c7e0b0d..0000000000 --- a/vlib/v/checker/tests/asm_alias_does_not_exist.out +++ /dev/null @@ -1,5 +0,0 @@ -vlib/v/checker/tests/asm_alias_does_not_exist.vv:2:11: error: alias or label `a` does not exist - 1 | asm amd64 { - 2 | mov ebx, a - | ^ - 3 | } diff --git a/vlib/v/checker/tests/asm_alias_does_not_exist.vv b/vlib/v/checker/tests/asm_alias_does_not_exist.vv deleted file mode 100644 index 471862f244..0000000000 --- a/vlib/v/checker/tests/asm_alias_does_not_exist.vv +++ /dev/null @@ -1,3 +0,0 @@ -asm amd64 { - mov ebx, a -} diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 72b6bd2f3c..0404909bdc 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -392,7 +392,6 @@ pub fn (mut g Gen) init() { } else { g.cheaders.writeln(c_headers) } - g.definitions.writeln('void _STR_PRINT_ARG(const char*, char**, int*, int*, int, ...);') g.definitions.writeln('string _STR(const char*, int, ...);') g.definitions.writeln('string _STR_TMP(const char*, ...);') } @@ -1893,7 +1892,7 @@ fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) { ast.AsmAlias { name := arg.name if name in stmt.local_labels || name in stmt.global_labels - || name in g.file.global_labels { + || name in g.file.global_labels || stmt.is_top_level { asm_formatted_name := if name in stmt.global_labels { '%l[$name]' } else { name } g.write(asm_formatted_name) } else { @@ -5466,7 +5465,9 @@ fn (mut g Gen) write_types(types []ast.TypeSymbol) { g.type_definitions.writeln('} $name;') } } else { - g.type_definitions.writeln('typedef pthread_t $name;') + if !g.pref.is_bare { + g.type_definitions.writeln('typedef pthread_t $name;') + } } } ast.SumType { diff --git a/vlib/v/gen/c/cheaders.v b/vlib/v/gen/c/cheaders.v index 26d3dd5ff2..5f0581bc15 100644 --- a/vlib/v/gen/c/cheaders.v +++ b/vlib/v/gen/c/cheaders.v @@ -5,18 +5,18 @@ module c // for each constant, during C code generation. const ( // V_COMMIT_HASH is generated by cmd/tools/gen_vc.v . - c_commit_hash_default = ' + c_commit_hash_default = ' #ifndef V_COMMIT_HASH #define V_COMMIT_HASH "@@@" #endif ' // V_CURRENT_COMMIT_HASH is updated, when V is rebuilt inside a git repo. - c_current_commit_hash_default = ' + c_current_commit_hash_default = ' #ifndef V_CURRENT_COMMIT_HASH #define V_CURRENT_COMMIT_HASH "@@@" #endif ' - c_concurrency_helpers = ' + c_concurrency_helpers = ' typedef struct __shared_map __shared_map; struct __shared_map { map val; sync__RwMutex mtx; }; static inline voidptr __dup_shared_map(voidptr src, int sz) { @@ -47,7 +47,136 @@ static inline void __sort_ptr(uintptr_t a[], bool b[], int l) } } ' - c_common_macros = ' + c_str_fn_defs = ' +void _STR_PRINT_ARG(const char *fmt, char** refbufp, int *nbytes, int *memsize, int guess, ...) { + va_list args; + va_start(args, guess); + // NB: (*memsize - *nbytes) === how much free space is left at the end of the current buffer refbufp + // *memsize === total length of the buffer refbufp + // *nbytes === already occupied bytes of buffer refbufp + // guess === how many bytes were taken during the current vsnprintf run + for(;;) { + int remaining_space = *memsize - *nbytes; + if (guess < remaining_space) { + guess = vsnprintf(*refbufp + *nbytes, *memsize - *nbytes, fmt, args); + if (guess < remaining_space) { // result did fit into buffer + *nbytes += guess; + break; + } + } + // increase buffer (somewhat exponentially) + *memsize += guess + 3 * (*memsize) / 2; +#ifdef _VGCBOEHM + *refbufp = (char*)GC_REALLOC((void*)*refbufp, *memsize); +#else + *refbufp = (char*)realloc((void*)*refbufp, *memsize); +#endif + } + va_end(args); +} + +string _STR(const char *fmt, int nfmts, ...) { + va_list argptr; + int memsize = 128; + int nbytes = 0; +#ifdef _VGCBOEHM + char* buf = (char*)GC_MALLOC(memsize); +#else + char* buf = (char*)malloc(memsize); +#endif + va_start(argptr, nfmts); + for (int i=0; i= \'E\' && fup <= \'G\') { // floating point + _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+10, va_arg(argptr, double)); + } else if (f == \'p\') { + _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+14, va_arg(argptr, void*)); + } else if (f == \'C\') { // a C string + char* sptr = va_arg(argptr, char*); + char* fmt_no_c = (char*)v_malloc(k+4); + memcpy(fmt_no_c, fmt, k); + fmt_no_c[k-2]=\'C\'; + fmt_no_c[k-1]=\'"\'; + fmt_no_c[k]=\'%\'; + fmt_no_c[k+1]=\'s\'; + fmt_no_c[k+2]=\'"\'; + fmt_no_c[k+3]=0; + _STR_PRINT_ARG(fmt_no_c, &buf, &nbytes, &memsize, k + 4 + 100, sptr); + v_free(fmt_no_c); + } else if (f == \'s\') { // v string + string s = va_arg(argptr, string); + if (fmt[k-4] == \'*\') { // %*.*s + int fwidth = va_arg(argptr, int); + if (fwidth < 0) + fwidth -= (s.len - utf8_str_visible_length(s)); + else + fwidth += (s.len - utf8_str_visible_length(s)); + _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+s.len-4, fwidth, s.len, s.str); + } else { // %.*s + _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+s.len-4, s.len, s.str); + } + } else { + //v_panic(tos3(\'Invaid format specifier\')); + } + } else { + _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k); + } + fmt += k+1; + } + va_end(argptr); + buf[nbytes] = 0; + +#ifdef DEBUG_ALLOC + //puts(\'_STR:\'); + puts(buf); +#endif + +#if _VAUTOFREE + //g_cur_str = (byteptr)buf; +#endif + return tos2((byteptr)buf); +} + +string _STR_TMP(const char *fmt, ...) { + va_list argptr; + va_start(argptr, fmt); + size_t len = vsnprintf(0, 0, fmt, argptr) + 1; + va_end(argptr); + va_start(argptr, fmt); + vsprintf((char *)g_str_buf, fmt, argptr); + va_end(argptr); + +#ifdef DEBUG_ALLOC + //puts(\'_STR_TMP:\'); + //puts(g_str_buf); +#endif + string res = tos(g_str_buf, len); + res.is_lit = 1; + return res; + +} // endof _STR_TMP + +' + c_common_macros = ' #define EMPTY_VARG_INITIALIZATION 0 #define EMPTY_STRUCT_DECLARATION #define EMPTY_STRUCT_INITIALIZATION 0 @@ -126,12 +255,6 @@ static inline void __sort_ptr(uintptr_t a[], bool b[], int l) #define V64_PRINTFORMAT "0x%llx" #endif #endif -' - c_headers = ' -// c_headers -typedef int (*qsort_callback_func)(const void*, const void*); -#include // TODO remove all these includes, define all function signatures and types manually -#include #if defined(_WIN32) || defined(__CYGWIN__) #define VV_EXPORTED_SYMBOL extern __declspec(dllexport) @@ -167,144 +290,8 @@ typedef int (*qsort_callback_func)(const void*, const void*); #undef __has_include #endif -#ifndef _WIN32 - #if defined __has_include - #if __has_include () - #include - #else - // Most probably musl OR __ANDROID__ ... - int backtrace (void **__array, int __size) { return 0; } - char **backtrace_symbols (void *const *__array, int __size){ return 0; } - void backtrace_symbols_fd (void *const *__array, int __size, int __fd){} - #endif - #endif -#endif - -//#include "fns.h" -#include -#include // for va_list -#include // memcpy - -#if INTPTR_MAX == INT32_MAX - #define TARGET_IS_32BIT 1 -#elif INTPTR_MAX == INT64_MAX - #define TARGET_IS_64BIT 1 -#else - #error "The environment is not 32 or 64-bit." -#endif - -#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ || defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) - #define TARGET_ORDER_IS_BIG -#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_IX86) - #define TARGET_ORDER_IS_LITTLE -#else - #error "Unknown architecture endianness" -#endif - -#ifndef _WIN32 - #include - #include // tolower - #include - #include // sleep - extern char **environ; -#endif - -#if defined(__CYGWIN__) && !defined(_WIN32) - #error Cygwin is not supported, please use MinGW or Visual Studio. -#endif - -#ifdef __linux__ - #include - #include // os__wait uses wait on nix -#endif - -#ifdef __FreeBSD__ - #include - #include // os__wait uses wait on nix -#endif - -#ifdef __DragonFly__ - #include - #include // os__wait uses wait on nix -#endif - -#ifdef __OpenBSD__ - #include - #include - #include // os__wait uses wait on nix -#endif - -#ifdef __NetBSD__ - #include // os__wait uses wait on nix -#endif - -#ifdef __sun - #include - #include // os__wait uses wait on nix -#endif - -$c_common_macros - -#ifdef _WIN32 - #define WINVER 0x0600 - #ifdef _WIN32_WINNT - #undef _WIN32_WINNT - #endif - #define _WIN32_WINNT 0x0600 - #ifndef WIN32_FULL - #define WIN32_LEAN_AND_MEAN - #endif - #ifndef _UNICODE - #define _UNICODE - #endif - #ifndef UNICODE - #define UNICODE - #endif - #include - - #include // _waccess - #include // _wgetcwd - - #ifdef _MSC_VER - // On MSVC these are the same (as long as /volatile:ms is passed) - #define _Atomic volatile - - // MSVC cannot parse some things properly - #undef EMPTY_STRUCT_DECLARATION - #undef OPTION_CAST - - #define EMPTY_STRUCT_DECLARATION int ____dummy_variable - #define OPTION_CAST(x) - #undef __NOINLINE - #undef __IRQHANDLER - #define __NOINLINE __declspec(noinline) - #define __IRQHANDLER __declspec(naked) - - #include - #pragma comment(lib, "Dbghelp") - #endif -#else - #include - #ifndef PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP - // musl does not have that - #define pthread_rwlockattr_setkind_np(a, b) - #endif -#endif - -// g_live_info is used by live.info() -static void* g_live_info = NULL; - -//============================== HELPER C MACROS =============================*/ -//#define tos4(s, slen) ((string){.str=(s), .len=(slen)}) -// `"" s` is used to enforce a string literal argument -#define _SLIT(s) ((string){.str=(byteptr)("" s), .len=(sizeof(s)-1), .is_lit=1}) -// take the address of an rvalue -#define ADDR(type, expr) (&((type[]){expr}[0])) -// copy something to the heap -#define HEAP(type, expr) ((type*)memdup((void*)&((type[]){expr}[0]), sizeof(type))) -#define _PUSH_MANY(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array_push_many(arr, tmp.data, tmp.len);} -#define _IN_MAP(val, m) map_exists(m, val) - +' + c_unsigned_comparison_functions = ' // unsigned/signed comparisons static inline bool _us32_gt(uint32_t a, int32_t b) { return a > INT32_MAX || (int32_t)a > b; } static inline bool _us32_ge(uint32_t a, int32_t b) { return a >= INT32_MAX || (int32_t)a >= b; } @@ -318,32 +305,8 @@ static inline bool _us64_eq(uint64_t a, int64_t b) { return a <= INT64_MAX && (i static inline bool _us64_ne(uint64_t a, int64_t b) { return a > INT64_MAX || (int64_t)a != b; } static inline bool _us64_le(uint64_t a, int64_t b) { return a <= INT64_MAX && (int64_t)a <= b; } static inline bool _us64_lt(uint64_t a, int64_t b) { return a < INT64_MAX && (int64_t)a < b; } - -#if defined(__MINGW32__) || defined(__MINGW64__) || (defined(_WIN32) && defined(__TINYC__)) - #undef PRId64 - #undef PRIi64 - #undef PRIo64 - #undef PRIu64 - #undef PRIx64 - #undef PRIX64 - #define PRId64 "lld" - #define PRIi64 "lli" - #define PRIo64 "llo" - #define PRIu64 "llu" - #define PRIx64 "llx" - #define PRIX64 "llX" -#endif - -//================================== GLOBALS =================================*/ -//byte g_str_buf[1024]; -byte* g_str_buf; -int load_so(byteptr); -void reload_so(); -void _vinit(int ___argc, voidptr ___argv); -void _vcleanup(); -#define sigaction_size sizeof(sigaction); -#define _ARR_LEN(a) ( (sizeof(a)) / (sizeof(a[0])) ) - +' + c_wyhash = ' // ============== wyhash ============== #ifndef wyhash_final_version_3 #define wyhash_final_version_3 @@ -363,7 +326,6 @@ void _vcleanup(); //includes #include -#include #if defined(_MSC_VER) && defined(_M_X64) #include #pragma intrinsic(_umul128) @@ -527,15 +489,215 @@ static inline void make_secret(uint64_t seed, uint64_t *secret){ } } #endif +' + c_helper_macros = '//============================== HELPER C MACROS =============================*/ +//#define tos4(s, slen) ((string){.str=(s), .len=(slen)}) +// `"" s` is used to enforce a string literal argument +#define _SLIT(s) ((string){.str=(byteptr)("" s), .len=(sizeof(s)-1), .is_lit=1}) +// take the address of an rvalue +#define ADDR(type, expr) (&((type[]){expr}[0])) +// copy something to the heap +#define HEAP(type, expr) ((type*)memdup((void*)&((type[]){expr}[0]), sizeof(type))) +#define _PUSH_MANY(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array_push_many(arr, tmp.data, tmp.len);} +#define _IN_MAP(val, m) map_exists(m, val) +' + c_headers = + c_helper_macros + c_unsigned_comparison_functions + c_common_macros + r' +// c_headers +typedef int (*qsort_callback_func)(const void*, const void*); +#include // TODO remove all these includes, define all function signatures and types manually +#include +#include + +#if defined(_WIN32) || defined(__CYGWIN__) + #define VV_EXPORTED_SYMBOL extern __declspec(dllexport) + #define VV_LOCAL_SYMBOL static +#else + // 4 < gcc < 5 is used by some older Ubuntu LTS and Centos versions, + // and does not support __has_attribute(visibility) ... + #ifndef __has_attribute + #define __has_attribute(x) 0 // Compatibility with non-clang compilers. + #endif + #if (defined(__GNUC__) && (__GNUC__ >= 4)) || (defined(__clang__) && __has_attribute(visibility)) + #ifdef ARM + #define VV_EXPORTED_SYMBOL extern __attribute__((externally_visible,visibility("default"))) + #else + #define VV_EXPORTED_SYMBOL extern __attribute__((visibility("default"))) + #endif + #define VV_LOCAL_SYMBOL __attribute__ ((visibility ("hidden"))) + #else + #define VV_EXPORTED_SYMBOL extern + #define VV_LOCAL_SYMBOL static + #endif +#endif + +#if defined(__TINYC__) && defined(__has_include) +// tcc does not support has_include properly yet, turn it off completely +#undef __has_include +#endif + +#ifndef _WIN32 + #if defined __has_include + #if __has_include () + #include + #else + // Most probably musl OR __ANDROID__ ... + int backtrace (void **__array, int __size) { return 0; } + char **backtrace_symbols (void *const *__array, int __size){ return 0; } + void backtrace_symbols_fd (void *const *__array, int __size, int __fd){} + #endif + #endif +#endif + +//#include "fns.h" +#include +#include // for va_list + +//================================== GLOBALS =================================*/ +//byte g_str_buf[1024]; +byte* g_str_buf; +int load_so(byteptr); +void reload_so(); +void _vinit(int ___argc, voidptr ___argv); +void _vcleanup(); +#define sigaction_size sizeof(sigaction); +#define _ARR_LEN(a) ( (sizeof(a)) / (sizeof(a[0])) ) void v_free(voidptr ptr); voidptr memdup(voidptr src, int sz); static voidptr memfreedup(voidptr ptr, voidptr src, int sz) { - v_free(ptr); + v_free(ptr); // heloe return memdup(src, sz); } -' - c_builtin_types = ' + + +#if INTPTR_MAX == INT32_MAX + #define TARGET_IS_32BIT 1 +#elif INTPTR_MAX == INT64_MAX + #define TARGET_IS_64BIT 1 +#else + #error "The environment is not 32 or 64-bit." +#endif + +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ || defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) + #define TARGET_ORDER_IS_BIG +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_IX86) + #define TARGET_ORDER_IS_LITTLE +#else + #error "Unknown architecture endianness" +#endif + +#ifndef _WIN32 + #include + #include // tolower + #include + #include // sleep + extern char **environ; +#endif + +#if defined(__CYGWIN__) && !defined(_WIN32) + #error Cygwin is not supported, please use MinGW or Visual Studio. +#endif + +#ifdef __linux__ + #include + #include // os__wait uses wait on nix +#endif + +#ifdef __FreeBSD__ + #include + #include // os__wait uses wait on nix +#endif + +#ifdef __DragonFly__ + #include + #include // os__wait uses wait on nix +#endif + +#ifdef __OpenBSD__ + #include + #include + #include // os__wait uses wait on nix +#endif + +#ifdef __NetBSD__ + #include // os__wait uses wait on nix +#endif + +#ifdef __sun + #include + #include // os__wait uses wait on nix +#endif + +#ifdef _WIN32 + #define WINVER 0x0600 + #ifdef _WIN32_WINNT + #undef _WIN32_WINNT + #endif + #define _WIN32_WINNT 0x0600 + #ifndef WIN32_FULL + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef _UNICODE + #define _UNICODE + #endif + #ifndef UNICODE + #define UNICODE + #endif + #include + + #include // _waccess + #include // _wgetcwd + + #ifdef _MSC_VER + // On MSVC these are the same (as long as /volatile:ms is passed) + #define _Atomic volatile + + // MSVC cannot parse some things properly + #undef EMPTY_STRUCT_DECLARATION + #undef OPTION_CAST + + #define EMPTY_STRUCT_DECLARATION int ____dummy_variable + #define OPTION_CAST(x) + #undef __NOINLINE + #undef __IRQHANDLER + #define __NOINLINE __declspec(noinline) + #define __IRQHANDLER __declspec(naked) + + #include + #pragma comment(lib, "Dbghelp") + #endif +#else + #include + #ifndef PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP + // musl does not have that + #define pthread_rwlockattr_setkind_np(a, b) + #endif +#endif + +// g_live_info is used by live.info() +static void* g_live_info = NULL; + +#if defined(__MINGW32__) || defined(__MINGW64__) || (defined(_WIN32) && defined(__TINYC__)) + #undef PRId64 + #undef PRIi64 + #undef PRIo64 + #undef PRIu64 + #undef PRIx64 + #undef PRIX64 + #define PRId64 "lld" + #define PRIi64 "lli" + #define PRIo64 "llo" + #define PRIu64 "llu" + #define PRIx64 "llx" + #define PRIX64 "llX" +#endif + +#ifdef _VFREESTANDING +#undef _VFREESTANDING +#endif +' + c_wyhash + c_builtin_types = ' //================================== builtin types ================================*/ typedef int64_t i64; typedef int16_t i16; @@ -569,12 +731,79 @@ typedef bool (*MapEqFn)(voidptr, voidptr); typedef void (*MapCloneFn)(voidptr, voidptr); typedef void (*MapFreeFn)(voidptr); ' - bare_c_headers = ' -$c_common_macros + bare_c_headers = c_helper_macros + c_unsigned_comparison_functions + + c_common_macros + + ' #ifndef exit #define exit(rc) sys_exit(rc) void sys_exit (int); #endif -' + +#define stdin 0 +#define stdout 1 +#define stderr 2 + +#define _VFREESTANDING + +typedef long unsigned int size_t; + +// Memory allocation related headers +// void *malloc(int size); +byte *calloc(int nitems, int size); +byte *realloc(byte *ptr, int size); +byte *memcpy(byte *dest, byte *src, int n); +byte *memset(byte *s, int c, int n); +byte *memmove(byte *dest, byte *src, int n); + +// Backtrace headers +int backtrace(void **buffer, int size); +char **backtrace_symbols(void *const *buffer, int size); +void backtrace_symbols_fd(void *const *buffer, int size, int fd); + +// I/O related headers +typedef void FILE; +FILE *popen(const char *command, const char *type); +int pclose(FILE *stream); +int fgetc(FILE *stream); +char *fgets(char *s, int size, FILE *stream); +int getc(FILE *stream); +int getchar(void); +int ungetc(int c, FILE *stream); + + +// char manipulation headers +int toupper(int c); +int tolower(int c); +// int toupper_l(int c, locale_t locale); +// int tolower_l(int c, locale_t locale); + +int isatty(int fd); + +// varargs implementation, TODO: works on tcc and gcc, but is very unportable and hacky +typedef __builtin_va_list va_list; +#define va_start(a, b) __builtin_va_start(a, b) +#define va_end(a) __builtin_va_end(a) +#define va_arg(a, b) __builtin_va_arg(a, b) +#define va_copy(a, b) __builtin_va_copy(a, b) + +//================================== GLOBALS =================================*/ +//byte g_str_buf[1024]; +byte* g_str_buf; +int load_so(byteptr); +void reload_so(); +void _vinit(int ___argc, voidptr ___argv); +void _vcleanup(); +#define sigaction_size sizeof(sigaction); +#define _ARR_LEN(a) ( (sizeof(a)) / (sizeof(a[0])) ) + +void v_free(voidptr ptr); +voidptr memdup(voidptr src, int sz); +static voidptr memfreedup(voidptr ptr, voidptr src, int sz) { + v_free(ptr); // heloe + return memdup(src, sz); +} + +' + + c_wyhash ) diff --git a/vlib/v/gen/c/str.v b/vlib/v/gen/c/str.v index 8203be08cc..d57f9350a6 100644 --- a/vlib/v/gen/c/str.v +++ b/vlib/v/gen/c/str.v @@ -6,136 +6,7 @@ import v.ast import v.util fn (mut g Gen) write_str_fn_definitions() { - // _STR function can't be defined in vlib - g.writeln(' -void _STR_PRINT_ARG(const char *fmt, char** refbufp, int *nbytes, int *memsize, int guess, ...) { - va_list args; - va_start(args, guess); - // NB: (*memsize - *nbytes) === how much free space is left at the end of the current buffer refbufp - // *memsize === total length of the buffer refbufp - // *nbytes === already occupied bytes of buffer refbufp - // guess === how many bytes were taken during the current vsnprintf run - for(;;) { - int remaining_space = *memsize - *nbytes; - if (guess < remaining_space) { - guess = vsnprintf(*refbufp + *nbytes, *memsize - *nbytes, fmt, args); - if (guess < remaining_space) { // result did fit into buffer - *nbytes += guess; - break; - } - } - // increase buffer (somewhat exponentially) - *memsize += guess + 3 * (*memsize) / 2; -#ifdef _VGCBOEHM - *refbufp = (char*)GC_REALLOC((void*)*refbufp, *memsize); -#else - *refbufp = (char*)realloc((void*)*refbufp, *memsize); -#endif - } - va_end(args); -} - -string _STR(const char *fmt, int nfmts, ...) { - va_list argptr; - int memsize = 128; - int nbytes = 0; -#ifdef _VGCBOEHM - char* buf = (char*)GC_MALLOC(memsize); -#else - char* buf = (char*)malloc(memsize); -#endif - va_start(argptr, nfmts); - for (int i=0; i= \'E\' && fup <= \'G\') { // floating point - _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+10, va_arg(argptr, double)); - } else if (f == \'p\') { - _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+14, va_arg(argptr, void*)); - } else if (f == \'C\') { // a C string - char* sptr = va_arg(argptr, char*); - char* fmt_no_c = (char*)v_malloc(k+4); - memcpy(fmt_no_c, fmt, k); - fmt_no_c[k-2]=\'C\'; - fmt_no_c[k-1]=\'"\'; - fmt_no_c[k]=\'%\'; - fmt_no_c[k+1]=\'s\'; - fmt_no_c[k+2]=\'"\'; - fmt_no_c[k+3]=0; - _STR_PRINT_ARG(fmt_no_c, &buf, &nbytes, &memsize, k + 4 + 100, sptr); - v_free(fmt_no_c); - } else if (f == \'s\') { // v string - string s = va_arg(argptr, string); - if (fmt[k-4] == \'*\') { // %*.*s - int fwidth = va_arg(argptr, int); - if (fwidth < 0) - fwidth -= (s.len - utf8_str_visible_length(s)); - else - fwidth += (s.len - utf8_str_visible_length(s)); - _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+s.len-4, fwidth, s.len, s.str); - } else { // %.*s - _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k+s.len-4, s.len, s.str); - } - } else { - //v_panic(tos3(\'Invaid format specifier\')); - } - } else { - _STR_PRINT_ARG(fmt, &buf, &nbytes, &memsize, k); - } - fmt += k+1; - } - va_end(argptr); - buf[nbytes] = 0; - -#ifdef DEBUG_ALLOC - //puts(\'_STR:\'); - puts(buf); -#endif - -#if _VAUTOFREE - //g_cur_str = (byteptr)buf; -#endif - return tos2((byteptr)buf); -} - -string _STR_TMP(const char *fmt, ...) { - va_list argptr; - va_start(argptr, fmt); - size_t len = vsnprintf(0, 0, fmt, argptr) + 1; - va_end(argptr); - va_start(argptr, fmt); - vsprintf((char *)g_str_buf, fmt, argptr); - va_end(argptr); - -#ifdef DEBUG_ALLOC - //puts(\'_STR_TMP:\'); - //puts(g_str_buf); -#endif - string res = tos(g_str_buf, len); - res.is_lit = 1; - return res; - -} // endof _STR_TMP - -') + g.writeln(c_str_fn_defs) } fn (mut g Gen) string_literal(node ast.StringLiteral) { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index ac2bd19953..f8c1f71139 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -916,6 +916,9 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt { mut args := []ast.AsmArg{} args_loop: for { + if p.prev_tok.position().line_nr < p.tok.position().line_nr { + break + } match p.tok.kind { .name { args << p.reg_or_alias() @@ -976,6 +979,9 @@ fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt { } else { break } + // if p.prev_tok.position().line_nr < p.tok.position().line_nr { + // break + // } } mut comments := []ast.Comment{} for p.tok.kind == .comment { diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index 98addd7776..32505f2192 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -596,6 +596,9 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences res.path += '.v' } } + if !res.is_bare && res.bare_builtin_dir != '' { + eprintln('`-bare-builtin-dir` must be used with `-freestanding`') + } if command == 'build-module' { res.build_mode = .build_module res.path = args[command_pos + 1] diff --git a/vlib/v/pref/should_compile.v b/vlib/v/pref/should_compile.v index 80f4196c7c..a7951021be 100644 --- a/vlib/v/pref/should_compile.v +++ b/vlib/v/pref/should_compile.v @@ -8,9 +8,6 @@ pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files_ []s files.sort() mut all_v_files := []string{} for file in files { - if prefs.is_bare && os.join_path(dir, file).ends_with('/vlib/builtin/builtin_nix.c.v') { - continue - } if !file.ends_with('.v') && !file.ends_with('.vh') { continue } @@ -123,22 +120,22 @@ pub fn (prefs &Preferences) should_compile_c(file string) bool { // Probably something like `a.js.v`. return false } + if prefs.is_bare && file.ends_with('.freestanding.v') { + return true + } if prefs.os == .all { return true } - if !prefs.is_bare && file.ends_with('.freestanding.v') { + if prefs.backend != .x64 && file.ends_with('_x64.v') { return false } - if (file.ends_with('_windows.c.v') || file.ends_with('_windows.v')) && prefs.os != .windows { + if prefs.os != .windows && (file.ends_with('_windows.c.v') || file.ends_with('_windows.v')) { return false } - if (file.ends_with('_linux.c.v') || file.ends_with('_linux.v')) && prefs.os != .linux { + if prefs.os != .linux && (file.ends_with('_linux.c.v') || file.ends_with('_linux.v')) { return false } - if (file.ends_with('_darwin.c.v') || file.ends_with('_darwin.v')) && prefs.os != .macos { - return false - } - if (file.ends_with('_macos.c.v') || file.ends_with('_macos.v')) && prefs.os != .macos { + if prefs.os != .macos && (file.ends_with('_darwin.c.v') || file.ends_with('_darwin.v')) { return false } if (file.ends_with('_ios.c.v') || file.ends_with('_ios.v')) && prefs.os != .ios { @@ -147,25 +144,28 @@ pub fn (prefs &Preferences) should_compile_c(file string) bool { if file.ends_with('_nix.c.v') && prefs.os == .windows { return false } - if file.ends_with('_android.c.v') && prefs.os != .android { + if prefs.os != .macos && (file.ends_with('_macos.c.v') || file.ends_with('_macos.v')) { return false } - if file.ends_with('_freebsd.c.v') && prefs.os != .freebsd { + if prefs.os == .windows && file.ends_with('_nix.c.v') { return false } - if file.ends_with('_openbsd.c.v') && prefs.os != .openbsd { + if prefs.os != .android && file.ends_with('_android.c.v') { return false } - if file.ends_with('_netbsd.c.v') && prefs.os != .netbsd { + if prefs.os != .freebsd && file.ends_with('_freebsd.c.v') { return false } - if file.ends_with('_dragonfly.c.v') && prefs.os != .dragonfly { + if prefs.os != .openbsd && file.ends_with('_openbsd.c.v') { return false } - if file.ends_with('_solaris.c.v') && prefs.os != .solaris { + if prefs.os != .netbsd && file.ends_with('_netbsd.c.v') { return false } - if file.ends_with('_x64.v') && prefs.backend != .x64 { + if prefs.os != .dragonfly && file.ends_with('_dragonfly.c.v') { + return false + } + if prefs.os != .solaris && file.ends_with('_solaris.c.v') { return false } return true