Skip to content

Commit 10bf974

Browse files
authored
all: gc: provide optimized mode (#9716)
1 parent 8c95f07 commit 10bf974

26 files changed

+11724
-49
lines changed

.github/workflows/ci.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,27 +124,27 @@ jobs:
124124
run: |
125125
thirdparty/tcc/tcc.exe -version
126126
./v -cg -o v cmd/v # Make sure vtcc can build itself twice
127-
- name: v self compilation with -gc boehm
127+
- name: v self compilation with -gc boehm_full_opt
128128
run: |
129-
./v -gc boehm -o v2 cmd/v && ./v2 -gc boehm -o v3 cmd/v && ./v3 -gc boehm -o v4 cmd/v
129+
./v -gc boehm_full_opt -o v2 cmd/v && ./v2 -gc boehm_full_opt -o v3 cmd/v && ./v3 -gc boehm_full_opt -o v4 cmd/v
130130
mv v4 v
131131
- name: v doctor
132132
run: |
133133
./v doctor
134-
- name: Verify `v -gc boehm test` works
134+
- name: Verify `v -gc boehm_full_opt test` works
135135
run: |
136-
./v -gc boehm cmd/tools/test_if_v_test_system_works.v
136+
./v -gc boehm_full_opt cmd/tools/test_if_v_test_system_works.v
137137
./cmd/tools/test_if_v_test_system_works
138-
- name: Self tests with `-gc boehm` with V compiler using Boehm-GC itself
139-
run: ./v -gc boehm -silent test-self
138+
- name: Self tests with `-gc boehm_full_opt` with V compiler using Boehm-GC itself
139+
run: ./v -gc boehm_full_opt -silent test-self
140140
- name: Test leak detector
141141
run: |
142142
./v -gc boehm_leak -o testcase_leak vlib/v/tests/testcase_leak.v
143143
./testcase_leak 2>leaks.txt
144144
grep "Found 1 leaked object" leaks.txt && grep ", sz=1000," leaks.txt
145-
- name: Test leak detector not being active for `-gc boehm`
145+
- name: Test leak detector not being active for `-gc boehm_full_opt`
146146
run: |
147-
./v -gc boehm -o testcase_leak vlib/v/tests/testcase_leak.v
147+
./v -gc boehm_full_opt -o testcase_leak vlib/v/tests/testcase_leak.v
148148
./testcase_leak 2>leaks.txt
149149
[ "$(stat -c %s leaks.txt)" = "0" ]
150150
- name: Test leak detector not being active for normal compile

cmd/v/help/build-c.txt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,12 @@ see also `v help build`.
7474
Use and link an optional garbage collector. Only tThe Boehm–Demers–Weiser
7575
garbage collector is supported currently with the following sub-options:
7676

77-
`-gc boehm` ........ selects the default mode for the architecture
78-
`-gc boehm_full` ... full garbage collection mode
79-
`-gc boehm_incr` ... incremental/generational garbage collection mode
80-
`-gc boehm_leak` ... leak detection mode
77+
`-gc boehm` ........... selects the default mode for the architecture
78+
`-gc boehm_full` ...... full garbage collection mode
79+
`-gc boehm_incr` ...... incremental/generational garbage collection mode
80+
`-gc boehm_full_opt` .. optimized full garbage collection mode
81+
`-gc boehm_incr_opt` .. optimized incremental/generational garbage collection mode
82+
`-gc boehm_leak` ...... leak detection mode
8183

8284
You need to install a `libgc-dev` package first, or install it manually from:
8385

vlib/builtin/array.v

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ fn (mut a array) ensure_cap(required int) {
9696
}
9797
new_size := cap * a.element_size
9898
mut new_data := &byte(0)
99-
if a.cap > 0 {
99+
if a.data != voidptr(0) {
100100
new_data = unsafe { realloc_data(a.data, a.cap * a.element_size, new_size) }
101101
} else {
102102
new_data = vcalloc(new_size)
@@ -656,3 +656,167 @@ pub fn (data voidptr) vbytes(len int) []byte {
656656
pub fn (data &byte) vbytes(len int) []byte {
657657
return unsafe { voidptr(data).vbytes(len) }
658658
}
659+
660+
// non-pub "noscan" versions of some above functions
661+
fn __new_array_noscan(mylen int, cap int, elm_size int) array {
662+
cap_ := if cap < mylen { mylen } else { cap }
663+
arr := array{
664+
element_size: elm_size
665+
data: vcalloc_noscan(cap_ * elm_size)
666+
len: mylen
667+
cap: cap_
668+
}
669+
return arr
670+
}
671+
672+
fn __new_array_with_default_noscan(mylen int, cap int, elm_size int, val voidptr) array {
673+
cap_ := if cap < mylen { mylen } else { cap }
674+
mut arr := array{
675+
element_size: elm_size
676+
data: vcalloc_noscan(cap_ * elm_size)
677+
len: mylen
678+
cap: cap_
679+
}
680+
if val != 0 {
681+
for i in 0 .. arr.len {
682+
unsafe { arr.set_unsafe(i, val) }
683+
}
684+
}
685+
return arr
686+
}
687+
688+
fn __new_array_with_array_default_noscan(mylen int, cap int, elm_size int, val array) array {
689+
cap_ := if cap < mylen { mylen } else { cap }
690+
mut arr := array{
691+
element_size: elm_size
692+
data: vcalloc_noscan(cap_ * elm_size)
693+
len: mylen
694+
cap: cap_
695+
}
696+
for i in 0 .. arr.len {
697+
val_clone := val.clone()
698+
unsafe { arr.set_unsafe(i, &val_clone) }
699+
}
700+
return arr
701+
}
702+
703+
// Private function, used by V (`nums := [1, 2, 3]`)
704+
fn new_array_from_c_array_noscan(len int, cap int, elm_size int, c_array voidptr) array {
705+
cap_ := if cap < len { len } else { cap }
706+
arr := array{
707+
element_size: elm_size
708+
data: vcalloc_noscan(cap_ * elm_size)
709+
len: len
710+
cap: cap_
711+
}
712+
// TODO Write all memory functions (like memcpy) in V
713+
unsafe { C.memcpy(arr.data, c_array, len * elm_size) }
714+
return arr
715+
}
716+
717+
fn (a array) repeat_noscan(count int) array {
718+
if count < 0 {
719+
panic('array.repeat: count is negative: $count')
720+
}
721+
mut size := count * a.len * a.element_size
722+
if size == 0 {
723+
size = a.element_size
724+
}
725+
arr := array{
726+
element_size: a.element_size
727+
data: vcalloc_noscan(size)
728+
len: count * a.len
729+
cap: count * a.len
730+
}
731+
size_of_array := int(sizeof(array))
732+
for i in 0 .. count {
733+
if a.len > 0 && a.element_size == size_of_array {
734+
ary := array{}
735+
unsafe { C.memcpy(&ary, a.data, size_of_array) }
736+
ary_clone := ary.clone()
737+
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &ary_clone, a.len * a.element_size) }
738+
} else {
739+
unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(a.data), a.len * a.element_size) }
740+
}
741+
}
742+
return arr
743+
}
744+
745+
pub fn (a &array) clone_noscan() array {
746+
mut size := a.cap * a.element_size
747+
if size == 0 {
748+
size++
749+
}
750+
mut arr := array{
751+
element_size: a.element_size
752+
data: vcalloc_noscan(size)
753+
len: a.len
754+
cap: a.cap
755+
}
756+
// Recursively clone-generated elements if array element is array type
757+
size_of_array := int(sizeof(array))
758+
if a.element_size == size_of_array {
759+
mut is_elem_array := true
760+
for i in 0 .. a.len {
761+
ar := array{}
762+
unsafe { C.memcpy(&ar, a.get_unsafe(i), size_of_array) }
763+
if ar.len > ar.cap || ar.cap <= 0 || ar.element_size <= 0 {
764+
is_elem_array = false
765+
break
766+
}
767+
ar_clone := ar.clone()
768+
unsafe { arr.set_unsafe(i, &ar_clone) }
769+
}
770+
if is_elem_array {
771+
return arr
772+
}
773+
}
774+
775+
if !isnil(a.data) {
776+
unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) }
777+
}
778+
return arr
779+
}
780+
781+
fn (a &array) slice_clone_noscan(start int, _end int) array {
782+
mut end := _end
783+
$if !no_bounds_checking ? {
784+
if start > end {
785+
panic('array.slice: invalid slice index ($start > $end)')
786+
}
787+
if end > a.len {
788+
panic('array.slice: slice bounds out of range ($end >= $a.len)')
789+
}
790+
if start < 0 {
791+
panic('array.slice: slice bounds out of range ($start < 0)')
792+
}
793+
}
794+
mut data := &byte(0)
795+
unsafe {
796+
data = &byte(a.data) + start * a.element_size
797+
}
798+
l := end - start
799+
res := array{
800+
element_size: a.element_size
801+
data: data
802+
len: l
803+
cap: l
804+
}
805+
return res.clone_noscan()
806+
}
807+
808+
fn (a array) reverse_noscan() array {
809+
if a.len < 2 {
810+
return a
811+
}
812+
mut arr := array{
813+
element_size: a.element_size
814+
data: vcalloc_noscan(a.cap * a.element_size)
815+
len: a.len
816+
cap: a.cap
817+
}
818+
for i in 0 .. a.len {
819+
unsafe { arr.set_unsafe(i, a.get_unsafe(a.len - 1 - i)) }
820+
}
821+
return arr
822+
}

