all: support `-gc boehm` on systems with libgc-dev installed (#9382)

pull/9386/head
Delyan Angelov 2021-03-20 15:16:36 +02:00 committed by GitHub
parent a6ddd24f5c
commit 8810af76df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 135 additions and 19 deletions

View File

@ -70,6 +70,20 @@ see also `v help build`.
Some short lived applications, like compilers and other CLI tools are Some short lived applications, like compilers and other CLI tools are
more performant without autofree. more performant without autofree.
-gc <mode>
Use and link an optional garbage collector.
Only `-gc boehm` is supported currently. You need to install a
`libgc-dev` package first, or install it manually from source:
https://github.com/ivmai/bdwgc
Note, this option is complementary to -autofree. The Boehm garbage
collector is conservative, and it may make your program significantly
slower if it does many small allocations in a loop. This option
is intended *mainly* for reducing the memory usage of programs, that
process large amounts of text in *batch mode* on low/limited memory
environments like small VPSes, and for which a few ms of garbage
collection pauses from time to time *do not matter much*.
# Miscellaneous: # Miscellaneous:
-printfn <fn_name> -printfn <fn_name>
Print the content of the generated C function named fn_name. Print the content of the generated C function named fn_name.

View File

@ -3698,6 +3698,9 @@ If you do need a custom flag file, that has platform dependent code, use the
postfix `_d_customflag.v`, and then use plaftorm dependent compile time postfix `_d_customflag.v`, and then use plaftorm dependent compile time
conditional blocks inside it, i.e. `$if linux {}` etc. conditional blocks inside it, i.e. `$if linux {}` etc.
- `_notd_customflag.v` => similar to _d_customflag.v, but will be used
*only* if you do NOT pass `-d customflag` to V.
## Compile time pseudo variables ## Compile time pseudo variables
V also gives your code access to a set of pseudo string variables, V also gives your code access to a set of pseudo string variables,

View File

@ -482,7 +482,7 @@ pub fn (a &array) free() {
// if a.is_slice { // if a.is_slice {
// return // return
// } // }
C.free(a.data) unsafe { free(a.data) }
} }
[unsafe] [unsafe]
@ -493,7 +493,7 @@ pub fn (mut a []string) free() {
for s in a { for s in a {
unsafe { s.free() } unsafe { s.free() }
} }
C.free(a.data) unsafe { free(a.data) }
} }
// str returns a string representation of the array of strings // str returns a string representation of the array of strings

View File

