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