162306a36Sopenharmony_ci/* Simple expression parser */ 262306a36Sopenharmony_ci%{ 362306a36Sopenharmony_ci#define YYDEBUG 1 462306a36Sopenharmony_ci#include <assert.h> 562306a36Sopenharmony_ci#include <math.h> 662306a36Sopenharmony_ci#include <stdlib.h> 762306a36Sopenharmony_ci#include "util/debug.h" 862306a36Sopenharmony_ci#define IN_EXPR_Y 1 962306a36Sopenharmony_ci#include "expr.h" 1062306a36Sopenharmony_ci#include "expr-bison.h" 1162306a36Sopenharmony_ciint expr_lex(YYSTYPE * yylval_param , void *yyscanner); 1262306a36Sopenharmony_ci%} 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci%define api.pure full 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci%parse-param { double *final_val } 1762306a36Sopenharmony_ci%parse-param { struct expr_parse_ctx *ctx } 1862306a36Sopenharmony_ci%parse-param { bool compute_ids } 1962306a36Sopenharmony_ci%parse-param {void *scanner} 2062306a36Sopenharmony_ci%lex-param {void* scanner} 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci%union { 2362306a36Sopenharmony_ci double num; 2462306a36Sopenharmony_ci char *str; 2562306a36Sopenharmony_ci struct ids { 2662306a36Sopenharmony_ci /* 2762306a36Sopenharmony_ci * When creating ids, holds the working set of event ids. NULL 2862306a36Sopenharmony_ci * implies the set is empty. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci struct hashmap *ids; 3162306a36Sopenharmony_ci /* 3262306a36Sopenharmony_ci * The metric value. When not creating ids this is the value 3362306a36Sopenharmony_ci * read from a counter, a constant or some computed value. When 3462306a36Sopenharmony_ci * creating ids the value is either a constant or BOTTOM. NAN is 3562306a36Sopenharmony_ci * used as the special BOTTOM value, representing a "set of all 3662306a36Sopenharmony_ci * values" case. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci double val; 3962306a36Sopenharmony_ci } ids; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT STRCMP_CPUID_STR EXPR_ERROR 4362306a36Sopenharmony_ci%left MIN MAX IF 4462306a36Sopenharmony_ci%left '|' 4562306a36Sopenharmony_ci%left '^' 4662306a36Sopenharmony_ci%left '&' 4762306a36Sopenharmony_ci%left '<' '>' 4862306a36Sopenharmony_ci%left '-' '+' 4962306a36Sopenharmony_ci%left '*' '/' '%' 5062306a36Sopenharmony_ci%left NEG NOT 5162306a36Sopenharmony_ci%type <num> NUMBER LITERAL 5262306a36Sopenharmony_ci%type <str> ID 5362306a36Sopenharmony_ci%destructor { free ($$); } <str> 5462306a36Sopenharmony_ci%type <ids> expr if_expr 5562306a36Sopenharmony_ci%destructor { ids__free($$.ids); } <ids> 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci%{ 5862306a36Sopenharmony_cistatic void expr_error(double *final_val __maybe_unused, 5962306a36Sopenharmony_ci struct expr_parse_ctx *ctx __maybe_unused, 6062306a36Sopenharmony_ci bool compute_ids __maybe_unused, 6162306a36Sopenharmony_ci void *scanner __maybe_unused, 6262306a36Sopenharmony_ci const char *s) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci pr_debug("%s\n", s); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* 6862306a36Sopenharmony_ci * During compute ids, the special "bottom" value uses NAN to represent the set 6962306a36Sopenharmony_ci * of all values. NAN is selected as it isn't a useful constant value. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci#define BOTTOM NAN 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* During computing ids, does val represent a constant (non-BOTTOM) value? */ 7462306a36Sopenharmony_cistatic bool is_const(double val) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci return isfinite(val); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic struct ids union_expr(struct ids ids1, struct ids ids2) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct ids result = { 8262306a36Sopenharmony_ci .val = BOTTOM, 8362306a36Sopenharmony_ci .ids = ids__union(ids1.ids, ids2.ids), 8462306a36Sopenharmony_ci }; 8562306a36Sopenharmony_ci return result; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic struct ids handle_id(struct expr_parse_ctx *ctx, char *id, 8962306a36Sopenharmony_ci bool compute_ids, bool source_count) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct ids result; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (!compute_ids) { 9462306a36Sopenharmony_ci /* 9562306a36Sopenharmony_ci * Compute the event's value from ID. If the ID isn't known then 9662306a36Sopenharmony_ci * it isn't used to compute the formula so set to NAN. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_ci struct expr_id_data *data; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci result.val = NAN; 10162306a36Sopenharmony_ci if (expr__resolve_id(ctx, id, &data) == 0) { 10262306a36Sopenharmony_ci result.val = source_count 10362306a36Sopenharmony_ci ? expr_id_data__source_count(data) 10462306a36Sopenharmony_ci : expr_id_data__value(data); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci result.ids = NULL; 10762306a36Sopenharmony_ci free(id); 10862306a36Sopenharmony_ci } else { 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * Set the value to BOTTOM to show that any value is possible 11162306a36Sopenharmony_ci * when the event is computed. Create a set of just the ID. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci result.val = BOTTOM; 11462306a36Sopenharmony_ci result.ids = ids__new(); 11562306a36Sopenharmony_ci if (!result.ids || ids__insert(result.ids, id)) { 11662306a36Sopenharmony_ci pr_err("Error creating IDs for '%s'", id); 11762306a36Sopenharmony_ci free(id); 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci return result; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * If we're not computing ids or $1 and $3 are constants, compute the new 12562306a36Sopenharmony_ci * constant value using OP. Its invariant that there are no ids. If computing 12662306a36Sopenharmony_ci * ids for non-constants union the set of IDs that must be computed. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci#define BINARY_OP(RESULT, OP, LHS, RHS) \ 12962306a36Sopenharmony_ci if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \ 13062306a36Sopenharmony_ci assert(LHS.ids == NULL); \ 13162306a36Sopenharmony_ci assert(RHS.ids == NULL); \ 13262306a36Sopenharmony_ci if (isnan(LHS.val) || isnan(RHS.val)) { \ 13362306a36Sopenharmony_ci RESULT.val = NAN; \ 13462306a36Sopenharmony_ci } else { \ 13562306a36Sopenharmony_ci RESULT.val = LHS.val OP RHS.val; \ 13662306a36Sopenharmony_ci } \ 13762306a36Sopenharmony_ci RESULT.ids = NULL; \ 13862306a36Sopenharmony_ci } else { \ 13962306a36Sopenharmony_ci RESULT = union_expr(LHS, RHS); \ 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci%} 14362306a36Sopenharmony_ci%% 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistart: if_expr 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci if (compute_ids) 14862306a36Sopenharmony_ci ctx->ids = ids__union($1.ids, ctx->ids); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (final_val) 15162306a36Sopenharmony_ci *final_val = $1.val; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciif_expr: expr IF expr ELSE if_expr 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci if (fpclassify($3.val) == FP_ZERO) { 15862306a36Sopenharmony_ci /* 15962306a36Sopenharmony_ci * The IF expression evaluated to 0 so treat as false, take the 16062306a36Sopenharmony_ci * ELSE and discard everything else. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci $$.val = $5.val; 16362306a36Sopenharmony_ci $$.ids = $5.ids; 16462306a36Sopenharmony_ci ids__free($1.ids); 16562306a36Sopenharmony_ci ids__free($3.ids); 16662306a36Sopenharmony_ci } else if (!compute_ids || is_const($3.val)) { 16762306a36Sopenharmony_ci /* 16862306a36Sopenharmony_ci * If ids aren't computed then treat the expression as true. If 16962306a36Sopenharmony_ci * ids are being computed and the IF expr is a non-zero 17062306a36Sopenharmony_ci * constant, then also evaluate the true case. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci $$.val = $1.val; 17362306a36Sopenharmony_ci $$.ids = $1.ids; 17462306a36Sopenharmony_ci ids__free($3.ids); 17562306a36Sopenharmony_ci ids__free($5.ids); 17662306a36Sopenharmony_ci } else if ($1.val == $5.val) { 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * LHS == RHS, so both are an identical constant. No need to 17962306a36Sopenharmony_ci * evaluate any events. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci $$.val = $1.val; 18262306a36Sopenharmony_ci $$.ids = NULL; 18362306a36Sopenharmony_ci ids__free($1.ids); 18462306a36Sopenharmony_ci ids__free($3.ids); 18562306a36Sopenharmony_ci ids__free($5.ids); 18662306a36Sopenharmony_ci } else { 18762306a36Sopenharmony_ci /* 18862306a36Sopenharmony_ci * Value is either the LHS or RHS and we need the IF expression 18962306a36Sopenharmony_ci * to compute it. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci $$ = union_expr($1, union_expr($3, $5)); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci| expr 19562306a36Sopenharmony_ci; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ciexpr: NUMBER 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci $$.val = $1; 20062306a36Sopenharmony_ci $$.ids = NULL; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci| ID { $$ = handle_id(ctx, $1, compute_ids, /*source_count=*/false); } 20362306a36Sopenharmony_ci| SOURCE_COUNT '(' ID ')' { $$ = handle_id(ctx, $3, compute_ids, /*source_count=*/true); } 20462306a36Sopenharmony_ci| HAS_EVENT '(' ID ')' 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci $$.val = expr__has_event(ctx, compute_ids, $3); 20762306a36Sopenharmony_ci $$.ids = NULL; 20862306a36Sopenharmony_ci free($3); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci| STRCMP_CPUID_STR '(' ID ')' 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci $$.val = expr__strcmp_cpuid_str(ctx, compute_ids, $3); 21362306a36Sopenharmony_ci $$.ids = NULL; 21462306a36Sopenharmony_ci free($3); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci| expr '|' expr 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci if (is_const($1.val) && is_const($3.val)) { 21962306a36Sopenharmony_ci assert($1.ids == NULL); 22062306a36Sopenharmony_ci assert($3.ids == NULL); 22162306a36Sopenharmony_ci $$.ids = NULL; 22262306a36Sopenharmony_ci $$.val = (fpclassify($1.val) == FP_ZERO && fpclassify($3.val) == FP_ZERO) ? 0 : 1; 22362306a36Sopenharmony_ci } else if (is_const($1.val)) { 22462306a36Sopenharmony_ci assert($1.ids == NULL); 22562306a36Sopenharmony_ci if (fpclassify($1.val) == FP_ZERO) { 22662306a36Sopenharmony_ci $$ = $3; 22762306a36Sopenharmony_ci } else { 22862306a36Sopenharmony_ci $$.val = 1; 22962306a36Sopenharmony_ci $$.ids = NULL; 23062306a36Sopenharmony_ci ids__free($3.ids); 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci } else if (is_const($3.val)) { 23362306a36Sopenharmony_ci assert($3.ids == NULL); 23462306a36Sopenharmony_ci if (fpclassify($3.val) == FP_ZERO) { 23562306a36Sopenharmony_ci $$ = $1; 23662306a36Sopenharmony_ci } else { 23762306a36Sopenharmony_ci $$.val = 1; 23862306a36Sopenharmony_ci $$.ids = NULL; 23962306a36Sopenharmony_ci ids__free($1.ids); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci } else { 24262306a36Sopenharmony_ci $$ = union_expr($1, $3); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci| expr '&' expr 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci if (is_const($1.val) && is_const($3.val)) { 24862306a36Sopenharmony_ci assert($1.ids == NULL); 24962306a36Sopenharmony_ci assert($3.ids == NULL); 25062306a36Sopenharmony_ci $$.val = (fpclassify($1.val) != FP_ZERO && fpclassify($3.val) != FP_ZERO) ? 1 : 0; 25162306a36Sopenharmony_ci $$.ids = NULL; 25262306a36Sopenharmony_ci } else if (is_const($1.val)) { 25362306a36Sopenharmony_ci assert($1.ids == NULL); 25462306a36Sopenharmony_ci if (fpclassify($1.val) != FP_ZERO) { 25562306a36Sopenharmony_ci $$ = $3; 25662306a36Sopenharmony_ci } else { 25762306a36Sopenharmony_ci $$.val = 0; 25862306a36Sopenharmony_ci $$.ids = NULL; 25962306a36Sopenharmony_ci ids__free($3.ids); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci } else if (is_const($3.val)) { 26262306a36Sopenharmony_ci assert($3.ids == NULL); 26362306a36Sopenharmony_ci if (fpclassify($3.val) != FP_ZERO) { 26462306a36Sopenharmony_ci $$ = $1; 26562306a36Sopenharmony_ci } else { 26662306a36Sopenharmony_ci $$.val = 0; 26762306a36Sopenharmony_ci $$.ids = NULL; 26862306a36Sopenharmony_ci ids__free($1.ids); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci } else { 27162306a36Sopenharmony_ci $$ = union_expr($1, $3); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci| expr '^' expr 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci if (is_const($1.val) && is_const($3.val)) { 27762306a36Sopenharmony_ci assert($1.ids == NULL); 27862306a36Sopenharmony_ci assert($3.ids == NULL); 27962306a36Sopenharmony_ci $$.val = (fpclassify($1.val) == FP_ZERO) != (fpclassify($3.val) == FP_ZERO) ? 1 : 0; 28062306a36Sopenharmony_ci $$.ids = NULL; 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci $$ = union_expr($1, $3); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci| expr '<' expr { BINARY_OP($$, <, $1, $3); } 28662306a36Sopenharmony_ci| expr '>' expr { BINARY_OP($$, >, $1, $3); } 28762306a36Sopenharmony_ci| expr '+' expr { BINARY_OP($$, +, $1, $3); } 28862306a36Sopenharmony_ci| expr '-' expr { BINARY_OP($$, -, $1, $3); } 28962306a36Sopenharmony_ci| expr '*' expr { BINARY_OP($$, *, $1, $3); } 29062306a36Sopenharmony_ci| expr '/' expr 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci if (fpclassify($3.val) == FP_ZERO) { 29362306a36Sopenharmony_ci pr_debug("division by zero\n"); 29462306a36Sopenharmony_ci assert($3.ids == NULL); 29562306a36Sopenharmony_ci if (compute_ids) 29662306a36Sopenharmony_ci ids__free($1.ids); 29762306a36Sopenharmony_ci $$.val = NAN; 29862306a36Sopenharmony_ci $$.ids = NULL; 29962306a36Sopenharmony_ci } else if (!compute_ids || (is_const($1.val) && is_const($3.val))) { 30062306a36Sopenharmony_ci assert($1.ids == NULL); 30162306a36Sopenharmony_ci assert($3.ids == NULL); 30262306a36Sopenharmony_ci $$.val = $1.val / $3.val; 30362306a36Sopenharmony_ci $$.ids = NULL; 30462306a36Sopenharmony_ci } else { 30562306a36Sopenharmony_ci /* LHS and/or RHS need computing from event IDs so union. */ 30662306a36Sopenharmony_ci $$ = union_expr($1, $3); 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci| expr '%' expr 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci if (fpclassify($3.val) == FP_ZERO) { 31262306a36Sopenharmony_ci pr_debug("division by zero\n"); 31362306a36Sopenharmony_ci YYABORT; 31462306a36Sopenharmony_ci } else if (!compute_ids || (is_const($1.val) && is_const($3.val))) { 31562306a36Sopenharmony_ci assert($1.ids == NULL); 31662306a36Sopenharmony_ci assert($3.ids == NULL); 31762306a36Sopenharmony_ci $$.val = (long)$1.val % (long)$3.val; 31862306a36Sopenharmony_ci $$.ids = NULL; 31962306a36Sopenharmony_ci } else { 32062306a36Sopenharmony_ci /* LHS and/or RHS need computing from event IDs so union. */ 32162306a36Sopenharmony_ci $$ = union_expr($1, $3); 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci| D_RATIO '(' expr ',' expr ')' 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci if (fpclassify($5.val) == FP_ZERO) { 32762306a36Sopenharmony_ci /* 32862306a36Sopenharmony_ci * Division by constant zero always yields zero and no events 32962306a36Sopenharmony_ci * are necessary. 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ci assert($5.ids == NULL); 33262306a36Sopenharmony_ci $$.val = 0.0; 33362306a36Sopenharmony_ci $$.ids = NULL; 33462306a36Sopenharmony_ci ids__free($3.ids); 33562306a36Sopenharmony_ci } else if (!compute_ids || (is_const($3.val) && is_const($5.val))) { 33662306a36Sopenharmony_ci assert($3.ids == NULL); 33762306a36Sopenharmony_ci assert($5.ids == NULL); 33862306a36Sopenharmony_ci $$.val = $3.val / $5.val; 33962306a36Sopenharmony_ci $$.ids = NULL; 34062306a36Sopenharmony_ci } else { 34162306a36Sopenharmony_ci /* LHS and/or RHS need computing from event IDs so union. */ 34262306a36Sopenharmony_ci $$ = union_expr($3, $5); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci| '-' expr %prec NEG 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci $$.val = -$2.val; 34862306a36Sopenharmony_ci $$.ids = $2.ids; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci| '(' if_expr ')' 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci $$ = $2; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci| MIN '(' expr ',' expr ')' 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci if (!compute_ids) { 35762306a36Sopenharmony_ci $$.val = $3.val < $5.val ? $3.val : $5.val; 35862306a36Sopenharmony_ci $$.ids = NULL; 35962306a36Sopenharmony_ci } else { 36062306a36Sopenharmony_ci $$ = union_expr($3, $5); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci| MAX '(' expr ',' expr ')' 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci if (!compute_ids) { 36662306a36Sopenharmony_ci $$.val = $3.val > $5.val ? $3.val : $5.val; 36762306a36Sopenharmony_ci $$.ids = NULL; 36862306a36Sopenharmony_ci } else { 36962306a36Sopenharmony_ci $$ = union_expr($3, $5); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci| LITERAL 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci $$.val = $1; 37562306a36Sopenharmony_ci $$.ids = NULL; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci%% 380