@ -184,7 +184,11 @@ pub fn malloc(n int) byteptr {
} }
nr_mallocs++ nr_mallocs++
} $else { } $else {
res = unsafe { C.malloc(n) } $if gcboehm ? {
res = unsafe { C.GC_MALLOC(n) }
} $else {
res = unsafe { C.malloc(n) }
}
if res == 0 { if res == 0 {
panic('malloc($n) failed') panic('malloc($n) failed')
} }
@ -214,7 +218,11 @@ pub fn v_realloc(b byteptr, n int) byteptr {
C.memcpy(new_ptr, b, n) C.memcpy(new_ptr, b, n)
} }
} $else { } $else {
new_ptr = unsafe { C.realloc(b, n) } $if gcboehm ? {
new_ptr = unsafe { C.GC_REALLOC(b, n) }
} $else {
new_ptr = unsafe { C.realloc(b, n) }
}
if new_ptr == 0 { if new_ptr == 0 {
panic('realloc($n) failed') panic('realloc($n) failed')
} }
@ -254,11 +262,16 @@ pub fn realloc_data(old_data byteptr, old_size int, new_size int) byteptr {
min_size := if old_size < new_size { old_size } else { new_size } min_size := if old_size < new_size { old_size } else { new_size }
C.memcpy(new_ptr, old_data, min_size) C.memcpy(new_ptr, old_data, min_size)
C.memset(old_data, 0x57, old_size) C.memset(old_data, 0x57, old_size)
C.free(old_data) free(old_data)
return new_ptr return new_ptr
} }
} }
nptr := unsafe { C.realloc(old_data, new_size) } mut nptr := byteptr(0)
$if gcboehm ? {
nptr = unsafe { C.GC_REALLOC(old_data, new_size) }
} $else {
nptr = unsafe { C.realloc(old_data, new_size) }
}
if nptr == 0 { if nptr == 0 {
panic('realloc_data($old_data, $old_size, $new_size) failed') panic('realloc_data($old_data, $old_size, $new_size) failed')
} }
@ -268,7 +281,11 @@ pub fn realloc_data(old_data byteptr, old_size int, new_size int) byteptr {
// v_calloc dynamically allocates a zeroed `n` bytes block of memory on the heap. // v_calloc dynamically allocates a zeroed `n` bytes block of memory on the heap.
// v_calloc returns a `byteptr` pointing to the memory address of the allocated space. // v_calloc returns a `byteptr` pointing to the memory address of the allocated space.
pub fn v_calloc(n int) byteptr { pub fn v_calloc(n int) byteptr {
return C.calloc(1, n) $if gcboehm ? {
return C.GC_MALLOC(n)
} $else {
return C.calloc(1, n)
}
} }
// vcalloc dynamically allocates a zeroed `n` bytes block of memory on the heap. // vcalloc dynamically allocates a zeroed `n` bytes block of memory on the heap.
@ -280,7 +297,11 @@ pub fn vcalloc(n int) byteptr {
} else if n == 0 { } else if n == 0 {
return byteptr(0) return byteptr(0)
} }
return C.calloc(1, n) $if gcboehm ? {
return C.GC_MALLOC(n)
} $else {
return C.calloc(1, n)
}
} }
// free allows for manually freeing memory allocated at the address `ptr`. // free allows for manually freeing memory allocated at the address `ptr`.
@ -289,6 +310,10 @@ pub fn free(ptr voidptr) {
$if prealloc { $if prealloc {
return return
} }
$if gcboehm ? {
C.GC_FREE(ptr)
return
}
C.free(ptr) C.free(ptr)
} }
@ -306,14 +331,6 @@ pub fn memdup(src voidptr, sz int) voidptr {
} }
} }
// v_ptr_free is used internally to manually free up memory allocated at the address `ptr`.
fn v_ptr_free(ptr voidptr) {
$if prealloc {
return
}
C.free(ptr)
}
// is_atty returns 1 if the `fd` file descriptor is open and refers to a terminal // is_atty returns 1 if the `fd` file descriptor is open and refers to a terminal
pub fn is_atty(fd int) int { pub fn is_atty(fd int) int {
$if windows { $if windows {

View File

@ -0,0 +1,13 @@
module builtin
#define GC_THREADS 1
#include <gc.h>
#flag -lgc
fn C.GC_MALLOC(n size_t) voidptr
fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr
fn C.GC_FREE(ptr voidptr)

View File

@ -0,0 +1,11 @@
module builtin
// Just define the C functions, so that V does not error because of the missing definitions.
// NB: they will NOT be used, since calls to them are wrapped with `$if gcboehm ? { }`
fn C.GC_MALLOC(n size_t) voidptr
fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr
fn C.GC_FREE(ptr voidptr)

View File

@ -91,6 +91,6 @@ pub fn (ie &IError) free() {
unsafe { unsafe {
ie.msg.free() ie.msg.free()
cie := &C.IError(ie) cie := &C.IError(ie)
C.free(cie._object) free(cie._object)
} }
} }

View File

@ -422,6 +422,9 @@ pub fn (mut g Gen) init() {
} }
g.comptime_defines.writeln('') g.comptime_defines.writeln('')
} }
if g.pref.gc_mode == .boehm {
g.comptime_defines.writeln('#define _VGCBOEHM (1)')
}
if g.pref.is_debug || 'debug' in g.pref.compile_defines { if g.pref.is_debug || 'debug' in g.pref.compile_defines {
g.comptime_defines.writeln('#define _VDEBUG (1)') g.comptime_defines.writeln('#define _VDEBUG (1)')
} }

View File

@ -454,9 +454,10 @@ void _vcleanup();
} }
#endif #endif
void v_free(voidptr ptr);
voidptr memdup(voidptr src, int sz); voidptr memdup(voidptr src, int sz);
static voidptr memfreedup(voidptr ptr, voidptr src, int sz) { static voidptr memfreedup(voidptr ptr, voidptr src, int sz) {
free(ptr); v_free(ptr);
return memdup(src, sz); return memdup(src, sz);
} }
' '

View File

@ -533,6 +533,9 @@ fn (mut g Gen) comp_if_to_ifdef(name string, is_comptime_optional bool) ?string
return '__cplusplus' return '__cplusplus'
} }
// other: // other:
'gcboehm' {
return '_VGCBOEHM'
}
'debug' { 'debug' {
return '_VDEBUG' return '_VDEBUG'
} }

View File

