xref: /openbmc/linux/tools/perf/util/expr.y (revision 1e7ab82975995d2238db8d8bad64e3aed34cfa26)
107516736SAndi Kleen /* Simple expression parser */
207516736SAndi Kleen %{
326226a97SJiri Olsa #define YYDEBUG 1
43f965a7dSIan Rogers #include <assert.h>
5edfe7f55SIan Rogers #include <math.h>
6*1e7ab829SIan Rogers #include <stdlib.h>
707516736SAndi Kleen #include "util/debug.h"
807516736SAndi Kleen #define IN_EXPR_Y 1
907516736SAndi Kleen #include "expr.h"
1007516736SAndi Kleen %}
1107516736SAndi Kleen 
12fc8c0a99SJiri Olsa %define api.pure full
13fc8c0a99SJiri Olsa 
1407516736SAndi Kleen %parse-param { double *final_val }
15aecce63eSJiri Olsa %parse-param { struct expr_parse_ctx *ctx }
163f965a7dSIan Rogers %parse-param { bool compute_ids }
1726226a97SJiri Olsa %parse-param {void *scanner}
1826226a97SJiri Olsa %lex-param {void* scanner}
1907516736SAndi Kleen 
2007516736SAndi Kleen %union {
2107516736SAndi Kleen 	double	 num;
2226226a97SJiri Olsa 	char	*str;
233f965a7dSIan Rogers 	struct ids {
243f965a7dSIan Rogers 		/*
253f965a7dSIan Rogers 		 * When creating ids, holds the working set of event ids. NULL
263f965a7dSIan Rogers 		 * implies the set is empty.
273f965a7dSIan Rogers 		 */
283f965a7dSIan Rogers 		struct hashmap *ids;
293f965a7dSIan Rogers 		/*
303f965a7dSIan Rogers 		 * The metric value. When not creating ids this is the value
313f965a7dSIan Rogers 		 * read from a counter, a constant or some computed value. When
323f965a7dSIan Rogers 		 * creating ids the value is either a constant or BOTTOM. NAN is
333f965a7dSIan Rogers 		 * used as the special BOTTOM value, representing a "set of all
343f965a7dSIan Rogers 		 * values" case.
353f965a7dSIan Rogers 		 */
363f965a7dSIan Rogers 		double val;
373f965a7dSIan Rogers 	} ids;
3807516736SAndi Kleen }
3907516736SAndi Kleen 
403613f6c1SIan Rogers %token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO EXPR_ERROR
41d73bad06SAndi Kleen %left MIN MAX IF
4207516736SAndi Kleen %left '|'
4307516736SAndi Kleen %left '^'
4407516736SAndi Kleen %left '&'
45ff1a12f9SIan Rogers %left '<' '>'
4607516736SAndi Kleen %left '-' '+'
4707516736SAndi Kleen %left '*' '/' '%'
4807516736SAndi Kleen %left NEG NOT
493613f6c1SIan Rogers %type <num> NUMBER LITERAL
50aed0d6f8SIan Rogers %type <str> ID
51aed0d6f8SIan Rogers %destructor { free ($$); } <str>
523f965a7dSIan Rogers %type <ids> expr if_expr
533f965a7dSIan Rogers %destructor { ids__free($$.ids); } <ids>
5407516736SAndi Kleen 
5507516736SAndi Kleen %{
5626226a97SJiri Olsa static void expr_error(double *final_val __maybe_unused,
57aecce63eSJiri Olsa 		       struct expr_parse_ctx *ctx __maybe_unused,
583f965a7dSIan Rogers 		       bool compute_ids __maybe_unused,
5926226a97SJiri Olsa 		       void *scanner,
6007516736SAndi Kleen 		       const char *s)
6107516736SAndi Kleen {
6207516736SAndi Kleen 	pr_debug("%s\n", s);
6307516736SAndi Kleen }
6407516736SAndi Kleen 
653f965a7dSIan Rogers /*
663f965a7dSIan Rogers  * During compute ids, the special "bottom" value uses NAN to represent the set
673f965a7dSIan Rogers  * of all values. NAN is selected as it isn't a useful constant value.
683f965a7dSIan Rogers  */
693f965a7dSIan Rogers #define BOTTOM NAN
703f965a7dSIan Rogers 
71970f7afeSIan Rogers /* During computing ids, does val represent a constant (non-BOTTOM) value? */
72970f7afeSIan Rogers static bool is_const(double val)
73970f7afeSIan Rogers {
74970f7afeSIan Rogers 	return isfinite(val);
75970f7afeSIan Rogers }
76970f7afeSIan Rogers 
773f965a7dSIan Rogers static struct ids union_expr(struct ids ids1, struct ids ids2)
783f965a7dSIan Rogers {
793f965a7dSIan Rogers 	struct ids result = {
803f965a7dSIan Rogers 		.val = BOTTOM,
813f965a7dSIan Rogers 		.ids = ids__union(ids1.ids, ids2.ids),
823f965a7dSIan Rogers 	};
833f965a7dSIan Rogers 	return result;
843f965a7dSIan Rogers }
853f965a7dSIan Rogers 
86*1e7ab829SIan Rogers static struct ids handle_id(struct expr_parse_ctx *ctx, char *id,
87*1e7ab829SIan Rogers 			    bool compute_ids)
88*1e7ab829SIan Rogers {
89*1e7ab829SIan Rogers 	struct ids result;
90*1e7ab829SIan Rogers 
91*1e7ab829SIan Rogers 	if (!compute_ids) {
92*1e7ab829SIan Rogers 		/*
93*1e7ab829SIan Rogers 		 * Compute the event's value from ID. If the ID isn't known then
94*1e7ab829SIan Rogers 		 * it isn't used to compute the formula so set to NAN.
95*1e7ab829SIan Rogers 		 */
96*1e7ab829SIan Rogers 		struct expr_id_data *data;
97*1e7ab829SIan Rogers 
98*1e7ab829SIan Rogers 		result.val = NAN;
99*1e7ab829SIan Rogers 		if (expr__resolve_id(ctx, id, &data) == 0)
100*1e7ab829SIan Rogers 			result.val = expr_id_data__value(data);
101*1e7ab829SIan Rogers 
102*1e7ab829SIan Rogers 		result.ids = NULL;
103*1e7ab829SIan Rogers 		free(id);
104*1e7ab829SIan Rogers 	} else {
105*1e7ab829SIan Rogers 		/*
106*1e7ab829SIan Rogers 		 * Set the value to BOTTOM to show that any value is possible
107*1e7ab829SIan Rogers 		 * when the event is computed. Create a set of just the ID.
108*1e7ab829SIan Rogers 		 */
109*1e7ab829SIan Rogers 		result.val = BOTTOM;
110*1e7ab829SIan Rogers 		result.ids = ids__new();
111*1e7ab829SIan Rogers 		if (!result.ids || ids__insert(result.ids, id)) {
112*1e7ab829SIan Rogers 			pr_err("Error creating IDs for '%s'", id);
113*1e7ab829SIan Rogers 			free(id);
114*1e7ab829SIan Rogers 		}
115*1e7ab829SIan Rogers 	}
116*1e7ab829SIan Rogers 	return result;
117*1e7ab829SIan Rogers }
118*1e7ab829SIan Rogers 
119970f7afeSIan Rogers /*
120970f7afeSIan Rogers  * If we're not computing ids or $1 and $3 are constants, compute the new
121970f7afeSIan Rogers  * constant value using OP. Its invariant that there are no ids.  If computing
122970f7afeSIan Rogers  * ids for non-constants union the set of IDs that must be computed.
123970f7afeSIan Rogers  */
124e87576c5SIan Rogers #define BINARY_LONG_OP(RESULT, OP, LHS, RHS)				\
125970f7afeSIan Rogers 	if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \
126970f7afeSIan Rogers 		assert(LHS.ids == NULL);				\
127970f7afeSIan Rogers 		assert(RHS.ids == NULL);				\
1283f965a7dSIan Rogers 		RESULT.val = (long)LHS.val OP (long)RHS.val;		\
1293f965a7dSIan Rogers 		RESULT.ids = NULL;					\
1303f965a7dSIan Rogers 	} else {							\
1313f965a7dSIan Rogers 	        RESULT = union_expr(LHS, RHS);				\
1323f965a7dSIan Rogers 	}
133e87576c5SIan Rogers 
134e87576c5SIan Rogers #define BINARY_OP(RESULT, OP, LHS, RHS)					\
135970f7afeSIan Rogers 	if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \
136970f7afeSIan Rogers 		assert(LHS.ids == NULL);				\
137970f7afeSIan Rogers 		assert(RHS.ids == NULL);				\
1383f965a7dSIan Rogers 		RESULT.val = LHS.val OP RHS.val;			\
1393f965a7dSIan Rogers 		RESULT.ids = NULL;					\
1403f965a7dSIan Rogers 	} else {							\
1413f965a7dSIan Rogers 	        RESULT = union_expr(LHS, RHS);				\
1423f965a7dSIan Rogers 	}
143e87576c5SIan Rogers 
14407516736SAndi Kleen %}
14507516736SAndi Kleen %%
14607516736SAndi Kleen 
1473f965a7dSIan Rogers start: if_expr
14826226a97SJiri Olsa {
1493f965a7dSIan Rogers 	if (compute_ids)
1503f965a7dSIan Rogers 		ctx->ids = ids__union($1.ids, ctx->ids);
15126226a97SJiri Olsa 
1523f965a7dSIan Rogers 	if (final_val)
1533f965a7dSIan Rogers 		*final_val = $1.val;
1543f965a7dSIan Rogers }
1553f965a7dSIan Rogers ;
156d73bad06SAndi Kleen 
157c924e0ccSIan Rogers if_expr: expr IF expr ELSE expr
158c924e0ccSIan Rogers {
1593f965a7dSIan Rogers 	if (fpclassify($3.val) == FP_ZERO) {
160a8e4e880SIan Rogers 		/*
161a8e4e880SIan Rogers 		 * The IF expression evaluated to 0 so treat as false, take the
162a8e4e880SIan Rogers 		 * ELSE and discard everything else.
163a8e4e880SIan Rogers 		 */
1643f965a7dSIan Rogers 		$$.val = $5.val;
165a8e4e880SIan Rogers 		$$.ids = $5.ids;
166a8e4e880SIan Rogers 		ids__free($1.ids);
167a8e4e880SIan Rogers 		ids__free($3.ids);
168a8e4e880SIan Rogers 	} else if (!compute_ids || is_const($3.val)) {
169a8e4e880SIan Rogers 		/*
170a8e4e880SIan Rogers 		 * If ids aren't computed then treat the expression as true. If
171a8e4e880SIan Rogers 		 * ids are being computed and the IF expr is a non-zero
172a8e4e880SIan Rogers 		 * constant, then also evaluate the true case.
173a8e4e880SIan Rogers 		 */
1743f965a7dSIan Rogers 		$$.val = $1.val;
175a8e4e880SIan Rogers 		$$.ids = $1.ids;
176a8e4e880SIan Rogers 		ids__free($3.ids);
177a8e4e880SIan Rogers 		ids__free($5.ids);
17894886961SIan Rogers 	} else if ($1.val == $5.val) {
17994886961SIan Rogers 		/*
18094886961SIan Rogers 		 * LHS == RHS, so both are an identical constant. No need to
18194886961SIan Rogers 		 * evaluate any events.
18294886961SIan Rogers 		 */
18394886961SIan Rogers 		$$.val = $1.val;
18494886961SIan Rogers 		$$.ids = NULL;
18594886961SIan Rogers 		ids__free($1.ids);
18694886961SIan Rogers 		ids__free($3.ids);
18794886961SIan Rogers 		ids__free($5.ids);
1883f965a7dSIan Rogers 	} else {
189a8e4e880SIan Rogers 		/*
190a8e4e880SIan Rogers 		 * Value is either the LHS or RHS and we need the IF expression
191a8e4e880SIan Rogers 		 * to compute it.
192a8e4e880SIan Rogers 		 */
1933f965a7dSIan Rogers 		$$ = union_expr($1, union_expr($3, $5));
1943f965a7dSIan Rogers 	}
195c924e0ccSIan Rogers }
196d73bad06SAndi Kleen | expr
19707516736SAndi Kleen ;
19807516736SAndi Kleen 
19907516736SAndi Kleen expr: NUMBER
200c924e0ccSIan Rogers {
2013f965a7dSIan Rogers 	$$.val = $1;
2023f965a7dSIan Rogers 	$$.ids = NULL;
203c924e0ccSIan Rogers }
204*1e7ab829SIan Rogers | ID		{ $$ = handle_id(ctx, $1, compute_ids); }
205e87576c5SIan Rogers | expr '|' expr { BINARY_LONG_OP($$, |, $1, $3); }
206e87576c5SIan Rogers | expr '&' expr { BINARY_LONG_OP($$, &, $1, $3); }
207e87576c5SIan Rogers | expr '^' expr { BINARY_LONG_OP($$, ^, $1, $3); }
208e87576c5SIan Rogers | expr '<' expr { BINARY_OP($$, <, $1, $3); }
209e87576c5SIan Rogers | expr '>' expr { BINARY_OP($$, >, $1, $3); }
210e87576c5SIan Rogers | expr '+' expr { BINARY_OP($$, +, $1, $3); }
211e87576c5SIan Rogers | expr '-' expr { BINARY_OP($$, -, $1, $3); }
212e87576c5SIan Rogers | expr '*' expr { BINARY_OP($$, *, $1, $3); }
213c924e0ccSIan Rogers | expr '/' expr
214c924e0ccSIan Rogers {
2153f965a7dSIan Rogers 	if (fpclassify($3.val) == FP_ZERO) {
2169be27a5dSIan Rogers 		pr_debug("division by zero\n");
2179be27a5dSIan Rogers 		YYABORT;
218970f7afeSIan Rogers 	} else if (!compute_ids || (is_const($1.val) && is_const($3.val))) {
219970f7afeSIan Rogers 		assert($1.ids == NULL);
220970f7afeSIan Rogers 		assert($3.ids == NULL);
2213f965a7dSIan Rogers 		$$.val = $1.val / $3.val;
2223f965a7dSIan Rogers 		$$.ids = NULL;
2233f965a7dSIan Rogers 	} else {
224970f7afeSIan Rogers 		/* LHS and/or RHS need computing from event IDs so union. */
2253f965a7dSIan Rogers 		$$ = union_expr($1, $3);
2263f965a7dSIan Rogers 	}
2279be27a5dSIan Rogers }
228c924e0ccSIan Rogers | expr '%' expr
229c924e0ccSIan Rogers {
2303f965a7dSIan Rogers 	if (fpclassify($3.val) == FP_ZERO) {
2319be27a5dSIan Rogers 		pr_debug("division by zero\n");
2329be27a5dSIan Rogers 		YYABORT;
233970f7afeSIan Rogers 	} else if (!compute_ids || (is_const($1.val) && is_const($3.val))) {
234970f7afeSIan Rogers 		assert($1.ids == NULL);
235970f7afeSIan Rogers 		assert($3.ids == NULL);
2363f965a7dSIan Rogers 		$$.val = (long)$1.val % (long)$3.val;
2373f965a7dSIan Rogers 		$$.ids = NULL;
2383f965a7dSIan Rogers 	} else {
239970f7afeSIan Rogers 		/* LHS and/or RHS need computing from event IDs so union. */
2403f965a7dSIan Rogers 		$$ = union_expr($1, $3);
2413f965a7dSIan Rogers 	}
2429be27a5dSIan Rogers }
243c924e0ccSIan Rogers | D_RATIO '(' expr ',' expr ')'
244c924e0ccSIan Rogers {
2453f965a7dSIan Rogers 	if (fpclassify($5.val) == FP_ZERO) {
246970f7afeSIan Rogers 		/*
247970f7afeSIan Rogers 		 * Division by constant zero always yields zero and no events
248970f7afeSIan Rogers 		 * are necessary.
249970f7afeSIan Rogers 		 */
250970f7afeSIan Rogers 		assert($5.ids == NULL);
2513f965a7dSIan Rogers 		$$.val = 0.0;
252970f7afeSIan Rogers 		$$.ids = NULL;
253970f7afeSIan Rogers 		ids__free($3.ids);
254970f7afeSIan Rogers 	} else if (!compute_ids || (is_const($3.val) && is_const($5.val))) {
255970f7afeSIan Rogers 		assert($3.ids == NULL);
256970f7afeSIan Rogers 		assert($5.ids == NULL);
2573f965a7dSIan Rogers 		$$.val = $3.val / $5.val;
258970f7afeSIan Rogers 		$$.ids = NULL;
2593f965a7dSIan Rogers 	} else {
260970f7afeSIan Rogers 		/* LHS and/or RHS need computing from event IDs so union. */
2613f965a7dSIan Rogers 		$$ = union_expr($3, $5);
2627f8fdcbbSIan Rogers 	}
2637f8fdcbbSIan Rogers }
264c924e0ccSIan Rogers | '-' expr %prec NEG
265c924e0ccSIan Rogers {
2663f965a7dSIan Rogers 	$$.val = -$2.val;
2673f965a7dSIan Rogers 	$$.ids = $2.ids;
268c924e0ccSIan Rogers }
269c924e0ccSIan Rogers | '(' if_expr ')'
270c924e0ccSIan Rogers {
271c924e0ccSIan Rogers 	$$ = $2;
272c924e0ccSIan Rogers }
273c924e0ccSIan Rogers | MIN '(' expr ',' expr ')'
274c924e0ccSIan Rogers {
2753f965a7dSIan Rogers 	if (!compute_ids) {
2763f965a7dSIan Rogers 		$$.val = $3.val < $5.val ? $3.val : $5.val;
2773f965a7dSIan Rogers 		$$.ids = NULL;
2783f965a7dSIan Rogers 	} else {
2793f965a7dSIan Rogers 		$$ = union_expr($3, $5);
2803f965a7dSIan Rogers 	}
281c924e0ccSIan Rogers }
282c924e0ccSIan Rogers | MAX '(' expr ',' expr ')'
283c924e0ccSIan Rogers {
2843f965a7dSIan Rogers 	if (!compute_ids) {
2853f965a7dSIan Rogers 		$$.val = $3.val > $5.val ? $3.val : $5.val;
2863f965a7dSIan Rogers 		$$.ids = NULL;
2873f965a7dSIan Rogers 	} else {
2883f965a7dSIan Rogers 		$$ = union_expr($3, $5);
2893f965a7dSIan Rogers 	}
290c924e0ccSIan Rogers }
2913613f6c1SIan Rogers | LITERAL
292c924e0ccSIan Rogers {
2933613f6c1SIan Rogers 	$$.val = $1;
2943f965a7dSIan Rogers 	$$.ids = NULL;
295c924e0ccSIan Rogers }
29607516736SAndi Kleen ;
29707516736SAndi Kleen 
29807516736SAndi Kleen %%
299