all: [direct_array_access] tag (#6203)
parent
6a0cb3e552
commit
e8e0d9fa42
|
@ -2270,7 +2270,7 @@ eprintln('$vm.name $vm.version\n $vm.description')
|
|||
|
||||
The generated C code is usually fast enough, when you compile your code
|
||||
with `-prod`. There are some situations though, where you may want to give
|
||||
additional hints to the C compiler, so that it can further optimize some
|
||||
additional hints to the compiler, so that it can further optimize some
|
||||
blocks of code.
|
||||
|
||||
NB: These are *rarely* needed, and should not be used, unless you
|
||||
|
@ -2282,6 +2282,12 @@ how their programs actually perform".
|
|||
try to inline them, which in some cases, may be beneficial for performance,
|
||||
but may impact the size of your executable.
|
||||
|
||||
`[direct_array_access]` - in functions tagged with `[direct_array_access]`
|
||||
the compiler will translate array operations directly into C array operations -
|
||||
omiting bounds checking. This may save a lot of time in a function that iterates
|
||||
over an array but at the cost of making the function unsafe - unless
|
||||
the boundries will be checked by the user.
|
||||
|
||||
`if _likely_(bool expression) {` this hints the C compiler, that the passed
|
||||
boolean expression is very likely to be true, so it can generate assembly
|
||||
code, with less chance of branch misprediction. In the JS backend,
|
||||
|
|
|
@ -1001,3 +1001,34 @@ fn test_array_string_pop() {
|
|||
assert a.len == 0
|
||||
assert a.cap == 3
|
||||
}
|
||||
|
||||
|
||||
[direct_array_access]
|
||||
fn test_direct_array_access() {
|
||||
mut a := [11,22,33,44]
|
||||
assert a[0] == 11
|
||||
assert a[2] == 33
|
||||
x := a[0]
|
||||
a[0] = 21
|
||||
a[1] += 2
|
||||
a[2] = x + 3
|
||||
a[3] -= a[1]
|
||||
assert a == [21, 24, 14, 20]
|
||||
}
|
||||
|
||||
[direct_array_access]
|
||||
fn test_direct_array_access_via_ptr() {
|
||||
mut b := [11,22,33,44]
|
||||
unsafe {
|
||||
mut a := &b
|
||||
assert a[0] == 11
|
||||
assert a[2] == 33
|
||||
x := a[0]
|
||||
a[0] = 21
|
||||
a[1] += 2
|
||||
a[2] = x + 3
|
||||
a[3] -= a[1]
|
||||
assert a == [21, 24, 14, 20]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -258,6 +258,7 @@ pub:
|
|||
body_pos token.Position
|
||||
file string
|
||||
is_generic bool
|
||||
is_direct_arr bool // direct array access
|
||||
attrs []table.Attr
|
||||
pub mut:
|
||||
stmts []Stmt
|
||||
|
|
|
@ -801,6 +801,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
|
|||
}
|
||||
}
|
||||
ast.FnDecl {
|
||||
g.gen_attrs(node.attrs)
|
||||
// g.tmp_count = 0 TODO
|
||||
mut skip := false
|
||||
pos := g.out.buf.len
|
||||
|
@ -2857,12 +2858,22 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
|
|||
// `(*(Val*)array_get(vals, i)).field = x;`
|
||||
is_selector := node.left is ast.SelectorExpr
|
||||
if g.is_assign_lhs && !is_selector && node.is_setter {
|
||||
g.is_array_set = true
|
||||
g.write('array_set(')
|
||||
if !left_is_ptr || node.left_type.has_flag(.shared_f) {
|
||||
g.write('&')
|
||||
is_direct_array_access := g.fn_decl != 0 && g.fn_decl.is_direct_arr
|
||||
array_ptr_type_str := match elem_typ.kind {
|
||||
.function { 'voidptr*' }
|
||||
else { '$elem_type_str*' }
|
||||
}
|
||||
if is_direct_array_access {
|
||||
g.write('(($array_ptr_type_str)')
|
||||
} else {
|
||||
g.is_array_set = true // special handling of assign_op and closing with '})'
|
||||
g.write('array_set(')
|
||||
if !left_is_ptr || node.left_type.has_flag(.shared_f) {
|
||||
g.write('&')
|
||||
}
|
||||
}
|
||||
g.expr(node.left)
|
||||
// TODO: test direct_array_access when 'shared' is implemented
|
||||
if node.left_type.has_flag(.shared_f) {
|
||||
if left_is_ptr {
|
||||
g.write('->val')
|
||||
|
@ -2870,72 +2881,89 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
|
|||
g.write('.val')
|
||||
}
|
||||
}
|
||||
g.write(', ')
|
||||
g.expr(node.index)
|
||||
mut need_wrapper := true
|
||||
/*
|
||||
match node.right {
|
||||
ast.EnumVal, ast.Ident {
|
||||
// `&x` is enough for variables and enums
|
||||
// `&(Foo[]){ ... }` is only needed for function calls and literals
|
||||
need_wrapper = false
|
||||
}
|
||||
else {}
|
||||
}
|
||||
*/
|
||||
if need_wrapper {
|
||||
if elem_typ.kind == .function {
|
||||
g.write(', &(voidptr[]) { ')
|
||||
if is_direct_array_access {
|
||||
if left_is_ptr && !node.left_type.has_flag(.shared_f) {
|
||||
g.write('->')
|
||||
} else {
|
||||
g.write(', &($elem_type_str[]) { ')
|
||||
g.write('.')
|
||||
}
|
||||
g.write('data)[')
|
||||
g.expr(node.index)
|
||||
g.write(']')
|
||||
} else {
|
||||
g.write(', &')
|
||||
g.write(', ')
|
||||
g.expr(node.index)
|
||||
mut need_wrapper := true
|
||||
/*
|
||||
match node.right {
|
||||
ast.EnumVal, ast.Ident {
|
||||
// `&x` is enough for variables and enums
|
||||
// `&(Foo[]){ ... }` is only needed for function calls and literals
|
||||
need_wrapper = false
|
||||
}
|
||||
else {}
|
||||
}
|
||||
*/
|
||||
if need_wrapper {
|
||||
if elem_typ.kind == .function {
|
||||
g.write(', &(voidptr[]) { ')
|
||||
} else {
|
||||
g.write(', &($elem_type_str[]) { ')
|
||||
}
|
||||
} else {
|
||||
g.write(', &')
|
||||
}
|
||||
// `x[0] *= y`
|
||||
if g.assign_op != .assign &&
|
||||
g.assign_op in token.assign_tokens && info.elem_type != table.string_type {
|
||||
// TODO move this
|
||||
g.write('*($elem_type_str*)array_get(')
|
||||
if left_is_ptr && !node.left_type.has_flag(.shared_f) {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.left)
|
||||
if node.left_type.has_flag(.shared_f) {
|
||||
if left_is_ptr {
|
||||
g.write('->val')
|
||||
} else {
|
||||
g.write('.val')
|
||||
}
|
||||
}
|
||||
g.write(', ')
|
||||
g.expr(node.index)
|
||||
g.write(') ')
|
||||
op := match g.assign_op {
|
||||
.mult_assign { '*' }
|
||||
.plus_assign { '+' }
|
||||
.minus_assign { '-' }
|
||||
.div_assign { '/' }
|
||||
.xor_assign { '^' }
|
||||
.mod_assign { '%' }
|
||||
.or_assign { '|' }
|
||||
.and_assign { '&' }
|
||||
.left_shift_assign { '<<' }
|
||||
.right_shift_assign { '>>' }
|
||||
else { '' }
|
||||
}
|
||||
g.write(op)
|
||||
}
|
||||
}
|
||||
// `x[0] *= y`
|
||||
if g.assign_op != .assign &&
|
||||
g.assign_op in token.assign_tokens && info.elem_type != table.string_type {
|
||||
// TODO move this
|
||||
g.write('*($elem_type_str*)array_get(')
|
||||
} else {
|
||||
is_direct_array_access := g.fn_decl != 0 && g.fn_decl.is_direct_arr
|
||||
array_ptr_type_str := match elem_typ.kind {
|
||||
.function { 'voidptr*' }
|
||||
else { '$elem_type_str*' }
|
||||
}
|
||||
if is_direct_array_access {
|
||||
g.write('(($array_ptr_type_str)')
|
||||
} else {
|
||||
g.write('(*($array_ptr_type_str)array_get(')
|
||||
if left_is_ptr && !node.left_type.has_flag(.shared_f) {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.left)
|
||||
if node.left_type.has_flag(.shared_f) {
|
||||
if left_is_ptr {
|
||||
g.write('->val')
|
||||
} else {
|
||||
g.write('.val')
|
||||
}
|
||||
}
|
||||
g.write(', ')
|
||||
g.expr(node.index)
|
||||
g.write(') ')
|
||||
op := match g.assign_op {
|
||||
.mult_assign { '*' }
|
||||
.plus_assign { '+' }
|
||||
.minus_assign { '-' }
|
||||
.div_assign { '/' }
|
||||
.xor_assign { '^' }
|
||||
.mod_assign { '%' }
|
||||
.or_assign { '|' }
|
||||
.and_assign { '&' }
|
||||
.left_shift_assign { '<<' }
|
||||
.right_shift_assign { '>>' }
|
||||
else { '' }
|
||||
}
|
||||
g.write(op)
|
||||
}
|
||||
} else {
|
||||
if elem_typ.kind == .function {
|
||||
g.write('(*(voidptr*)array_get(')
|
||||
} else {
|
||||
g.write('(*($elem_type_str*)array_get(')
|
||||
}
|
||||
if left_is_ptr && !node.left_type.has_flag(.shared_f) {
|
||||
g.write('*')
|
||||
}
|
||||
g.expr(node.left)
|
||||
// TODO: test direct_array_access when 'shared' is implemented
|
||||
if node.left_type.has_flag(.shared_f) {
|
||||
if left_is_ptr {
|
||||
g.write('->val')
|
||||
|
@ -2943,9 +2971,20 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
|
|||
g.write('.val')
|
||||
}
|
||||
}
|
||||
g.write(', ')
|
||||
g.expr(node.index)
|
||||
g.write('))')
|
||||
if is_direct_array_access {
|
||||
if left_is_ptr && !node.left_type.has_flag(.shared_f) {
|
||||
g.write('->')
|
||||
} else {
|
||||
g.write('.')
|
||||
}
|
||||
g.write('data)[')
|
||||
g.expr(node.index)
|
||||
g.write(']')
|
||||
} else {
|
||||
g.write(', ')
|
||||
g.expr(node.index)
|
||||
g.write('))')
|
||||
}
|
||||
}
|
||||
} else if sym.kind == .map {
|
||||
info := sym.info as table.Map
|
||||
|
|
|
@ -132,6 +132,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
p.top_level_statement_start()
|
||||
start_pos := p.tok.position()
|
||||
is_deprecated := p.attrs.contains('deprecated')
|
||||
is_direct_arr := p.attrs.contains('direct_array_access')
|
||||
mut is_unsafe := p.attrs.contains('unsafe')
|
||||
is_pub := p.tok.kind == .key_pub
|
||||
if is_pub {
|
||||
|
@ -318,6 +319,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
return_type: return_type
|
||||
args: args
|
||||
is_deprecated: is_deprecated
|
||||
is_direct_arr: is_direct_arr
|
||||
is_pub: is_pub
|
||||
is_generic: is_generic
|
||||
is_variadic: is_variadic
|
||||
|
|
Loading…
Reference in New Issue