1 /* Simple expression parser */
9 #include "util/debug.h"
12 #include "expr-bison.h"
13 int expr_lex
(YYSTYPE * yylval_param
, void *yyscanner
);
18 %parse
-param
{ double *final_val
}
19 %parse
-param
{ struct expr_parse_ctx
*ctx
}
20 %parse
-param
{ bool compute_ids
}
21 %parse
-param
{void *scanner
}
22 %lex
-param
{void* scanner
}
29 * When creating ids, holds the working set of event ids. NULL
30 * implies the set is empty.
34 * The metric value. When not creating ids this is the value
35 * read from a counter, a constant or some computed value. When
36 * creating ids the value is either a constant or BOTTOM. NAN is
37 * used as the special BOTTOM value, representing a "set of all
44 %token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT STRCMP_CPUID_STR EXPR_ERROR
53 %type
<num
> NUMBER LITERAL
55 %destructor
{ free
($$
); } <str
>
56 %type
<ids
> expr if_expr
57 %destructor
{ ids__free
($$.ids
); } <ids
>
60 static void expr_error
(double *final_val __maybe_unused
,
61 struct expr_parse_ctx
*ctx __maybe_unused
,
62 bool compute_ids __maybe_unused
,
63 void *scanner __maybe_unused
,
70 * During compute ids, the special "bottom" value uses NAN to represent the set
71 * of all values. NAN is selected as it isn't a useful constant value.
75 /* During computing ids, does val represent a constant (non-BOTTOM) value? */
76 static bool is_const
(double val
)
81 static struct ids union_expr
(struct ids ids1
, struct ids ids2
)
85 .ids
= ids__union
(ids1.ids
, ids2.ids
),
90 static struct ids handle_id
(struct expr_parse_ctx
*ctx
, char *id
,
91 bool compute_ids
, bool source_count
)
97 * Compute the event's value from ID. If the ID isn't known then
98 * it isn't used to compute the formula so set to NAN.
100 struct expr_id_data
*data
;
103 if
(expr__resolve_id
(ctx
, id
, &data
) == 0) {
104 result.val
= source_count
105 ? expr_id_data__source_count
(data
)
106 : expr_id_data__value
(data
);
112 * Set the value to BOTTOM to show that any value is possible
113 * when the event is computed. Create a set of just the ID.
116 result.ids
= ids__new
();
117 if
(!result.ids || ids__insert
(result.ids
, id
)) {
118 pr_err
("Error creating IDs for '%s'", id
);
126 * If we're not computing ids or $1 and $3 are constants, compute the new
127 * constant value using OP. Its invariant that there are no ids. If computing
128 * ids for non-constants union the set of IDs that must be computed.
130 #define BINARY_OP(RESULT, OP, LHS, RHS) \
131 if
(!compute_ids ||
(is_const
(LHS.val
) && is_const
(RHS.val
))) { \
132 assert
(LHS.ids
== NULL
); \
133 assert
(RHS.ids
== NULL
); \
134 if
(isnan
(LHS.val
) || isnan
(RHS.val
)) { \
137 RESULT.val
= LHS.val OP RHS.val
; \
141 RESULT
= union_expr
(LHS
, RHS
); \
150 ctx
->ids
= ids__union
($1.ids
, ctx
->ids
);
157 if_expr: expr IF expr ELSE if_expr
159 if
(fpclassify
($3.val
) == FP_ZERO
) {
161 * The IF expression evaluated to 0 so treat as false, take the
162 * ELSE and discard everything else.
168 } else if
(!compute_ids || is_const
($3.val
)) {
170 * If ids aren't computed then treat the expression as true. If
171 * ids are being computed and the IF expr is a non-zero
172 * constant, then also evaluate the true case.
178 } else if
($1.val
== $5.val
) {
180 * LHS == RHS, so both are an identical constant. No need to
181 * evaluate any events.
190 * Value is either the LHS or RHS and we need the IF expression
193 $$
= union_expr
($1, union_expr
($3, $5));
204 | ID
{ $$
= handle_id
(ctx
, $1, compute_ids
, /*source_count=*/false
); }
205 | SOURCE_COUNT
'(' ID
')' { $$
= handle_id
(ctx
, $3, compute_ids
, /*source_count=*/true
); }
206 | HAS_EVENT
'(' ID
')'
208 $$.val
= expr__has_event
(ctx
, compute_ids
, $3);
212 | STRCMP_CPUID_STR
'(' ID
')'
214 $$.val
= expr__strcmp_cpuid_str
(ctx
, compute_ids
, $3);
220 if
(is_const
($1.val
) && is_const
($3.val
)) {
221 assert
($1.ids
== NULL
);
222 assert
($3.ids
== NULL
);
224 $$.val
= (fpclassify
($1.val
) == FP_ZERO
&& fpclassify
($3.val
) == FP_ZERO
) ?
0 : 1;
225 } else if
(is_const
($1.val
)) {
226 assert
($1.ids
== NULL
);
227 if
(fpclassify
($1.val
) == FP_ZERO
) {
234 } else if
(is_const
($3.val
)) {
235 assert
($3.ids
== NULL
);
236 if
(fpclassify
($3.val
) == FP_ZERO
) {
244 $$
= union_expr
($1, $3);
249 if
(is_const
($1.val
) && is_const
($3.val
)) {
250 assert
($1.ids
== NULL
);
251 assert
($3.ids
== NULL
);
252 $$.val
= (fpclassify
($1.val
) != FP_ZERO
&& fpclassify
($3.val
) != FP_ZERO
) ?
1 : 0;
254 } else if
(is_const
($1.val
)) {
255 assert
($1.ids
== NULL
);
256 if
(fpclassify
($1.val
) != FP_ZERO
) {
263 } else if
(is_const
($3.val
)) {
264 assert
($3.ids
== NULL
);
265 if
(fpclassify
($3.val
) != FP_ZERO
) {
273 $$
= union_expr
($1, $3);
278 if
(is_const
($1.val
) && is_const
($3.val
)) {
279 assert
($1.ids
== NULL
);
280 assert
($3.ids
== NULL
);
281 $$.val
= (fpclassify
($1.val
) == FP_ZERO
) != (fpclassify
($3.val
) == FP_ZERO
) ?
1 : 0;
284 $$
= union_expr
($1, $3);
287 | expr
'<' expr
{ BINARY_OP
($$
, <, $1, $3); }
288 | expr
'>' expr
{ BINARY_OP
($$
, >, $1, $3); }
289 | expr
'+' expr
{ BINARY_OP
($$
, +, $1, $3); }
290 | expr
'-' expr
{ BINARY_OP
($$
, -, $1, $3); }
291 | expr
'*' expr
{ BINARY_OP
($$
, *, $1, $3); }
294 if
(fpclassify
($3.val
) == FP_ZERO
) {
295 pr_debug
("division by zero\n");
296 assert
($3.ids
== NULL
);
301 } else if
(!compute_ids ||
(is_const
($1.val
) && is_const
($3.val
))) {
302 assert
($1.ids
== NULL
);
303 assert
($3.ids
== NULL
);
304 $$.val
= $1.val
/ $3.val
;
307 /* LHS and/or RHS need computing from event IDs so union. */
308 $$
= union_expr
($1, $3);
313 if
(fpclassify
($3.val
) == FP_ZERO
) {
314 pr_debug
("division by zero\n");
316 } else if
(!compute_ids ||
(is_const
($1.val
) && is_const
($3.val
))) {
317 assert
($1.ids
== NULL
);
318 assert
($3.ids
== NULL
);
319 $$.val
= (long)$1.val %
(long)$3.val
;
322 /* LHS and/or RHS need computing from event IDs so union. */
323 $$
= union_expr
($1, $3);
326 | D_RATIO
'(' expr
',' expr
')'
328 if
(fpclassify
($5.val
) == FP_ZERO
) {
330 * Division by constant zero always yields zero and no events
333 assert
($5.ids
== NULL
);
337 } else if
(!compute_ids ||
(is_const
($3.val
) && is_const
($5.val
))) {
338 assert
($3.ids
== NULL
);
339 assert
($5.ids
== NULL
);
340 $$.val
= $3.val
/ $5.val
;
343 /* LHS and/or RHS need computing from event IDs so union. */
344 $$
= union_expr
($3, $5);
356 | MIN
'(' expr
',' expr
')'
359 $$.val
= $3.val
< $5.val ?
$3.val
: $5.val
;
362 $$
= union_expr
($3, $5);
365 | MAX
'(' expr
',' expr
')'
368 $$.val
= $3.val
> $5.val ?
$3.val
: $5.val
;
371 $$
= union_expr
($3, $5);