From 4fc41c4bc4277576e9c644e3529d6dae8b02f68b Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Tue, 9 Jun 2020 18:08:31 +0300 Subject: [PATCH] v: add compiler support for _unlikely_(x) too --- doc/docs.md | 3 +++ vlib/v/ast/ast.v | 1 + vlib/v/checker/checker.v | 3 ++- vlib/v/fmt/fmt.v | 7 ++++++- vlib/v/gen/cgen.v | 7 ++++++- vlib/v/gen/cheaders.v | 2 ++ vlib/v/parser/pratt.v | 4 +++- vlib/v/tests/supports__likely__test.v | 26 +++++++++++++++++--------- vlib/v/token/token.v | 2 ++ 9 files changed, 42 insertions(+), 13 deletions(-) diff --git a/doc/docs.md b/doc/docs.md index e3d7fbb593..daa17b75cd 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -1700,6 +1700,9 @@ 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. +`if _unlikely_(bool expression) {` similar to `_likely_(x)`, but it hints that +the boolean expression is highly improbable. 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 diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 7af4ae3f99..84414f970d 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -759,6 +759,7 @@ pub struct Likely { pub: expr Expr pos token.Position + is_likely bool // false for _unlikely_ } pub struct TypeOf { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index c1557a12e7..365bb95940 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1948,7 +1948,8 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { 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) + lname := if it.is_likely { '_likely_' } else { '_unlikely_' } + c.error('`${lname}()` expects a boolean expression, instead it got `${ltype_sym.name}`', it.pos) } return table.bool_type } diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 06f9417b09..0f1c02740c 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -735,7 +735,12 @@ pub fn (mut f Fmt) expr(node ast.Expr) { f.write(')') } ast.Likely { - f.write('_likely_(') + if it.is_likely { + f.write('_likely_') + } else { + f.write('_unlikely_') + } + f.write('(') f.expr(it.expr) f.write(')') } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 475c20a60b..f4296b4792 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -1587,7 +1587,12 @@ fn (mut g Gen) expr(node ast.Expr) { g.typeof_expr(it) } ast.Likely { - g.write('_likely_(') + if it.is_likely { + g.write('_likely_') + } else { + g.write('_unlikely_') + } + g.write('(') g.expr(it.expr) g.write(')') } diff --git a/vlib/v/gen/cheaders.v b/vlib/v/gen/cheaders.v index e99860d538..b6a0e83e7d 100644 --- a/vlib/v/gen/cheaders.v +++ b/vlib/v/gen/cheaders.v @@ -255,8 +255,10 @@ void _vcleanup(); #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) || defined(__TINYC__) #define _likely_(x) __builtin_expect(x, 1) +#define _unlikely_(x) __builtin_expect((x), 0) #else #define _likely_(x) (x) +#define _unlikely_(x) (x) #endif #if defined(TARGET_ORDER_IS_LITTLE) diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index 1051ad2eb6..e9a7e0eb63 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -103,7 +103,8 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { expr: expr } } - .key_likely { + .key_likely, .key_unlikely { + is_likely := p.tok.kind == .key_likely p.next() p.check(.lpar) lpos := p.tok.position() @@ -112,6 +113,7 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { node = ast.Likely{ expr: expr pos: lpos + is_likely: is_likely } } .lcbr { diff --git a/vlib/v/tests/supports__likely__test.v b/vlib/v/tests/supports__likely__test.v index 555cc60925..1ba38a5bc2 100644 --- a/vlib/v/tests/supports__likely__test.v +++ b/vlib/v/tests/supports__likely__test.v @@ -1,24 +1,32 @@ // _likely_(expr) should be compilable, and it should return the expr - -fn test_likely_type(){ +fn test_likely_type() { assert typeof(_likely_(false)) == 'bool' -} - -fn test_likely(){ - if _likely_(2<10) { + assert _likely_(false) == false + assert _likely_(true) == true +} + +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(){ +fn test_likely_returns_the_value_of_its_bool_argument() { i := 123 - if _likely_(i<2) { + if _likely_(i < 2) { assert false } else { assert true } } + +// _unlikely_ is the same as _likely_ from the V point of view: +fn test_unlikely_type() { + assert typeof(_unlikely_(false)) == 'bool' + assert _unlikely_(false) == false + assert _unlikely_(true) == true +} diff --git a/vlib/v/token/token.v b/vlib/v/token/token.v index 6f338faba7..a6f0f0b066 100644 --- a/vlib/v/token/token.v +++ b/vlib/v/token/token.v @@ -113,6 +113,7 @@ pub enum Kind { key_select key_sizeof key_likely + key_unlikely key_offsetof key_struct key_switch @@ -220,6 +221,7 @@ fn build_token_str() []string { s[Kind.key_module] = 'module' s[Kind.key_sizeof] = 'sizeof' s[Kind.key_likely] = '_likely_' + s[Kind.key_unlikely] = '_unlikely_' s[Kind.key_go] = 'go' s[Kind.key_goto] = 'goto' s[Kind.key_const] = 'const'