From 9edb48571fbef1be5840ff8fc8b495fe56e55637 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 30 Oct 2022 14:06:07 +0200 Subject: [PATCH] pref,cgen: support `-no-bounds-checking`, instead of `-d no_bounds_checking`, and make it enable direct_array_access for all fns/methods. --- vlib/builtin/array.v | 20 ++++++------- vlib/builtin/array_d_gcboehm_opt.v | 6 ++-- vlib/builtin/builtin.c.v | 2 +- vlib/builtin/string.v | 6 ++-- vlib/v/gen/c/cgen.v | 1 + vlib/v/gen/c/fn.v | 5 ++++ vlib/v/gen/c/index.v | 47 ++++++++++++++---------------- vlib/v/pref/pref.v | 15 +++++++++- 8 files changed, 59 insertions(+), 43 deletions(-) diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index db447248e7..e9dccfb5b4 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -219,7 +219,7 @@ pub fn (a array) repeat_to_depth(count int, depth int) array { // c.insert(0, [1, 2]) // c now is [[1, 2], [3, 4]] // ``` pub fn (mut a array) insert(i int, val voidptr) { - $if !no_bounds_checking ? { + $if !no_bounds_checking { if i < 0 || i > a.len { panic('array.insert: index out of range (i == $i, a.len == $a.len)') } @@ -238,7 +238,7 @@ pub fn (mut a array) insert(i int, val voidptr) { // into an the array beginning at `i`. [unsafe] fn (mut a array) insert_many(i int, val voidptr, size int) { - $if !no_bounds_checking ? { + $if !no_bounds_checking { if i < 0 || i > a.len { panic('array.insert_many: index out of range (i == $i, a.len == $a.len)') } @@ -297,7 +297,7 @@ pub fn (mut a array) delete(i int) { // dump(b) // b: [1, 2, 3, 4, 5, 6, 7, 8, 9] // `b` is still the same // ``` pub fn (mut a array) delete_many(i int, size int) { - $if !no_bounds_checking ? { + $if !no_bounds_checking { if i < 0 || i + size > a.len { endidx := if size > 1 { '..${i + size}' } else { '' } panic('array.delete: index out of range (i == $i$endidx, a.len == $a.len)') @@ -379,7 +379,7 @@ fn (a array) get_unsafe(i int) voidptr { // Private function. Used to implement array[] operator. fn (a array) get(i int) voidptr { - $if !no_bounds_checking ? { + $if !no_bounds_checking { if i < 0 || i >= a.len { panic('array.get: index out of range (i == $i, a.len == $a.len)') } @@ -404,7 +404,7 @@ fn (a array) get_with_check(i int) voidptr { // However, `a[0]` returns an error object // so it can be handled with an `or` block. pub fn (a array) first() voidptr { - $if !no_bounds_checking ? { + $if !no_bounds_checking { if a.len == 0 { panic('array.first: array is empty') } @@ -415,7 +415,7 @@ pub fn (a array) first() voidptr { // last returns the last element of the `array`. // If the `array` is empty, this will panic. pub fn (a array) last() voidptr { - $if !no_bounds_checking ? { + $if !no_bounds_checking { if a.len == 0 { panic('array.last: array is empty') } @@ -442,7 +442,7 @@ pub fn (a array) last() voidptr { // ``` pub fn (mut a array) pop() voidptr { // in a sense, this is the opposite of `a << x` - $if !no_bounds_checking ? { + $if !no_bounds_checking { if a.len == 0 { panic('array.pop: array is empty') } @@ -461,7 +461,7 @@ pub fn (mut a array) pop() voidptr { // See also: [trim](#array.trim) pub fn (mut a array) delete_last() { // copy pasting code for performance - $if !no_bounds_checking ? { + $if !no_bounds_checking { if a.len == 0 { panic('array.pop: array is empty') } @@ -480,7 +480,7 @@ pub fn (mut a array) delete_last() { // Alternative: `.slice_ni()` will always return an array. fn (a array) slice(start int, _end int) array { mut end := _end - $if !no_bounds_checking ? { + $if !no_bounds_checking { if start > end { panic('array.slice: invalid slice index ($start > $end)') } @@ -616,7 +616,7 @@ fn (mut a array) set_unsafe(i int, val voidptr) { // Private function. Used to implement assignment to the array element. fn (mut a array) set(i int, val voidptr) { - $if !no_bounds_checking ? { + $if !no_bounds_checking { if i < 0 || i >= a.len { panic('array.set: index out of range (i == $i, a.len == $a.len)') } diff --git a/vlib/builtin/array_d_gcboehm_opt.v b/vlib/builtin/array_d_gcboehm_opt.v index 9f8a5bab3e..1cfa5c6f6f 100644 --- a/vlib/builtin/array_d_gcboehm_opt.v +++ b/vlib/builtin/array_d_gcboehm_opt.v @@ -122,7 +122,7 @@ fn (a array) repeat_to_depth_noscan(count int, 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 !no_bounds_checking { if i < 0 || i > a.len { panic('array.insert: index out of range (i == $i, a.len == $a.len)') } @@ -138,7 +138,7 @@ fn (mut a array) insert_noscan(i int, val voidptr) { // 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 !no_bounds_checking { if i < 0 || i > a.len { panic('array.insert_many: index out of range (i == $i, a.len == $a.len)') } @@ -167,7 +167,7 @@ fn (mut a array) prepend_many_noscan(val voidptr, size int) { // 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 !no_bounds_checking { if a.len == 0 { panic('array.pop: array is empty') } diff --git a/vlib/builtin/builtin.c.v b/vlib/builtin/builtin.c.v index 7bf4ed5555..324554f7c9 100644 --- a/vlib/builtin/builtin.c.v +++ b/vlib/builtin/builtin.c.v @@ -611,7 +611,7 @@ pub fn memdup_uncollectable(src voidptr, sz int) voidptr { [inline] fn v_fixed_index(i int, len int) int { - $if !no_bounds_checking ? { + $if !no_bounds_checking { if i < 0 || i >= len { s := 'fixed array index out of range (index: $i, len: $len)' panic(s) diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 3d668e0ca9..3e81a4f774 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -499,7 +499,7 @@ pub fn (s string) replace_each(vals []string) string { // Example: assert '\tHello!'.replace_char(`\t`,` `,8) == ' Hello!' [direct_array_access] pub fn (s string) replace_char(rep u8, with u8, repeat int) string { - $if !no_bounds_checking ? { + $if !no_bounds_checking { if repeat <= 0 { panic('string.replace_char(): tab length too short') } @@ -873,7 +873,7 @@ fn (s string) substr2(start int, _end int, end_max bool) string { // Example: assert 'ABCD'.substr(1,3) == 'BC' [direct_array_access] pub fn (s string) substr(start int, end int) string { - $if !no_bounds_checking ? { + $if !no_bounds_checking { if start > end || start > s.len || end > s.len || start < 0 || end < 0 { panic('substr($start, $end) out of bounds (len=$s.len)') } @@ -1582,7 +1582,7 @@ pub fn (s string) str() string { // at returns the byte at index `idx`. // Example: assert 'ABC'.at(1) == u8(`B`) fn (s string) at(idx int) byte { - $if !no_bounds_checking ? { + $if !no_bounds_checking { if idx < 0 || idx >= s.len { panic('string index out of range: $idx / $s.len') } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index c69c2cbfea..8b94fdb91b 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -89,6 +89,7 @@ mut: tmp_count_declarations int // counter for unique tmp names (_d1, _d2 etc); does NOT reset, used for C declarations global_tmp_count int // like tmp_count but global and not resetted in each function discard_or_result bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage + is_direct_array_access bool // inside a `[direct_array_access fn a() {}` function is_assign_lhs bool // inside left part of assign expr (for array_set(), etc) is_void_expr_stmt bool // ExprStmt whos result is discarded is_arraymap_set bool // map or array set value state diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index ecc1e9e83e..d5bdb73f53 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -57,6 +57,11 @@ fn (mut g Gen) fn_decl(node ast.FnDecl) { g.out_fn_start_pos << g.out.len } } + prev_is_direct_array_access := g.is_direct_array_access + g.is_direct_array_access = node.is_direct_arr || g.pref.no_bounds_checking + defer { + g.is_direct_array_access = prev_is_direct_array_access + } g.gen_attrs(node.attrs) mut skip := false pos := g.out.len diff --git a/vlib/v/gen/c/index.v b/vlib/v/gen/c/index.v index 60cee5011f..9ced644ece 100644 --- a/vlib/v/gen/c/index.v +++ b/vlib/v/gen/c/index.v @@ -18,29 +18,28 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) { } else if sym.kind == .map { g.index_of_map(node, sym) } else if sym.kind == .string && !node.left_type.is_ptr() { - is_direct_array_access := (unsafe { g.fn_decl != 0 } && g.fn_decl.is_direct_arr) - || node.is_direct - if is_direct_array_access { + gen_or := node.or_expr.kind != .absent || node.is_option + if gen_or { + tmp_opt := g.new_tmp_var() + cur_line := g.go_before_stmt(0) + g.out.write_string(util.tabs(g.indent)) + opt_elem_type := g.typ(ast.u8_type.set_flag(.optional)) + g.write('$opt_elem_type $tmp_opt = string_at_with_check(') g.expr(node.left) - g.write('.str[ ') + g.write(', ') g.expr(node.index) - g.write(']') + g.writeln(');') + if !node.is_option { + g.or_block(tmp_opt, node.or_expr, ast.u8_type) + } + g.write('\n$cur_line*(byte*)&${tmp_opt}.data') } else { - gen_or := node.or_expr.kind != .absent || node.is_option - if gen_or { - tmp_opt := g.new_tmp_var() - cur_line := g.go_before_stmt(0) - g.out.write_string(util.tabs(g.indent)) - opt_elem_type := g.typ(ast.u8_type.set_flag(.optional)) - g.write('$opt_elem_type $tmp_opt = string_at_with_check(') + is_direct_array_access := g.is_direct_array_access || node.is_direct + if is_direct_array_access { g.expr(node.left) - g.write(', ') + g.write('.str[ ') g.expr(node.index) - g.writeln(');') - if !node.is_option { - g.or_block(tmp_opt, node.or_expr, ast.u8_type) - } - g.write('\n$cur_line*(byte*)&${tmp_opt}.data') + g.write(']') } else { g.write('string_at(') g.expr(node.left) @@ -175,8 +174,7 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) { // `vals[i].field = x` is an exception and requires `array_get`: // `(*(Val*)array_get(vals, i)).field = x;` if g.is_assign_lhs && node.is_setter { - is_direct_array_access := (unsafe { g.fn_decl != 0 } && g.fn_decl.is_direct_arr) - || node.is_direct + is_direct_array_access := g.is_direct_array_access || node.is_direct is_op_assign := g.assign_op != .assign && info.elem_type != ast.string_type if is_direct_array_access { g.write('(($elem_type_str*)') @@ -235,11 +233,11 @@ fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) { } } } else { - is_direct_array_access := (unsafe { g.fn_decl != 0 } && g.fn_decl.is_direct_arr) - || node.is_direct + is_direct_array_access := g.is_direct_array_access || node.is_direct // do not clone inside `opt_ok(opt_ok(&(string[]) {..})` before returns needs_clone := info.elem_type == ast.string_type_idx && g.is_autofree && !(g.inside_return - && g.fn_decl.return_type.has_flag(.optional)) && !g.is_assign_lhs + && g.fn_decl != unsafe { nil } && g.fn_decl.return_type.has_flag(.optional)) + && !g.is_assign_lhs is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result cur_line := if is_gen_or_and_assign_rhs { line := g.go_before_stmt(0) @@ -353,8 +351,7 @@ fn (mut g Gen) index_of_fixed_array(node ast.IndexExpr, sym ast.TypeSymbol) { } } g.write('[') - direct := unsafe { g.fn_decl != 0 } && g.fn_decl.is_direct_arr - if (direct || node.index is ast.IntegerLiteral) || g.pref.translated { + if g.is_direct_array_access || g.pref.translated || node.index is ast.IntegerLiteral { g.expr(node.index) } else { // bounds check diff --git a/vlib/v/pref/pref.v b/vlib/v/pref/pref.v index 5daa42c552..17b211f862 100644 --- a/vlib/v/pref/pref.v +++ b/vlib/v/pref/pref.v @@ -160,6 +160,7 @@ pub mut: ccompiler_type CompilerType // the type of the C compiler used third_party_option string building_v bool + no_bounds_checking bool // `-no-bounds-checking` turns off *all* bounds checks for all functions at runtime, as if they all had been tagged with `[direct_array_access]` autofree bool // `v -manualfree` => false, `v -autofree` => true; false by default for now. // Disabling `free()` insertion results in better performance in some applications (e.g. compilers) trace_calls bool // -trace-calls true = the transformer stage will generate and inject print calls for tracing function calls @@ -468,6 +469,12 @@ pub fn parse_args_and_show_errors(known_external_commands []string, args []strin res.is_glibc = true res.build_options << arg } + '-no-bounds-checking' { + res.no_bounds_checking = true + res.compile_defines << 'no_bounds_checking' + res.compile_defines_all << 'no_bounds_checking' + res.build_options << arg + } '-no-builtin' { res.no_builtin = true res.build_options << arg @@ -1016,6 +1023,9 @@ fn (mut prefs Preferences) parse_define(define string) { if define_parts.len == 1 { prefs.compile_defines << define prefs.compile_defines_all << define + if define == 'no_bounds_checking' { + prefs.no_bounds_checking = true + } return } if define_parts.len == 2 { @@ -1040,6 +1050,9 @@ fn (mut prefs Preferences) parse_define(define string) { fn (mut prefs Preferences) diagnose_deprecated_defines(define_parts []string) { if define_parts[0] == 'force_embed_file' { - eprintln('-d force_embed_file was deprecated in 2022/06/01. Now \$embed_file(file) always embeds the file, unless you pass `-d embed_only_metadata`.') + eprintln('`-d force_embed_file` was deprecated in 2022/06/01. Now \$embed_file(file) always embeds the file, unless you pass `-d embed_only_metadata`.') + } + if define_parts[0] == 'no_bounds_checking' { + eprintln('`-d no_bounds_checking` was deprecated in 2022/10/30. Use `-no-bounds-checking` instead.') } }