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 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 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. blocks of code.
NB: These are *rarely* needed, and should not be used, unless you 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, try to inline them, which in some cases, may be beneficial for performance,
but may impact the size of your executable. 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 `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 boolean expression is very likely to be true, so it can generate assembly
code, with less chance of branch misprediction. In the JS backend, 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.len == 0
assert a.cap == 3 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 body_pos token.Position
file string file string
is_generic bool is_generic bool
is_direct_arr bool // direct array access
attrs []table.Attr attrs []table.Attr
pub mut: pub mut:
stmts []Stmt stmts []Stmt

View File

@ -801,6 +801,7 @@ fn (mut g Gen) stmt(node ast.Stmt) {
} }
} }
ast.FnDecl { ast.FnDecl {
g.gen_attrs(node.attrs)
// g.tmp_count = 0 TODO // g.tmp_count = 0 TODO
mut skip := false mut skip := false
pos := g.out.buf.len 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;` // `(*(Val*)array_get(vals, i)).field = x;`
is_selector := node.left is ast.SelectorExpr is_selector := node.left is ast.SelectorExpr
if g.is_assign_lhs && !is_selector && node.is_setter { if g.is_assign_lhs && !is_selector && node.is_setter {
g.is_array_set = true is_direct_array_access := g.fn_decl != 0 && g.fn_decl.is_direct_arr
g.write('array_set(') array_ptr_type_str := match elem_typ.kind {
if !left_is_ptr || node.left_type.has_flag(.shared_f) { .function { 'voidptr*' }
g.write('&') 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) g.expr(node.left)
// TODO: test direct_array_access when 'shared' is implemented
if node.left_type.has_flag(.shared_f) { if node.left_type.has_flag(.shared_f) {
if left_is_ptr { if left_is_ptr {
g.write('->val') g.write('->val')
@ -2870,72 +2881,89 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
g.write('.val') g.write('.val')
} }
} }
g.write(', ') if is_direct_array_access {
g.expr(node.index) if left_is_ptr && !node.left_type.has_flag(.shared_f) {
mut need_wrapper := true g.write('->')
/*
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 { } else {
g.write(', &($elem_type_str[]) { ') g.write('.')
} }
g.write('data)[')
g.expr(node.index)
g.write(']')
} else { } 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` } else {
if g.assign_op != .assign && is_direct_array_access := g.fn_decl != 0 && g.fn_decl.is_direct_arr
g.assign_op in token.assign_tokens && info.elem_type != table.string_type { array_ptr_type_str := match elem_typ.kind {
// TODO move this .function { 'voidptr*' }
g.write('*($elem_type_str*)array_get(') 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) { if left_is_ptr && !node.left_type.has_flag(.shared_f) {
g.write('*') 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) g.expr(node.left)
// TODO: test direct_array_access when 'shared' is implemented
if node.left_type.has_flag(.shared_f) { if node.left_type.has_flag(.shared_f) {
if left_is_ptr { if left_is_ptr {
g.write('->val') g.write('->val')
@ -2943,9 +2971,20 @@ fn (mut g Gen) index_expr(node ast.IndexExpr) {
g.write('.val') g.write('.val')
} }
} }
g.write(', ') if is_direct_array_access {
g.expr(node.index) if left_is_ptr && !node.left_type.has_flag(.shared_f) {
g.write('))') 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 { } else if sym.kind == .map {
info := sym.info as table.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() p.top_level_statement_start()
start_pos := p.tok.position() start_pos := p.tok.position()
is_deprecated := p.attrs.contains('deprecated') is_deprecated := p.attrs.contains('deprecated')
is_direct_arr := p.attrs.contains('direct_array_access')
mut is_unsafe := p.attrs.contains('unsafe') mut is_unsafe := p.attrs.contains('unsafe')
is_pub := p.tok.kind == .key_pub is_pub := p.tok.kind == .key_pub
if is_pub { if is_pub {
@ -318,6 +319,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
return_type: return_type return_type: return_type
args: args args: args
is_deprecated: is_deprecated is_deprecated: is_deprecated
is_direct_arr: is_direct_arr
is_pub: is_pub is_pub: is_pub
is_generic: is_generic is_generic: is_generic
is_variadic: is_variadic is_variadic: is_variadic