GC-boehm: extend optimized mode to all `array` methods (#10406)
parent
9a9d539e6f
commit
0e2c86310a
|
@ -647,7 +647,11 @@ pub fn (data &byte) vbytes(len int) []byte {
|
|||
return unsafe { voidptr(data).vbytes(len) }
|
||||
}
|
||||
|
||||
// non-pub "noscan" versions of some above functions
|
||||
// non-pub versions of array functions
|
||||
// that allocale new memory using `GC_MALLOC_ATOMIC()`
|
||||
// when `-gc boehm_*_opt` is used. These memory areas are not
|
||||
// scanned for pointers.
|
||||
|
||||
fn __new_array_noscan(mylen int, cap int, elm_size int) array {
|
||||
cap_ := if cap < mylen { mylen } else { cap }
|
||||
arr := array{
|
||||
|
@ -704,6 +708,26 @@ fn new_array_from_c_array_noscan(len int, cap int, elm_size int, c_array voidptr
|
|||
return arr
|
||||
}
|
||||
|
||||
// Private function. Doubles array capacity if needed.
|
||||
fn (mut a array) ensure_cap_noscan(required int) {
|
||||
if required <= a.cap {
|
||||
return
|
||||
}
|
||||
mut cap := if a.cap > 0 { a.cap } else { 2 }
|
||||
for required > cap {
|
||||
cap *= 2
|
||||
}
|
||||
new_size := cap * a.element_size
|
||||
new_data := vcalloc_noscan(new_size)
|
||||
if a.data != voidptr(0) {
|
||||
unsafe { C.memcpy(new_data, a.data, a.len * a.element_size) }
|
||||
// TODO: the old data may be leaked when no GC is used (ref-counting?)
|
||||
}
|
||||
a.data = new_data
|
||||
a.offset = 0
|
||||
a.cap = cap
|
||||
}
|
||||
|
||||
// repeat returns a new array with the given array elements repeated given times.
|
||||
// `cgen` will replace this with an apropriate call to `repeat_to_depth()`
|
||||
|
||||
|
@ -737,14 +761,83 @@ fn (a array) repeat_to_depth_noscan(count int, depth int) array {
|
|||
return arr
|
||||
}
|
||||
|
||||
pub fn (a &array) clone_to_depth_noscan(depth int) array {
|
||||
// insert inserts a value in the array at index `i`
|
||||
fn (mut a array) insert_noscan(i int, val voidptr) {
|
||||
$if !no_bounds_checking ? {
|
||||
if i < 0 || i > a.len {
|
||||
panic('array.insert: index out of range (i == $i, a.len == $a.len)')
|
||||
}
|
||||
}
|
||||
a.ensure_cap_noscan(a.len + 1)
|
||||
unsafe {
|
||||
C.memmove(a.get_unsafe(i + 1), a.get_unsafe(i), (a.len - i) * a.element_size)
|
||||
a.set_unsafe(i, val)
|
||||
}
|
||||
a.len++
|
||||
}
|
||||
|
||||
// insert_many inserts many values into the array from index `i`.
|
||||
[unsafe]
|
||||
fn (mut a array) insert_many_noscan(i int, val voidptr, size int) {
|
||||
$if !no_bounds_checking ? {
|
||||
if i < 0 || i > a.len {
|
||||
panic('array.insert_many: index out of range (i == $i, a.len == $a.len)')
|
||||
}
|
||||
}
|
||||
a.ensure_cap_noscan(a.len + size)
|
||||
elem_size := a.element_size
|
||||
unsafe {
|
||||
iptr := a.get_unsafe(i)
|
||||
C.memmove(a.get_unsafe(i + size), iptr, (a.len - i) * elem_size)
|
||||
C.memcpy(iptr, val, size * elem_size)
|
||||
}
|
||||
a.len += size
|
||||
}
|
||||
|
||||
// prepend prepends one value to the array.
|
||||
fn (mut a array) prepend_noscan(val voidptr) {
|
||||
a.insert_noscan(0, val)
|
||||
}
|
||||
|
||||
// prepend_many prepends another array to this array.
|
||||
[unsafe]
|
||||
fn (mut a array) prepend_many_noscan(val voidptr, size int) {
|
||||
unsafe { a.insert_many_noscan(0, val, size) }
|
||||
}
|
||||
|
||||
// pop returns the last element of the array, and removes it.
|
||||
fn (mut a array) pop_noscan() voidptr {
|
||||
// in a sense, this is the opposite of `a << x`
|
||||
$if !no_bounds_checking ? {
|
||||
if a.len == 0 {
|
||||
panic('array.pop: array is empty')
|
||||
}
|
||||
}
|
||||
new_len := a.len - 1
|
||||
last_elem := unsafe { &byte(a.data) + new_len * a.element_size }
|
||||
a.len = new_len
|
||||
// NB: a.cap is not changed here *on purpose*, so that
|
||||
// further << ops on that array will be more efficient.
|
||||
return unsafe { memdup_noscan(last_elem, a.element_size) }
|
||||
}
|
||||
|
||||
// `clone_static_to_depth_noscan()` returns an independent copy of a given array.
|
||||
// Unlike `clone_to_depth_noscan()` it has a value receiver and is used internally
|
||||
// for slice-clone expressions like `a[2..4].clone()` and in -autofree generated code.
|
||||
fn (a array) clone_static_to_depth_noscan(depth int) array {
|
||||
return unsafe { a.clone_to_depth_noscan(depth) }
|
||||
}
|
||||
|
||||
// recursively clone given array - `unsafe` when called directly because depth is not checked
|
||||
[unsafe]
|
||||
fn (a &array) clone_to_depth_noscan(depth int) array {
|
||||
mut size := a.cap * a.element_size
|
||||
if size == 0 {
|
||||
size++
|
||||
}
|
||||
mut arr := array{
|
||||
element_size: a.element_size
|
||||
data: if depth > 0 { vcalloc(size) } else { vcalloc_noscan(size) }
|
||||
data: if depth == 0 { vcalloc_noscan(size) } else { vcalloc(size) }
|
||||
len: a.len
|
||||
cap: a.cap
|
||||
}
|
||||
|
@ -765,6 +858,34 @@ pub fn (a &array) clone_to_depth_noscan(depth int) array {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut a array) push_noscan(val voidptr) {
|
||||
a.ensure_cap_noscan(a.len + 1)
|
||||
unsafe { C.memmove(&byte(a.data) + a.element_size * a.len, val, a.element_size) }
|
||||
a.len++
|
||||
}
|
||||
|
||||
// push_many implements the functionality for pushing another array.
|
||||
// `val` is array.data and user facing usage is `a << [1,2,3]`
|
||||
[unsafe]
|
||||
fn (mut a3 array) push_many_noscan(val voidptr, size int) {
|
||||
if a3.data == val && !isnil(a3.data) {
|
||||
// handle `arr << arr`
|
||||
copy := a3.clone()
|
||||
a3.ensure_cap_noscan(a3.len + size)
|
||||
unsafe {
|
||||
// C.memcpy(a.data, copy.data, copy.element_size * copy.len)
|
||||
C.memcpy(a3.get_unsafe(a3.len), copy.data, a3.element_size * size)
|
||||
}
|
||||
} else {
|
||||
a3.ensure_cap_noscan(a3.len + size)
|
||||
if !isnil(a3.data) && !isnil(val) {
|
||||
unsafe { C.memcpy(a3.get_unsafe(a3.len), val, a3.element_size * size) }
|
||||
}
|
||||
}
|
||||
a3.len += size
|
||||
}
|
||||
|
||||
// reverse returns a new array with the elements of the original array in reverse order.
|
||||
fn (a array) reverse_noscan() array {
|
||||
if a.len < 2 {
|
||||
return a
|
||||
|
@ -780,3 +901,15 @@ fn (a array) reverse_noscan() array {
|
|||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// grow_cap grows the array's capacity by `amount` elements.
|
||||
fn (mut a array) grow_cap_noscan(amount int) {
|
||||
a.ensure_cap_noscan(a.cap + amount)
|
||||
}
|
||||
|
||||
// grow_len ensures that an array has a.len + amount of length
|
||||
[unsafe]
|
||||
fn (mut a array) grow_len_noscan(amount int) {
|
||||
a.ensure_cap_noscan(a.len + amount)
|
||||
a.len += amount
|
||||
}
|
||||
|
|
|
@ -408,6 +408,17 @@ pub fn memdup(src voidptr, sz int) voidptr {
|
|||
}
|
||||
}
|
||||
|
||||
[unsafe]
|
||||
pub fn memdup_noscan(src voidptr, sz int) voidptr {
|
||||
if sz == 0 {
|
||||
return vcalloc_noscan(1)
|
||||
}
|
||||
unsafe {
|
||||
mem := vcalloc_noscan(sz)
|
||||
return C.memcpy(mem, src, sz)
|
||||
}
|
||||
}
|
||||
|
||||
[inline]
|
||||
fn v_fixed_index(i int, len int) int {
|
||||
$if !no_bounds_checking ? {
|
||||
|
|
|
@ -47,10 +47,10 @@ fn (mut g Gen) array_init(node ast.ArrayInit) {
|
|||
return
|
||||
}
|
||||
elem_type_str := g.typ(node.elem_type)
|
||||
noscan := g.check_noscan(node.elem_type)
|
||||
if node.exprs.len == 0 {
|
||||
elem_sym := g.table.get_type_symbol(node.elem_type)
|
||||
is_default_array := elem_sym.kind == .array && node.has_default
|
||||
noscan := g.check_noscan(node.elem_type)
|
||||
if is_default_array {
|
||||
g.write('__new_array_with_array_default${noscan}(')
|
||||
} else {
|
||||
|
@ -104,7 +104,7 @@ fn (mut g Gen) array_init(node ast.ArrayInit) {
|
|||
if elem_sym.kind == .function {
|
||||
g.write('new_array_from_c_array($len, $len, sizeof(voidptr), _MOV((voidptr[$len]){')
|
||||
} else {
|
||||
g.write('new_array_from_c_array($len, $len, sizeof($elem_type_str), _MOV(($elem_type_str[$len]){')
|
||||
g.write('new_array_from_c_array${noscan}($len, $len, sizeof($elem_type_str), _MOV(($elem_type_str[$len]){')
|
||||
}
|
||||
if len > 8 {
|
||||
g.writeln('')
|
||||
|
@ -194,7 +194,7 @@ fn (mut g Gen) gen_array_map(node ast.CallExpr) {
|
|||
}
|
||||
}
|
||||
g.writeln(';')
|
||||
g.writeln('\tarray_push((array*)&$tmp, &ti);')
|
||||
g.writeln('\tarray_push${noscan}((array*)&$tmp, &ti);')
|
||||
g.writeln('}')
|
||||
if !is_embed_map_filter {
|
||||
g.stmt_path_pos << g.out.len
|
||||
|
@ -387,7 +387,7 @@ fn (mut g Gen) gen_array_filter(node ast.CallExpr) {
|
|||
}
|
||||
}
|
||||
g.writeln(') {')
|
||||
g.writeln('\t\tarray_push((array*)&$tmp, &it); \n\t\t}')
|
||||
g.writeln('\t\tarray_push${noscan}((array*)&$tmp, &it); \n\t\t}')
|
||||
g.writeln('}')
|
||||
if !is_embed_map_filter {
|
||||
g.stmt_path_pos << g.out.len
|
||||
|
@ -404,10 +404,11 @@ fn (mut g Gen) gen_array_insert(node ast.CallExpr) {
|
|||
elem_type_str := g.typ(left_info.elem_type)
|
||||
arg2_sym := g.table.get_type_symbol(node.args[1].typ)
|
||||
is_arg2_array := arg2_sym.kind == .array && node.args[1].typ == node.left_type
|
||||
noscan := g.check_noscan(left_info.elem_type)
|
||||
if is_arg2_array {
|
||||
g.write('array_insert_many(&')
|
||||
g.write('array_insert_many${noscan}(&')
|
||||
} else {
|
||||
g.write('array_insert(&')
|
||||
g.write('array_insert${noscan}(&')
|
||||
}
|
||||
g.expr(node.left)
|
||||
g.write(', ')
|
||||
|
@ -438,10 +439,11 @@ fn (mut g Gen) gen_array_prepend(node ast.CallExpr) {
|
|||
elem_type_str := g.typ(left_info.elem_type)
|
||||
arg_sym := g.table.get_type_symbol(node.args[0].typ)
|
||||
is_arg_array := arg_sym.kind == .array && node.args[0].typ == node.left_type
|
||||
noscan := g.check_noscan(left_info.elem_type)
|
||||
if is_arg_array {
|
||||
g.write('array_prepend_many(&')
|
||||
g.write('array_prepend_many${noscan}(&')
|
||||
} else {
|
||||
g.write('array_prepend(&')
|
||||
g.write('array_prepend${noscan}(&')
|
||||
}
|
||||
g.expr(node.left)
|
||||
if is_arg_array {
|
||||
|
|
|
@ -2274,16 +2274,17 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
|||
}
|
||||
}
|
||||
g.expr(lx)
|
||||
noscan := if is_auto_heap { g.check_noscan(return_type) } else { '' }
|
||||
if is_opt {
|
||||
mr_base_styp := g.base_type(return_type)
|
||||
if is_auto_heap {
|
||||
g.writeln(' = HEAP($mr_base_styp, *($mr_base_styp*)${mr_var_name}.data).arg$i);')
|
||||
g.writeln(' = HEAP${noscan}($mr_base_styp, *($mr_base_styp*)${mr_var_name}.data).arg$i);')
|
||||
} else {
|
||||
g.writeln(' = (*($mr_base_styp*)${mr_var_name}.data).arg$i;')
|
||||
}
|
||||
} else {
|
||||
if is_auto_heap {
|
||||
g.writeln(' = HEAP($styp, ${mr_var_name}.arg$i);')
|
||||
g.writeln(' = HEAP${noscan}($styp, ${mr_var_name}.arg$i);')
|
||||
} else {
|
||||
g.writeln(' = ${mr_var_name}.arg$i;')
|
||||
}
|
||||
|
@ -3750,9 +3751,10 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
|||
// arr << val
|
||||
tmp := g.new_tmp_var()
|
||||
info := left_final_sym.info as ast.Array
|
||||
noscan := g.check_noscan(info.elem_type)
|
||||
if right_final_sym.kind == .array && info.elem_type != g.unwrap_generic(node.right_type) {
|
||||
// push an array => PUSH_MANY, but not if pushing an array to 2d array (`[][]int << []int`)
|
||||
g.write('_PUSH_MANY(')
|
||||
g.write('_PUSH_MANY${noscan}(')
|
||||
mut expected_push_many_atype := left_type
|
||||
if !expected_push_many_atype.is_ptr() {
|
||||
// fn f(mut a []int) { a << [1,2,3] } -> type of `a` is `array_int*` -> no need for &
|
||||
|
@ -3769,7 +3771,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
|||
// push a single element
|
||||
elem_type_str := g.typ(info.elem_type)
|
||||
elem_sym := g.table.get_type_symbol(info.elem_type)
|
||||
g.write('array_push((array*)')
|
||||
g.write('array_push${noscan}((array*)')
|
||||
if !left_type.is_ptr() {
|
||||
g.write('&')
|
||||
}
|
||||
|
@ -6653,6 +6655,15 @@ pub fn (mut g Gen) contains_ptr(el_typ ast.Type) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
.multi_return {
|
||||
info := sym.info as ast.MultiReturn
|
||||
for mrtyp in info.types {
|
||||
if g.contains_ptr(mrtyp) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
else {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -201,7 +201,9 @@ const c_helper_macros = '//============================== HELPER C MACROS ======
|
|||
#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 HEAP_noscan(type, expr) ((type*)memdup_noscan((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 _PUSH_MANY_noscan(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array_push_many_noscan(arr, tmp.data, tmp.len);}
|
||||
'
|
||||
|
||||
const c_headers = c_helper_macros + c_unsigned_comparison_functions + c_common_macros +
|
||||
|
|
|
@ -650,11 +650,19 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
}
|
||||
mut name := util.no_dots('${receiver_type_name}_$node.name')
|
||||
mut array_depth := -1
|
||||
mut noscan := ''
|
||||
if left_sym.kind == .array {
|
||||
if node.name in ['clone', 'repeat'] {
|
||||
needs_depth := node.name in ['clone', 'repeat']
|
||||
if needs_depth {
|
||||
elem_type := (left_sym.info as ast.Array).elem_type
|
||||
array_depth = g.get_array_depth(elem_type)
|
||||
}
|
||||
maybe_noscan := needs_depth
|
||||
|| node.name in ['pop', 'push', 'push_many', 'reverse', 'grow_cap', 'grow_len']
|
||||
if maybe_noscan {
|
||||
info := left_sym.info as ast.Array
|
||||
noscan = g.check_noscan(info.elem_type)
|
||||
}
|
||||
} else if left_sym.kind == .chan {
|
||||
if node.name in ['close', 'try_pop', 'try_push'] {
|
||||
name = 'sync__Channel_$node.name'
|
||||
|
@ -708,10 +716,14 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
|
|||
if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name == 'str' {
|
||||
g.write('ptr_str(')
|
||||
} else {
|
||||
if array_depth >= 0 {
|
||||
name = name + '_to_depth'
|
||||
if left_sym.kind == .array {
|
||||
if array_depth >= 0 {
|
||||
name = name + '_to_depth'
|
||||
}
|
||||
g.write('$name${noscan}(')
|
||||
} else {
|
||||
g.write('${name}(')
|
||||
}
|
||||
g.write('${name}(')
|
||||
}
|
||||
if node.receiver_type.is_ptr() && (!node.left_type.is_ptr()
|
||||
|| node.from_embed_type != 0 || (node.left_type.has_flag(.shared_f) && node.name != 'str')) {
|
||||
|
@ -1167,7 +1179,8 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
|
|||
g.expr(args[args.len - 1].expr)
|
||||
} else {
|
||||
if variadic_count > 0 {
|
||||
g.write('new_array_from_c_array($variadic_count, $variadic_count, sizeof($elem_type), _MOV(($elem_type[$variadic_count]){')
|
||||
noscan := g.check_noscan(arr_info.elem_type)
|
||||
g.write('new_array_from_c_array${noscan}($variadic_count, $variadic_count, sizeof($elem_type), _MOV(($elem_type[$variadic_count]){')
|
||||
for j in arg_nr .. args.len {
|
||||
g.ref_or_deref_arg(args[j], arr_info.elem_type, node.language)
|
||||
if j < args.len - 1 {
|
||||
|
|
|
@ -54,7 +54,8 @@ fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
|
|||
} else if sym.kind == .array_fixed {
|
||||
// Convert a fixed array to V array when doing `fixed_arr[start..end]`
|
||||
info := sym.info as ast.ArrayFixed
|
||||
g.write('array_slice(new_array_from_c_array(')
|
||||
noscan := g.check_noscan(info.elem_type)
|
||||
g.write('array_slice(new_array_from_c_array${noscan}(')
|
||||
g.write('$info.size')
|
||||
g.write(', $info.size')
|
||||
g.write(', sizeof(')
|
||||
|
|
|
@ -232,7 +232,7 @@ fn (mut g Gen) decode_array(value_type ast.Type) string {
|
|||
cJSON_ArrayForEach(jsval, root)
|
||||
{
|
||||
$s
|
||||
array_push((array*)&res, &val);
|
||||
array_push${noscan}((array*)&res, &val);
|
||||
}
|
||||
'
|
||||
}
|
||||
|
|
|
@ -121,10 +121,25 @@ pub fn mark_used(mut table ast.Table, pref &pref.Preferences, ast_files []&ast.F
|
|||
|
||||
if pref.gc_mode in [.boehm_full_opt, .boehm_incr_opt] {
|
||||
all_fn_root_names << [
|
||||
'memdup_noscan',
|
||||
'__new_array_noscan',
|
||||
'__new_array_with_default_noscan',
|
||||
'__new_array_with_array_default_noscan',
|
||||
'new_array_from_c_array_noscan',
|
||||
'21.clone_static_to_depth_noscan',
|
||||
'21.clone_to_depth_noscan',
|
||||
'21.reverse_noscan',
|
||||
'21.repeat_to_depth_noscan',
|
||||
'65557.pop_noscan',
|
||||
'65557.push_noscan',
|
||||
'65557.push_many_noscan',
|
||||
'65557.insert_noscan',
|
||||
'65557.insert_many_noscan',
|
||||
'65557.prepend_noscan',
|
||||
'65557.prepend_many_noscan',
|
||||
'65557.reverse_noscan',
|
||||
'65557.grow_cap_noscan',
|
||||
'65557.grow_len_noscan',
|
||||
]
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue