v: add full compiler support for _likely_(x)

pull/5311/head
Delyan Angelov 2020-06-09 17:36:18 +03:00
parent 6663e94780
commit c7d4360931
11 changed files with 90 additions and 7 deletions

View File

@ -1684,6 +1684,22 @@ vm := vmod.decode( @VMOD_FILE ) or { panic(err) }
eprintln('$vm.name $vm.version\n $vm.description')
```
## Performance tuning
The generated C code is usually fast enough. Although rarely, there
are some situations though, where you want to give additional hints
to the C compiler, so that it can further optimize some blocks of code.
NB: these are *rarely* needed, and should not be used, unless you
profile your code, and see that there are significant benefits for them.
[inline] - you can tag functions with [inline], and the C compiler will
try to inline them, which in some cases, may be beneficial for peformance.
`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,
that does nothing.
## Reflection via codegen
Having built-in JSON support is nice, but V also allows you to create efficient

View File

@ -1,10 +1,5 @@
module builtin
// See cheaders.v: _likely_ is actually a macro, to hint 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.
fn C._likely_(bool) bool
// <string.h>
fn C.memcpy(byteptr, byteptr, int) voidptr

View File

@ -13,7 +13,7 @@ pub type Expr = AnonFn | ArrayInit | AsCast | AssignExpr | Assoc | BoolLiteral |
CastExpr | CharLiteral | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr |
IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | MapInit | MatchExpr | None | OrExpr |
ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | StringInterLiteral |
StringLiteral | StructInit | Type | TypeOf
StringLiteral | StructInit | Type | TypeOf | Likely
pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | Comment | CompIf | ConstDecl |
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt |
@ -755,6 +755,12 @@ pub:
type_name string
}
pub struct Likely {
pub:
expr Expr
pos token.Position
}
pub struct TypeOf {
pub:
expr Expr
@ -888,6 +894,9 @@ pub fn (expr Expr) position() token.Position {
StructInit {
return it.pos
}
Likely {
return it.pos
}
// ast.TypeOf { }
else {
return token.Position{}

View File

@ -163,6 +163,9 @@ pub fn (x Expr) str() string {
TypeOf {
return 'typeof(${it.expr.str()})'
}
Likely {
return '_likely_(${it.expr.str()})'
}
else {
return '[unhandled expr type ${typeof(x)}]'
}

View File

@ -1772,7 +1772,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
c.error('checker: too many expr levels: $c.expr_level ', node.position())
return table.void_type
}
match mut node {
ast.AnonFn {
keep_fn := c.cur_fn
@ -1944,6 +1944,14 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type {
it.expr_type = c.expr(it.expr)
return table.string_type
}
ast.Likely {
ltype := c.expr(it.expr)
if !c.check_types(ltype, table.bool_type) {
ltype_sym := c.table.get_type_symbol(ltype)
c.error('`_likely_()` expects a boolean expression, instead it got `${ltype_sym.name}`', it.pos)
}
return table.bool_type
}
else {
tnode := typeof(node)
if tnode != 'unknown v.ast.Expr' {

View File

@ -734,6 +734,11 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
f.expr(it.expr)
f.write(')')
}
ast.Likely {
f.write('_likely_(')
f.expr(it.expr)
f.write(')')
}
}
}

View File

@ -1586,6 +1586,11 @@ fn (mut g Gen) expr(node ast.Expr) {
ast.TypeOf {
g.typeof_expr(it)
}
ast.Likely {
g.write('_likely_(')
g.expr(it.expr)
g.write(')')
}
else {
// #printf("node=%d\n", node.typ);
println(term.red('cgen.expr(): bad node ' + typeof(node)))

View File

@ -627,6 +627,11 @@ fn (mut g JsGen) expr(node ast.Expr) {
// skip: JS has no types
// TODO maybe?
}
ast.Likely {
g.write('(')
g.expr(it.expr)
g.write(')')
}
ast.TypeOf {
g.gen_typeof_expr(it)
// TODO: Should this print the V type or the JS type?

View File

@ -103,6 +103,17 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
expr: expr
}
}
.key_likely {
p.next()
p.check(.lpar)
lpos := p.tok.position()
expr := p.expr(0)
p.check(.rpar)
node = ast.Likely{
expr: expr
pos: lpos
}
}
.lcbr {
// Map `{"age": 20}` or `{ x | foo:bar, a:10 }`
p.next()

View File

@ -0,0 +1,24 @@
// _likely_(expr) should be compilable, and it should return the expr
fn test_likely_type(){
assert typeof(_likely_(false)) == 'bool'
}
fn test_likely(){
if _likely_(2<10) {
assert true
eprintln('ok, happens every time')
} else {
eprintln('happens *infrequently*')
assert false
}
}
fn test_likely_returns_the_value_of_its_bool_argument(){
i := 123
if _likely_(i<2) {
assert false
} else {
assert true
}
}

View File

@ -112,6 +112,7 @@ pub enum Kind {
key_return
key_select
key_sizeof
key_likely
key_offsetof
key_struct
key_switch
@ -218,6 +219,7 @@ fn build_token_str() []string {
s[Kind.key_return] = 'return'
s[Kind.key_module] = 'module'
s[Kind.key_sizeof] = 'sizeof'
s[Kind.key_likely] = '_likely_'
s[Kind.key_go] = 'go'
s[Kind.key_goto] = 'goto'
s[Kind.key_const] = 'const'