vlib/builtin/builtin.c.v

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ pub fn realloc_data(old_data &byte, old_size int, new_size int) &byte {
302302
// Unlike `v_calloc` vcalloc checks for negative values given in `n`.
303303
pub fn vcalloc(n int) &byte {
304304
if n < 0 {
305-
panic('calloc(<=0)')
305+
panic('calloc(<0)')
306306
} else if n == 0 {
307307
return &byte(0)
308308
}
@@ -313,6 +313,24 @@ pub fn vcalloc(n int) &byte {
313313
}
314314
}
315315

316+
// special versions of the above that allocate memory which is not scanned
317+
// for pointers (but is collected) when the Boehm garbage collection is used
318+
pub fn vcalloc_noscan(n int) &byte {
319+
$if gcboehm ? {
320+
$if vplayground ? {
321+
if n > 10000 {
322+
panic('allocating more than 10 KB is not allowed in the playground')
323+
}
324+
}
325+
if n < 0 {
326+
panic('calloc(<0)')
327+
}
328+
return &byte(unsafe { C.memset(C.GC_MALLOC_ATOMIC(n), 0, n) })
329+
} $else {
330+
return unsafe { vcalloc(n) }
331+
}
332+
}
333+
316334
// free allows for manually freeing memory allocated at the address `ptr`.
317335
[unsafe]
318336
pub fn free(ptr voidptr) {

vlib/builtin/builtin_d_gcboehm.v

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ $if gcboehm_leak ? {
3434
// compiled with `-gc boehm` or `-gc boehm_leak`.
3535
fn C.GC_MALLOC(n size_t) voidptr
3636

37+
fn C.GC_MALLOC_ATOMIC(n size_t) voidptr
38+
3739
fn C.GC_MALLOC_UNCOLLECTABLE(n size_t) voidptr
3840

3941
fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr

vlib/builtin/builtin_notd_gcboehm.v

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ module builtin
66

77
fn C.GC_MALLOC(n size_t) voidptr
88

9+
fn C.GC_MALLOC_ATOMIC(n size_t) voidptr
10+
911
fn C.GC_MALLOC_UNCOLLECTABLE(n size_t) voidptr
1012

1113
fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr

vlib/v/gen/c/array.v

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@ fn (mut g Gen) array_init(node ast.ArrayInit) {
5050
if node.exprs.len == 0 {
5151
elem_sym := g.table.get_type_symbol(node.elem_type)
5252
is_default_array := elem_sym.kind == .array && node.has_default
53+
noscan := g.check_noscan(node.elem_type)
5354
if is_default_array {
54-
g.write('__new_array_with_array_default(')
55+
g.write('__new_array_with_array_default${noscan}(')
5556
} else {
56-
g.write('__new_array_with_default(')
57+
g.write('__new_array_with_default${noscan}(')
5758
}
5859
if node.has_len {
5960
g.expr(node.len_expr)
@@ -145,7 +146,8 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) {
145146
g.expr(node.left)
146147
g.writeln(';')
147148
g.writeln('int ${tmp}_len = ${tmp}_orig.len;')
148-
g.writeln('$ret_typ $tmp = __new_array(0, ${tmp}_len, sizeof($ret_elem_type));\n')
149+
noscan := g.check_noscan(ret_info.elem_type)
150+
g.writeln('$ret_typ $tmp = __new_array${noscan}(0, ${tmp}_len, sizeof($ret_elem_type));\n')
149151
i := g.new_tmp_var()
150152
g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {')
151153
g.writeln('\t$inp_elem_type it = (($inp_elem_type*) ${tmp}_orig.data)[$i];')
@@ -337,7 +339,8 @@ fn (mut g Gen) gen_array_filter(node ast.CallExpr) {
337339
g.expr(node.left)
338340
g.writeln(';')
339341
g.writeln('int ${tmp}_len = ${tmp}_orig.len;')
340-
g.writeln('$styp $tmp = __new_array(0, ${tmp}_len, sizeof($elem_type_str));\n')
342+
noscan := g.check_noscan(info.elem_type)
343+
g.writeln('$styp $tmp = __new_array${noscan}(0, ${tmp}_len, sizeof($elem_type_str));\n')
341344
i := g.new_tmp_var()
342345
g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {')
343346
g.writeln('\t$elem_type_str it = (($elem_type_str*) ${tmp}_orig.data)[$i];')

0 commit comments

Comments
 (0)