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

@ -69,6 +69,20 @@ see also `v help build`.
and unsafe{free(x)} calls manually in this mode).
Some short lived applications, like compilers and other CLI tools are
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:
-printfn <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
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
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 {
// return
// }
C.free(a.data)
unsafe { free(a.data) }
}
[unsafe]
@ -493,7 +493,7 @@ pub fn (mut a []string) free() {
for s in a {
unsafe { s.free() }
}
C.free(a.data)
unsafe { free(a.data) }
}
// str returns a string representation of the array of strings

View File

@ -184,7 +184,11 @@ pub fn malloc(n int) byteptr {
}
nr_mallocs++
} $else {
res = unsafe { C.malloc(n) }
$if gcboehm ? {
res = unsafe { C.GC_MALLOC(n) }
} $else {
res = unsafe { C.malloc(n) }
}
if res == 0 {
panic('malloc($n) failed')
}
@ -214,7 +218,11 @@ pub fn v_realloc(b byteptr, n int) byteptr {
C.memcpy(new_ptr, b, n)
}
} $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 {
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 }
C.memcpy(new_ptr, old_data, min_size)
C.memset(old_data, 0x57, old_size)
C.free(old_data)
free(old_data)
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 {
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 returns a `byteptr` pointing to the memory address of the allocated space.
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.
@ -280,7 +297,11 @@ pub fn vcalloc(n int) byteptr {
} else if n == 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`.
@ -289,6 +310,10 @@ pub fn free(ptr voidptr) {
$if prealloc {
return
}
$if gcboehm ? {
C.GC_FREE(ptr)
return
}
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
pub fn is_atty(fd int) int {
$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 {
ie.msg.free()
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('')
}
if g.pref.gc_mode == .boehm {
g.comptime_defines.writeln('#define _VGCBOEHM (1)')
}
if g.pref.is_debug || 'debug' in g.pref.compile_defines {
g.comptime_defines.writeln('#define _VDEBUG (1)')
}

View File

@ -454,9 +454,10 @@ void _vcleanup();
}
#endif
void v_free(voidptr ptr);
voidptr memdup(voidptr src, int sz);
static voidptr memfreedup(voidptr ptr, voidptr src, int sz) {
free(ptr);
v_free(ptr);
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'
}
// other:
'gcboehm' {
return '_VGCBOEHM'
}
'debug' {
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)
*memsize += (*memsize + *memsize) / 3 + guess;
#ifdef _VGCBOEHM
*refbufp = (char*)GC_REALLOC((void*)*refbufp, *memsize);
#else
*refbufp = (char*)realloc((void*)*refbufp, *memsize);
#endif
}
va_end(args);
}
@ -35,7 +39,11 @@ 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<nfmts; ++i) {
int k = strlen(fmt);
@ -84,11 +92,18 @@ string _STR(const char *fmt, int nfmts, ...) {
}
va_end(argptr);
buf[nbytes] = 0;
#ifdef _VGCBOEHM
buf = (char*)GC_REALLOC((void*)buf, nbytes+1);
#else
buf = (char*)realloc((void*)buf, nbytes+1);
#endif
#ifdef DEBUG_ALLOC
//puts('_STR:');
puts(buf);
#endif
#if _VAUTOFREE
//g_cur_str = (byteptr)buf;
#endif

View File

@ -17,6 +17,11 @@ pub enum BuildMode {
build_module
}
pub enum GarbageCollectionMode {
no_gc
boehm
}
pub enum OutputMode {
stdout
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
cache_manager vcache.CacheManager
is_help bool // -h, -help or --help was passed
gc_mode GarbageCollectionMode = .no_gc // .no_gc, .boehm
// checker settings:
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', '')
i++
target_arch_kind := arch_from_string(target_arch) or {
eprintln('unknown architercture target `$target_arch`')
eprintln('unknown architecture target `$target_arch`')
exit(1)
}
res.arch = target_arch_kind
@ -214,6 +220,23 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
'-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' {
res.is_debug = true
res.is_vlines = true

View File

@ -40,6 +40,19 @@ pub fn (prefs &Preferences) should_compile_filtered_files(dir string, files_ []s
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)
}
//