@ -26,7 +26,11 @@ void _STR_PRINT_ARG(const char *fmt, char** refbufp, int *nbytes, int *memsize,
} }
// increase buffer (somewhat exponentially) // increase buffer (somewhat exponentially)
*memsize += (*memsize + *memsize) / 3 + guess; *memsize += (*memsize + *memsize) / 3 + guess;
#ifdef _VGCBOEHM
*refbufp = (char*)GC_REALLOC((void*)*refbufp, *memsize);
#else
*refbufp = (char*)realloc((void*)*refbufp, *memsize); *refbufp = (char*)realloc((void*)*refbufp, *memsize);
#endif
} }
va_end(args); va_end(args);
} }
@ -35,7 +39,11 @@ string _STR(const char *fmt, int nfmts, ...) {
va_list argptr; va_list argptr;
int memsize = 128; int memsize = 128;
int nbytes = 0; int nbytes = 0;
#ifdef _VGCBOEHM
char* buf = (char*)GC_MALLOC(memsize);
#else
char* buf = (char*)malloc(memsize); char* buf = (char*)malloc(memsize);
#endif
va_start(argptr, nfmts); va_start(argptr, nfmts);
for (int i=0; i<nfmts; ++i) { for (int i=0; i<nfmts; ++i) {
int k = strlen(fmt); int k = strlen(fmt);
@ -84,11 +92,18 @@ string _STR(const char *fmt, int nfmts, ...) {
} }
va_end(argptr); va_end(argptr);
buf[nbytes] = 0; buf[nbytes] = 0;
#ifdef _VGCBOEHM
buf = (char*)GC_REALLOC((void*)buf, nbytes+1);
#else
buf = (char*)realloc((void*)buf, nbytes+1); buf = (char*)realloc((void*)buf, nbytes+1);
#endif
#ifdef DEBUG_ALLOC #ifdef DEBUG_ALLOC
//puts('_STR:'); //puts('_STR:');
puts(buf); puts(buf);
#endif #endif
#if _VAUTOFREE #if _VAUTOFREE
//g_cur_str = (byteptr)buf; //g_cur_str = (byteptr)buf;
#endif #endif

View File

@ -17,6 +17,11 @@ pub enum BuildMode {
build_module build_module
} }
pub enum GarbageCollectionMode {
no_gc
boehm
}
pub enum OutputMode { pub enum OutputMode {
stdout stdout
silent silent
@ -153,6 +158,7 @@ pub mut:
build_options []string // list of options, that should be passed down to `build-module`, if needed for -usecache build_options []string // list of options, that should be passed down to `build-module`, if needed for -usecache
cache_manager vcache.CacheManager cache_manager vcache.CacheManager
is_help bool // -h, -help or --help was passed is_help bool // -h, -help or --help was passed
gc_mode GarbageCollectionMode = .no_gc // .no_gc, .boehm
// checker settings: // checker settings:
checker_match_exhaustive_cutoff_limit int = 10 checker_match_exhaustive_cutoff_limit int = 10
} }
@ -177,7 +183,7 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
target_arch := cmdline.option(current_args, '-arch', '') target_arch := cmdline.option(current_args, '-arch', '')
i++ i++
target_arch_kind := arch_from_string(target_arch) or { target_arch_kind := arch_from_string(target_arch) or {
eprintln('unknown architercture target `$target_arch`') eprintln('unknown architecture target `$target_arch`')
exit(1) exit(1)
} }
res.arch = target_arch_kind res.arch = target_arch_kind
@ -214,6 +220,23 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
'-silent' { '-silent' {
res.output_mode = .silent res.output_mode = .silent
} }
'-gc' {
gc_mode := cmdline.option(current_args, '-gc', '')
match gc_mode {
'' {
res.gc_mode = .no_gc
}
'boehm' {
res.gc_mode = .boehm
parse_define(mut res, 'gcboehm')
}
else {
eprintln('unknown garbage collection mode, only `-gc boehm` is supported')
exit(1)
}
}
i++
}
'-g' { '-g' {
res.is_debug = true res.is_debug = true
res.is_vlines = true res.is_vlines = true

View File

@ -40,6 +40,19 @@ pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files_ []s
continue continue
} }
} }
if file.contains('_notd_') {
mut allowed := true
for cdefine in prefs.compile_defines {
file_postfix := '_notd_${cdefine}.v'
if file.ends_with(file_postfix) {
allowed = false
break
}
}
if !allowed {
continue
}
}
all_v_files << os.join_path(dir, file) all_v_files << os.join_path(dir, file)
} }
// //