v: add full compiler support for _likely_(x)
parent
6663e94780
commit
c7d4360931
16
doc/docs.md
16
doc/docs.md
|
@ -1684,6 +1684,22 @@ vm := vmod.decode( @VMOD_FILE ) or { panic(err) }
|
||||||
eprintln('$vm.name $vm.version\n $vm.description')
|
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
|
## Reflection via codegen
|
||||||
|
|
||||||
Having built-in JSON support is nice, but V also allows you to create efficient
|
Having built-in JSON support is nice, but V also allows you to create efficient
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
module builtin
|
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>
|
// <string.h>
|
||||||
fn C.memcpy(byteptr, byteptr, int) voidptr
|
fn C.memcpy(byteptr, byteptr, int) voidptr
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ pub type Expr = AnonFn | ArrayInit | AsCast | AssignExpr | Assoc | BoolLiteral |
|
||||||
CastExpr | CharLiteral | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr |
|
CastExpr | CharLiteral | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr |
|
||||||
IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | MapInit | MatchExpr | None | OrExpr |
|
IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | MapInit | MatchExpr | None | OrExpr |
|
||||||
ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | StringInterLiteral |
|
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 |
|
pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | Comment | CompIf | ConstDecl |
|
||||||
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt |
|
DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt |
|
||||||
|
@ -755,6 +755,12 @@ pub:
|
||||||
type_name string
|
type_name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Likely {
|
||||||
|
pub:
|
||||||
|
expr Expr
|
||||||
|
pos token.Position
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TypeOf {
|
pub struct TypeOf {
|
||||||
pub:
|
pub:
|
||||||
expr Expr
|
expr Expr
|
||||||
|
@ -888,6 +894,9 @@ pub fn (expr Expr) position() token.Position {
|
||||||
StructInit {
|
StructInit {
|
||||||
return it.pos
|
return it.pos
|
||||||
}
|
}
|
||||||
|
Likely {
|
||||||
|
return it.pos
|
||||||
|
}
|
||||||
// ast.TypeOf { }
|
// ast.TypeOf { }
|
||||||
else {
|
else {
|
||||||
return token.Position{}
|
return token.Position{}
|
||||||
|
|
|
@ -163,6 +163,9 @@ pub fn (x Expr) str() string {
|
||||||
TypeOf {
|
TypeOf {
|
||||||
return 'typeof(${it.expr.str()})'
|
return 'typeof(${it.expr.str()})'
|
||||||
}
|
}
|
||||||
|
Likely {
|
||||||
|
return '_likely_(${it.expr.str()})'
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return '[unhandled expr type ${typeof(x)}]'
|
return '[unhandled expr type ${typeof(x)}]'
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
c.error('checker: too many expr levels: $c.expr_level ', node.position())
|
||||||
return table.void_type
|
return table.void_type
|
||||||
}
|
}
|
||||||
|
|
||||||
match mut node {
|
match mut node {
|
||||||
ast.AnonFn {
|
ast.AnonFn {
|
||||||
keep_fn := c.cur_fn
|
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)
|
it.expr_type = c.expr(it.expr)
|
||||||
return table.string_type
|
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 {
|
else {
|
||||||
tnode := typeof(node)
|
tnode := typeof(node)
|
||||||
if tnode != 'unknown v.ast.Expr' {
|
if tnode != 'unknown v.ast.Expr' {
|
||||||
|
|
|
@ -734,6 +734,11 @@ pub fn (mut f Fmt) expr(node ast.Expr) {
|
||||||
f.expr(it.expr)
|
f.expr(it.expr)
|
||||||
f.write(')')
|
f.write(')')
|
||||||
}
|
}
|
||||||
|
ast.Likely {
|
||||||
|
f.write('_likely_(')
|
||||||
|
f.expr(it.expr)
|
||||||
|
f.write(')')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1586,6 +1586,11 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||||
ast.TypeOf {
|
ast.TypeOf {
|
||||||
g.typeof_expr(it)
|
g.typeof_expr(it)
|
||||||
}
|
}
|
||||||
|
ast.Likely {
|
||||||
|
g.write('_likely_(')
|
||||||
|
g.expr(it.expr)
|
||||||
|
g.write(')')
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// #printf("node=%d\n", node.typ);
|
// #printf("node=%d\n", node.typ);
|
||||||
println(term.red('cgen.expr(): bad node ' + typeof(node)))
|
println(term.red('cgen.expr(): bad node ' + typeof(node)))
|
||||||
|
|
|
@ -627,6 +627,11 @@ fn (mut g JsGen) expr(node ast.Expr) {
|
||||||
// skip: JS has no types
|
// skip: JS has no types
|
||||||
// TODO maybe?
|
// TODO maybe?
|
||||||
}
|
}
|
||||||
|
ast.Likely {
|
||||||
|
g.write('(')
|
||||||
|
g.expr(it.expr)
|
||||||
|
g.write(')')
|
||||||
|
}
|
||||||
ast.TypeOf {
|
ast.TypeOf {
|
||||||
g.gen_typeof_expr(it)
|
g.gen_typeof_expr(it)
|
||||||
// TODO: Should this print the V type or the JS type?
|
// TODO: Should this print the V type or the JS type?
|
||||||
|
|
|
@ -103,6 +103,17 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr {
|
||||||
expr: 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 {
|
.lcbr {
|
||||||
// Map `{"age": 20}` or `{ x | foo:bar, a:10 }`
|
// Map `{"age": 20}` or `{ x | foo:bar, a:10 }`
|
||||||
p.next()
|
p.next()
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -112,6 +112,7 @@ pub enum Kind {
|
||||||
key_return
|
key_return
|
||||||
key_select
|
key_select
|
||||||
key_sizeof
|
key_sizeof
|
||||||
|
key_likely
|
||||||
key_offsetof
|
key_offsetof
|
||||||
key_struct
|
key_struct
|
||||||
key_switch
|
key_switch
|
||||||
|
@ -218,6 +219,7 @@ fn build_token_str() []string {
|
||||||
s[Kind.key_return] = 'return'
|
s[Kind.key_return] = 'return'
|
||||||
s[Kind.key_module] = 'module'
|
s[Kind.key_module] = 'module'
|
||||||
s[Kind.key_sizeof] = 'sizeof'
|
s[Kind.key_sizeof] = 'sizeof'
|
||||||
|
s[Kind.key_likely] = '_likely_'
|
||||||
s[Kind.key_go] = 'go'
|
s[Kind.key_go] = 'go'
|
||||||
s[Kind.key_goto] = 'goto'
|
s[Kind.key_goto] = 'goto'
|
||||||
s[Kind.key_const] = 'const'
|
s[Kind.key_const] = 'const'
|
||||||
|
|
Loading…
Reference in New Issue