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). and unsafe{free(x)} calls manually in this mode).
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>

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)
} }
// //