expr.y 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. /* Simple expression parser */
  2. %{
  3. #ifndef NDEBUG
  4. #define YYDEBUG 1
  5. #endif
  6. #include <assert.h>
  7. #include <math.h>
  8. #include <stdlib.h>
  9. #include "util/debug.h"
  10. #define IN_EXPR_Y 1
  11. #include "expr.h"
  12. #include "expr-bison.h"
  13. int expr_lex(YYSTYPE * yylval_param , void *yyscanner);
  14. %}
  15. %define api.pure full
  16. %parse-param { double *final_val }
  17. %parse-param { struct expr_parse_ctx *ctx }
  18. %parse-param { bool compute_ids }
  19. %parse-param {void *scanner}
  20. %lex-param {void* scanner}
  21. %union {
  22. double num;
  23. char *str;
  24. struct ids {
  25. /*
  26. * When creating ids, holds the working set of event ids. NULL
  27. * implies the set is empty.
  28. */
  29. struct hashmap *ids;
  30. /*
  31. * The metric value. When not creating ids this is the value
  32. * read from a counter, a constant or some computed value. When
  33. * creating ids the value is either a constant or BOTTOM. NAN is
  34. * used as the special BOTTOM value, representing a "set of all
  35. * values" case.
  36. */
  37. double val;
  38. } ids;
  39. }
  40. %token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT STRCMP_CPUID_STR EXPR_ERROR
  41. %left MIN MAX IF
  42. %left '|'
  43. %left '^'
  44. %left '&'
  45. %left '<' '>'
  46. %left '-' '+'
  47. %left '*' '/' '%'
  48. %left NEG NOT
  49. %type <num> NUMBER LITERAL
  50. %type <str> ID
  51. %destructor { free ($$); } <str>
  52. %type <ids> expr if_expr
  53. %destructor { ids__free($$.ids); } <ids>
  54. %{
  55. static void expr_error(double *final_val __maybe_unused,
  56. struct expr_parse_ctx *ctx __maybe_unused,
  57. bool compute_ids __maybe_unused,
  58. void *scanner __maybe_unused,
  59. const char *s)
  60. {
  61. pr_debug("%s\n", s);
  62. }
  63. /*
  64. * During compute ids, the special "bottom" value uses NAN to represent the set
  65. * of all values. NAN is selected as it isn't a useful constant value.
  66. */
  67. #define BOTTOM NAN
  68. /* During computing ids, does val represent a constant (non-BOTTOM) value? */
  69. static bool is_const(double val)
  70. {
  71. return isfinite(val);
  72. }
  73. static struct ids union_expr(struct ids ids1, struct ids ids2)
  74. {
  75. struct ids result = {
  76. .val = BOTTOM,
  77. .ids = ids__union(ids1.ids, ids2.ids),
  78. };
  79. return result;
  80. }
  81. static struct ids handle_id(struct expr_parse_ctx *ctx, char *id,
  82. bool compute_ids, bool source_count)
  83. {
  84. struct ids result;
  85. if (!compute_ids) {
  86. /*
  87. * Compute the event's value from ID. If the ID isn't known then
  88. * it isn't used to compute the formula so set to NAN.
  89. */
  90. struct expr_id_data *data;
  91. result.val = NAN;
  92. if (expr__resolve_id(ctx, id, &data) == 0) {
  93. result.val = source_count
  94. ? expr_id_data__source_count(data)
  95. : expr_id_data__value(data);
  96. }
  97. result.ids = NULL;
  98. free(id);
  99. } else {
  100. /*
  101. * Set the value to BOTTOM to show that any value is possible
  102. * when the event is computed. Create a set of just the ID.
  103. */
  104. result.val = BOTTOM;
  105. result.ids = ids__new();
  106. if (!result.ids || ids__insert(result.ids, id)) {
  107. pr_err("Error creating IDs for '%s'", id);
  108. free(id);
  109. }
  110. }
  111. return result;
  112. }
  113. /*
  114. * If we're not computing ids or $1 and $3 are constants, compute the new
  115. * constant value using OP. Its invariant that there are no ids. If computing
  116. * ids for non-constants union the set of IDs that must be computed.
  117. */
  118. #define BINARY_OP(RESULT, OP, LHS, RHS) \
  119. if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \
  120. assert(LHS.ids == NULL); \
  121. assert(RHS.ids == NULL); \
  122. if (isnan(LHS.val) || isnan(RHS.val)) { \
  123. RESULT.val = NAN; \
  124. } else { \
  125. RESULT.val = LHS.val OP RHS.val; \
  126. } \
  127. RESULT.ids = NULL; \
  128. } else { \
  129. RESULT = union_expr(LHS, RHS); \
  130. }
  131. %}
  132. %%
  133. start: if_expr
  134. {
  135. if (compute_ids)
  136. ctx->ids = ids__union($1.ids, ctx->ids);
  137. if (final_val)
  138. *final_val = $1.val;
  139. }
  140. ;
  141. if_expr: expr IF expr ELSE if_expr
  142. {
  143. if (fpclassify($3.val) == FP_ZERO) {
  144. /*
  145. * The IF expression evaluated to 0 so treat as false, take the
  146. * ELSE and discard everything else.
  147. */
  148. $$.val = $5.val;
  149. $$.ids = $5.ids;
  150. ids__free($1.ids);
  151. ids__free($3.ids);
  152. } else if (!compute_ids || is_const($3.val)) {
  153. /*
  154. * If ids aren't computed then treat the expression as true. If
  155. * ids are being computed and the IF expr is a non-zero
  156. * constant, then also evaluate the true case.
  157. */
  158. $$.val = $1.val;
  159. $$.ids = $1.ids;
  160. ids__free($3.ids);
  161. ids__free($5.ids);
  162. } else if ($1.val == $5.val) {
  163. /*
  164. * LHS == RHS, so both are an identical constant. No need to
  165. * evaluate any events.
  166. */
  167. $$.val = $1.val;
  168. $$.ids = NULL;
  169. ids__free($1.ids);
  170. ids__free($3.ids);
  171. ids__free($5.ids);
  172. } else {
  173. /*
  174. * Value is either the LHS or RHS and we need the IF expression
  175. * to compute it.
  176. */
  177. $$ = union_expr($1, union_expr($3, $5));
  178. }
  179. }
  180. | expr
  181. ;
  182. expr: NUMBER
  183. {
  184. $$.val = $1;
  185. $$.ids = NULL;
  186. }
  187. | ID { $$ = handle_id(ctx, $1, compute_ids, /*source_count=*/false); }
  188. | SOURCE_COUNT '(' ID ')' { $$ = handle_id(ctx, $3, compute_ids, /*source_count=*/true); }
  189. | HAS_EVENT '(' ID ')'
  190. {
  191. $$.val = expr__has_event(ctx, compute_ids, $3);
  192. $$.ids = NULL;
  193. free($3);
  194. }
  195. | STRCMP_CPUID_STR '(' ID ')'
  196. {
  197. $$.val = expr__strcmp_cpuid_str(ctx, compute_ids, $3);
  198. $$.ids = NULL;
  199. free($3);
  200. }
  201. | expr '|' expr
  202. {
  203. if (is_const($1.val) && is_const($3.val)) {
  204. assert($1.ids == NULL);
  205. assert($3.ids == NULL);
  206. $$.ids = NULL;
  207. $$.val = (fpclassify($1.val) == FP_ZERO && fpclassify($3.val) == FP_ZERO) ? 0 : 1;
  208. } else if (is_const($1.val)) {
  209. assert($1.ids == NULL);
  210. if (fpclassify($1.val) == FP_ZERO) {
  211. $$ = $3;
  212. } else {
  213. $$.val = 1;
  214. $$.ids = NULL;
  215. ids__free($3.ids);
  216. }
  217. } else if (is_const($3.val)) {
  218. assert($3.ids == NULL);
  219. if (fpclassify($3.val) == FP_ZERO) {
  220. $$ = $1;
  221. } else {
  222. $$.val = 1;
  223. $$.ids = NULL;
  224. ids__free($1.ids);
  225. }
  226. } else {
  227. $$ = union_expr($1, $3);
  228. }
  229. }
  230. | expr '&' expr
  231. {
  232. if (is_const($1.val) && is_const($3.val)) {
  233. assert($1.ids == NULL);
  234. assert($3.ids == NULL);
  235. $$.val = (fpclassify($1.val) != FP_ZERO && fpclassify($3.val) != FP_ZERO) ? 1 : 0;
  236. $$.ids = NULL;
  237. } else if (is_const($1.val)) {
  238. assert($1.ids == NULL);
  239. if (fpclassify($1.val) != FP_ZERO) {
  240. $$ = $3;
  241. } else {
  242. $$.val = 0;
  243. $$.ids = NULL;
  244. ids__free($3.ids);
  245. }
  246. } else if (is_const($3.val)) {
  247. assert($3.ids == NULL);
  248. if (fpclassify($3.val) != FP_ZERO) {
  249. $$ = $1;
  250. } else {
  251. $$.val = 0;
  252. $$.ids = NULL;
  253. ids__free($1.ids);
  254. }
  255. } else {
  256. $$ = union_expr($1, $3);
  257. }
  258. }
  259. | expr '^' expr
  260. {
  261. if (is_const($1.val) && is_const($3.val)) {
  262. assert($1.ids == NULL);
  263. assert($3.ids == NULL);
  264. $$.val = (fpclassify($1.val) == FP_ZERO) != (fpclassify($3.val) == FP_ZERO) ? 1 : 0;
  265. $$.ids = NULL;
  266. } else {
  267. $$ = union_expr($1, $3);
  268. }
  269. }
  270. | expr '<' expr { BINARY_OP($$, <, $1, $3); }
  271. | expr '>' expr { BINARY_OP($$, >, $1, $3); }
  272. | expr '+' expr { BINARY_OP($$, +, $1, $3); }
  273. | expr '-' expr { BINARY_OP($$, -, $1, $3); }
  274. | expr '*' expr { BINARY_OP($$, *, $1, $3); }
  275. | expr '/' expr
  276. {
  277. if (fpclassify($3.val) == FP_ZERO) {
  278. pr_debug("division by zero\n");
  279. assert($3.ids == NULL);
  280. if (compute_ids)
  281. ids__free($1.ids);
  282. $$.val = NAN;
  283. $$.ids = NULL;
  284. } else if (!compute_ids || (is_const($1.val) && is_const($3.val))) {
  285. assert($1.ids == NULL);
  286. assert($3.ids == NULL);
  287. $$.val = $1.val / $3.val;
  288. $$.ids = NULL;
  289. } else {
  290. /* LHS and/or RHS need computing from event IDs so union. */
  291. $$ = union_expr($1, $3);
  292. }
  293. }
  294. | expr '%' expr
  295. {
  296. if (fpclassify($3.val) == FP_ZERO) {
  297. pr_debug("division by zero\n");
  298. YYABORT;
  299. } else if (!compute_ids || (is_const($1.val) && is_const($3.val))) {
  300. assert($1.ids == NULL);
  301. assert($3.ids == NULL);
  302. $$.val = (long)$1.val % (long)$3.val;
  303. $$.ids = NULL;
  304. } else {
  305. /* LHS and/or RHS need computing from event IDs so union. */
  306. $$ = union_expr($1, $3);
  307. }
  308. }
  309. | D_RATIO '(' expr ',' expr ')'
  310. {
  311. if (fpclassify($5.val) == FP_ZERO) {
  312. /*
  313. * Division by constant zero always yields zero and no events
  314. * are necessary.
  315. */
  316. assert($5.ids == NULL);
  317. $$.val = 0.0;
  318. $$.ids = NULL;
  319. ids__free($3.ids);
  320. } else if (!compute_ids || (is_const($3.val) && is_const($5.val))) {
  321. assert($3.ids == NULL);
  322. assert($5.ids == NULL);
  323. $$.val = $3.val / $5.val;
  324. $$.ids = NULL;
  325. } else {
  326. /* LHS and/or RHS need computing from event IDs so union. */
  327. $$ = union_expr($3, $5);
  328. }
  329. }
  330. | '-' expr %prec NEG
  331. {
  332. $$.val = -$2.val;
  333. $$.ids = $2.ids;
  334. }
  335. | '(' if_expr ')'
  336. {
  337. $$ = $2;
  338. }
  339. | MIN '(' expr ',' expr ')'
  340. {
  341. if (!compute_ids) {
  342. $$.val = $3.val < $5.val ? $3.val : $5.val;
  343. $$.ids = NULL;
  344. } else {
  345. $$ = union_expr($3, $5);
  346. }
  347. }
  348. | MAX '(' expr ',' expr ')'
  349. {
  350. if (!compute_ids) {
  351. $$.val = $3.val > $5.val ? $3.val : $5.val;
  352. $$.ids = NULL;
  353. } else {
  354. $$ = union_expr($3, $5);
  355. }
  356. }
  357. | LITERAL
  358. {
  359. $$.val = $1;
  360. $$.ids = NULL;
  361. }
  362. ;
  363. %%