all: [direct_array_access] tag (#6203)

pull/6209/head
Maciej Obarski 2020-08-24 09:04:50 +02:00 committed by GitHub
parent 6a0cb3e552
commit e8e0d9fa42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 144 additions and 65 deletions

View File

@ -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,

View File

@ -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]
}
}

View File

@ -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

View File

@ -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

View File

@ -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