*/
        term = list_entry(term->list.next, struct parse_events_term, list);
        TEST_ASSERT_VAL("wrong type term",
-                       term->type_term == PARSE_EVENTS__TERM_TYPE_USER);
+                       term->type_term == PARSE_EVENTS__TERM_TYPE_RAW);
        TEST_ASSERT_VAL("wrong type val",
-                       term->type_val == PARSE_EVENTS__TERM_TYPE_NUM);
-       TEST_ASSERT_VAL("wrong val", term->val.num == 1);
-       TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "read"));
+                       term->type_val == PARSE_EVENTS__TERM_TYPE_STR);
+       TEST_ASSERT_VAL("wrong val", !strcmp(term->val.str, "read"));
+       TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "raw"));
 
        /*
         * r0xead
         */
        term = list_entry(term->list.next, struct parse_events_term, list);
        TEST_ASSERT_VAL("wrong type term",
-                       term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG);
+                       term->type_term == PARSE_EVENTS__TERM_TYPE_RAW);
        TEST_ASSERT_VAL("wrong type val",
-                       term->type_val == PARSE_EVENTS__TERM_TYPE_NUM);
-       TEST_ASSERT_VAL("wrong val", term->val.num == 0xead);
-       TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "config"));
+                       term->type_val == PARSE_EVENTS__TERM_TYPE_STR);
+       TEST_ASSERT_VAL("wrong val", !strcmp(term->val.str, "r0xead"));
+       TEST_ASSERT_VAL("wrong config", !strcmp(term->config, "raw"));
        return TEST_OK;
 }
 
                return -ENOMEM;
 
        parse_events_error__init(&err);
-       perf_pmu__test_parse_init();
        ret = __parse_events(evlist, str, &err, &perf_pmu__fake, /*warn_if_reordered=*/true);
        if (ret) {
                pr_debug("failed to parse event '%s', err %d, str '%s'\n",
 
        INIT_LIST_HEAD(&terms);
 
-       /*
-        * The perf_pmu__test_parse_init prepares perf_pmu_events_list
-        * which gets freed in parse_events_terms.
-        */
-       if (perf_pmu__test_parse_init())
-               return -1;
-
        ret = parse_events_terms(&terms, t->str);
        if (ret) {
                pr_debug("failed to parse terms '%s', err %d\n",
 
        for (cur = strchr(dup, '@') ; cur; cur = strchr(++cur, '@'))
                *cur = '/';
 
-       if (fake_pmu) {
-               /*
-                * Every call to __parse_events will try to initialize the PMU
-                * state from sysfs and then clean it up at the end. Reset the
-                * PMU events to the test state so that we don't pick up
-                * erroneous prefixes and suffixes.
-                */
-               perf_pmu__test_parse_init();
-       }
        ret = __parse_events(evlist, dup, error, fake_pmu, /*warn_if_reordered=*/true);
        free(dup);
 
 
 
 #define MAX_NAME_LEN 100
 
-struct perf_pmu_event_symbol {
-       char    *symbol;
-       enum perf_pmu_event_symbol_type type;
-};
-
 #ifdef PARSER_DEBUG
 extern int parse_events_debug;
 #endif
                                         const char *str, char *pmu_name,
                                         struct list_head *list);
 
-static struct perf_pmu_event_symbol *perf_pmu_events_list;
-/*
- * The variable indicates the number of supported pmu event symbols.
- * 0 means not initialized and ready to init
- * -1 means failed to init, don't try anymore
- * >0 is the number of supported pmu event symbols
- */
-static int perf_pmu_events_list_num;
-
 struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
        [PERF_COUNT_HW_CPU_CYCLES] = {
                .symbol = "cpu-cycles",
        return get_config_str(head_terms, PARSE_EVENTS__TERM_TYPE_NAME);
 }
 
+/**
+ * fix_raw - For each raw term see if there is an event (aka alias) in pmu that
+ *           matches the raw's string value. If the string value matches an
+ *           event then change the term to be an event, if not then change it to
+ *           be a config term. For example, "read" may be an event of the PMU or
+ *           a raw hex encoding of 0xead. The fix-up is done late so the PMU of
+ *           the event can be determined and we don't need to scan all PMUs
+ *           ahead-of-time.
+ * @config_terms: the list of terms that may contain a raw term.
+ * @pmu: the PMU to scan for events from.
+ */
+static void fix_raw(struct list_head *config_terms, struct perf_pmu *pmu)
+{
+       struct parse_events_term *term;
+
+       list_for_each_entry(term, config_terms, list) {
+               struct perf_pmu_alias *alias;
+               bool matched = false;
+
+               if (term->type_term != PARSE_EVENTS__TERM_TYPE_RAW)
+                       continue;
+
+               list_for_each_entry(alias, &pmu->aliases, list) {
+                       if (!strcmp(alias->name, term->val.str)) {
+                               free(term->config);
+                               term->config = term->val.str;
+                               term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
+                               term->type_term = PARSE_EVENTS__TERM_TYPE_USER;
+                               term->val.num = 1;
+                               term->no_value = true;
+                               matched = true;
+                               break;
+                       }
+               }
+               if (!matched) {
+                       u64 num;
+
+                       free(term->config);
+                       term->config = strdup("config");
+                       errno = 0;
+                       num = strtoull(term->val.str + 1, NULL, 16);
+                       assert(errno == 0);
+                       free(term->val.str);
+                       term->type_val = PARSE_EVENTS__TERM_TYPE_NUM;
+                       term->type_term = PARSE_EVENTS__TERM_TYPE_CONFIG;
+                       term->val.num = num;
+                       term->no_value = false;
+               }
+       }
+}
+
 static struct evsel *
 __add_event(struct list_head *list, int *idx,
            struct perf_event_attr *attr,
        return 0;
 }
 
-static int parse_aliases(char *str, const char *const names[][EVSEL__MAX_ALIASES], int size)
+/**
+ * parse_aliases - search names for entries beginning or equalling str ignoring
+ *                 case. If mutliple entries in names match str then the longest
+ *                 is chosen.
+ * @str: The needle to look for.
+ * @names: The haystack to search.
+ * @size: The size of the haystack.
+ * @longest: Out argument giving the length of the matching entry.
+ */
+static int parse_aliases(const char *str, const char *const names[][EVSEL__MAX_ALIASES], int size,
+                        int *longest)
 {
-       int i, j;
-       int n, longest = -1;
+       *longest = -1;
+       for (int i = 0; i < size; i++) {
+               for (int j = 0; j < EVSEL__MAX_ALIASES && names[i][j]; j++) {
+                       int n = strlen(names[i][j]);
 
-       for (i = 0; i < size; i++) {
-               for (j = 0; j < EVSEL__MAX_ALIASES && names[i][j]; j++) {
-                       n = strlen(names[i][j]);
-                       if (n > longest && !strncasecmp(str, names[i][j], n))
-                               longest = n;
+                       if (n > *longest && !strncasecmp(str, names[i][j], n))
+                               *longest = n;
                }
-               if (longest > 0)
+               if (*longest > 0)
                        return i;
        }
 
                       struct parse_events_error *err,
                       config_term_func_t config_term);
 
-int parse_events_add_cache(struct list_head *list, int *idx,
-                          char *type, char *op_result1, char *op_result2,
+int parse_events_add_cache(struct list_head *list, int *idx, const char *name,
                           struct parse_events_error *err,
                           struct list_head *head_config,
                           struct parse_events_state *parse_state)
 {
        struct perf_event_attr attr;
        LIST_HEAD(config_terms);
-       char name[MAX_NAME_LEN];
        const char *config_name, *metric_id;
        int cache_type = -1, cache_op = -1, cache_result = -1;
-       char *op_result[2] = { op_result1, op_result2 };
-       int i, n, ret;
+       int ret, len;
+       const char *name_end = &name[strlen(name) + 1];
        bool hybrid;
+       const char *str = name;
 
        /*
-        * No fallback - if we cannot get a clear cache type
-        * then bail out:
+        * Search str for the legacy cache event name composed of 1, 2 or 3
+        * hyphen separated sections. The first section is the cache type while
+        * the others are the optional op and optional result. To make life hard
+        * the names in the table also contain hyphens and the longest name
+        * should always be selected.
         */
-       cache_type = parse_aliases(type, evsel__hw_cache, PERF_COUNT_HW_CACHE_MAX);
+       cache_type = parse_aliases(str, evsel__hw_cache, PERF_COUNT_HW_CACHE_MAX, &len);
        if (cache_type == -1)
                return -EINVAL;
+       str += len + 1;
 
        config_name = get_config_name(head_config);
-       n = snprintf(name, MAX_NAME_LEN, "%s", type);
-
-       for (i = 0; (i < 2) && (op_result[i]); i++) {
-               char *str = op_result[i];
-
-               n += snprintf(name + n, MAX_NAME_LEN - n, "-%s", str);
-
-               if (cache_op == -1) {
+       if (str < name_end) {
+               cache_op = parse_aliases(str, evsel__hw_cache_op,
+                                       PERF_COUNT_HW_CACHE_OP_MAX, &len);
+               if (cache_op >= 0) {
+                       if (!evsel__is_cache_op_valid(cache_type, cache_op))
+                               return -EINVAL;
+                       str += len + 1;
+               } else {
+                       cache_result = parse_aliases(str, evsel__hw_cache_result,
+                                               PERF_COUNT_HW_CACHE_RESULT_MAX, &len);
+                       if (cache_result >= 0)
+                               str += len + 1;
+               }
+       }
+       if (str < name_end) {
+               if (cache_op < 0) {
                        cache_op = parse_aliases(str, evsel__hw_cache_op,
-                                                PERF_COUNT_HW_CACHE_OP_MAX);
+                                               PERF_COUNT_HW_CACHE_OP_MAX, &len);
                        if (cache_op >= 0) {
                                if (!evsel__is_cache_op_valid(cache_type, cache_op))
                                        return -EINVAL;
-                               continue;
                        }
-               }
-
-               if (cache_result == -1) {
+               } else if (cache_result < 0) {
                        cache_result = parse_aliases(str, evsel__hw_cache_result,
-                                                    PERF_COUNT_HW_CACHE_RESULT_MAX);
-                       if (cache_result >= 0)
-                               continue;
+                                               PERF_COUNT_HW_CACHE_RESULT_MAX, &len);
                }
        }
 
        [PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT]            = "aux-output",
        [PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE]       = "aux-sample-size",
        [PARSE_EVENTS__TERM_TYPE_METRIC_ID]             = "metric-id",
+       [PARSE_EVENTS__TERM_TYPE_RAW]                   = "raw",
 };
 
 static bool config_term_shrinked;
        case PARSE_EVENTS__TERM_TYPE_METRIC_ID:
                CHECK_TYPE_VAL(STR);
                break;
+       case PARSE_EVENTS__TERM_TYPE_RAW:
+               CHECK_TYPE_VAL(STR);
+               break;
        case PARSE_EVENTS__TERM_TYPE_MAX_STACK:
                CHECK_TYPE_VAL(NUM);
                break;
                        parse_events_error__handle(err, 0, err_str, NULL);
                return -EINVAL;
        }
+       if (head_config)
+               fix_raw(head_config, pmu);
 
        if (pmu->default_config) {
                memcpy(&attr, pmu->default_config,
        return 0;
 }
 
-static int
-comp_pmu(const void *p1, const void *p2)
-{
-       struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1;
-       struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2;
-
-       return strcasecmp(pmu1->symbol, pmu2->symbol);
-}
-
-static void perf_pmu__parse_cleanup(void)
-{
-       if (perf_pmu_events_list_num > 0) {
-               struct perf_pmu_event_symbol *p;
-               int i;
-
-               for (i = 0; i < perf_pmu_events_list_num; i++) {
-                       p = perf_pmu_events_list + i;
-                       zfree(&p->symbol);
-               }
-               zfree(&perf_pmu_events_list);
-               perf_pmu_events_list_num = 0;
-       }
-}
-
-#define SET_SYMBOL(str, stype)         \
-do {                                   \
-       p->symbol = str;                \
-       if (!p->symbol)                 \
-               goto err;               \
-       p->type = stype;                \
-} while (0)
-
-/*
- * Read the pmu events list from sysfs
- * Save it into perf_pmu_events_list
- */
-static void perf_pmu__parse_init(void)
-{
-
-       struct perf_pmu *pmu = NULL;
-       struct perf_pmu_alias *alias;
-       int len = 0;
-
-       pmu = NULL;
-       while ((pmu = perf_pmu__scan(pmu)) != NULL) {
-               list_for_each_entry(alias, &pmu->aliases, list) {
-                       char *tmp = strchr(alias->name, '-');
-
-                       if (tmp) {
-                               char *tmp2 = NULL;
-
-                               tmp2 = strchr(tmp + 1, '-');
-                               len++;
-                               if (tmp2)
-                                       len++;
-                       }
-
-                       len++;
-               }
-       }
-
-       if (len == 0) {
-               perf_pmu_events_list_num = -1;
-               return;
-       }
-       perf_pmu_events_list = malloc(sizeof(struct perf_pmu_event_symbol) * len);
-       if (!perf_pmu_events_list)
-               return;
-       perf_pmu_events_list_num = len;
-
-       len = 0;
-       pmu = NULL;
-       while ((pmu = perf_pmu__scan(pmu)) != NULL) {
-               list_for_each_entry(alias, &pmu->aliases, list) {
-                       struct perf_pmu_event_symbol *p = perf_pmu_events_list + len;
-                       char *tmp = strchr(alias->name, '-');
-                       char *tmp2 = NULL;
-
-                       if (tmp)
-                               tmp2 = strchr(tmp + 1, '-');
-                       if (tmp2) {
-                               SET_SYMBOL(strndup(alias->name, tmp - alias->name),
-                                               PMU_EVENT_SYMBOL_PREFIX);
-                               p++;
-                               tmp++;
-                               SET_SYMBOL(strndup(tmp, tmp2 - tmp), PMU_EVENT_SYMBOL_SUFFIX);
-                               p++;
-                               SET_SYMBOL(strdup(++tmp2), PMU_EVENT_SYMBOL_SUFFIX2);
-                               len += 3;
-                       } else if (tmp) {
-                               SET_SYMBOL(strndup(alias->name, tmp - alias->name),
-                                               PMU_EVENT_SYMBOL_PREFIX);
-                               p++;
-                               SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX);
-                               len += 2;
-                       } else {
-                               SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL);
-                               len++;
-                       }
-               }
-       }
-       qsort(perf_pmu_events_list, len,
-               sizeof(struct perf_pmu_event_symbol), comp_pmu);
-
-       return;
-err:
-       perf_pmu__parse_cleanup();
-}
-
-/*
- * This function injects special term in
- * perf_pmu_events_list so the test code
- * can check on this functionality.
- */
-int perf_pmu__test_parse_init(void)
-{
-       struct perf_pmu_event_symbol *list, *tmp, symbols[] = {
-               {(char *)"read", PMU_EVENT_SYMBOL},
-               {(char *)"event", PMU_EVENT_SYMBOL_PREFIX},
-               {(char *)"two", PMU_EVENT_SYMBOL_SUFFIX},
-               {(char *)"hyphen", PMU_EVENT_SYMBOL_SUFFIX},
-               {(char *)"hyph", PMU_EVENT_SYMBOL_SUFFIX2},
-       };
-       unsigned long i, j;
-
-       tmp = list = malloc(sizeof(*list) * ARRAY_SIZE(symbols));
-       if (!list)
-               return -ENOMEM;
-
-       for (i = 0; i < ARRAY_SIZE(symbols); i++, tmp++) {
-               tmp->type = symbols[i].type;
-               tmp->symbol = strdup(symbols[i].symbol);
-               if (!tmp->symbol)
-                       goto err_free;
-       }
-
-       perf_pmu_events_list = list;
-       perf_pmu_events_list_num = ARRAY_SIZE(symbols);
-
-       qsort(perf_pmu_events_list, ARRAY_SIZE(symbols),
-             sizeof(struct perf_pmu_event_symbol), comp_pmu);
-       return 0;
-
-err_free:
-       for (j = 0, tmp = list; j < i; j++, tmp++)
-               zfree(&tmp->symbol);
-       free(list);
-       return -ENOMEM;
-}
-
-enum perf_pmu_event_symbol_type
-perf_pmu__parse_check(const char *name)
-{
-       struct perf_pmu_event_symbol p, *r;
-
-       /* scan kernel pmu events from sysfs if needed */
-       if (perf_pmu_events_list_num == 0)
-               perf_pmu__parse_init();
-       /*
-        * name "cpu" could be prefix of cpu-cycles or cpu// events.
-        * cpu-cycles has been handled by hardcode.
-        * So it must be cpu// events, not kernel pmu event.
-        */
-       if ((perf_pmu_events_list_num <= 0) || !strcmp(name, "cpu"))
-               return PMU_EVENT_SYMBOL_ERR;
-
-       p.symbol = strdup(name);
-       r = bsearch(&p, perf_pmu_events_list,
-                       (size_t) perf_pmu_events_list_num,
-                       sizeof(struct perf_pmu_event_symbol), comp_pmu);
-       zfree(&p.symbol);
-       return r ? r->type : PMU_EVENT_SYMBOL_ERR;
-}
-
 static int parse_events__scanner(const char *str,
                                 struct parse_events_state *parse_state)
 {
        int ret;
 
        ret = parse_events__scanner(str, &parse_state);
-       perf_pmu__parse_cleanup();
 
        if (!ret) {
                list_splice(parse_state.terms, terms);
        int ret;
 
        ret = parse_events__scanner(str, &ps);
-       perf_pmu__parse_cleanup();
 
        if (!ret) {
                if (!list_empty(&ps.list)) {
        int ret;
 
        ret = parse_events__scanner(str, &parse_state);
-       perf_pmu__parse_cleanup();
 
        if (!ret && list_empty(&parse_state.list)) {
                WARN_ONCE(true, "WARNING: event parser found nothing\n");
 
 int parse_filter(const struct option *opt, const char *str, int unset);
 int exclude_perf(const struct option *opt, const char *arg, int unset);
 
-enum perf_pmu_event_symbol_type {
-       PMU_EVENT_SYMBOL_ERR,           /* not a PMU EVENT */
-       PMU_EVENT_SYMBOL,               /* normal style PMU event */
-       PMU_EVENT_SYMBOL_PREFIX,        /* prefix of pre-suf style event */
-       PMU_EVENT_SYMBOL_SUFFIX,        /* suffix of pre-suf style event */
-       PMU_EVENT_SYMBOL_SUFFIX2,       /* suffix of pre-suf2 style event */
-};
-
 enum {
        PARSE_EVENTS__TERM_TYPE_NUM,
        PARSE_EVENTS__TERM_TYPE_STR,
        PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT,
        PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE,
        PARSE_EVENTS__TERM_TYPE_METRIC_ID,
+       PARSE_EVENTS__TERM_TYPE_RAW,
        __PARSE_EVENTS__TERM_TYPE_NR,
 };
 
 int parse_events_add_tool(struct parse_events_state *parse_state,
                          struct list_head *list,
                          int tool_event);
-int parse_events_add_cache(struct list_head *list, int *idx,
-                          char *type, char *op_result1, char *op_result2,
+int parse_events_add_cache(struct list_head *list, int *idx, const char *name,
                           struct parse_events_error *error,
                           struct list_head *head_config,
                           struct parse_events_state *parse_state);
 int parse_events_copy_term_list(struct list_head *old,
                                 struct list_head **new);
 
-enum perf_pmu_event_symbol_type
-perf_pmu__parse_check(const char *name);
 void parse_events__set_leader(char *name, struct list_head *list);
 void parse_events_update_lists(struct list_head *list_event,
                               struct list_head *list_all);
 }
 #endif /* HAVE_LIBELF_SUPPORT */
 
-int perf_pmu__test_parse_init(void);
-
 struct evsel *parse_events__add_event_hybrid(struct list_head *list, int *idx,
                                             struct perf_event_attr *attr,
                                             const char *name,
 
        return token;
 }
 
-static int raw(yyscan_t scanner)
-{
-       YYSTYPE *yylval = parse_events_get_lval(scanner);
-       char *text = parse_events_get_text(scanner);
-
-       if (perf_pmu__parse_check(text) == PMU_EVENT_SYMBOL)
-               return str(scanner, PE_NAME);
-
-       return __value(yylval, text + 1, 16, PE_RAW);
-}
-
 static bool isbpf_suffix(char *text)
 {
        int len = strlen(text);
        yyless(0);                                              \
 } while (0)
 
-static int pmu_str_check(yyscan_t scanner, struct parse_events_state *parse_state)
-{
-       YYSTYPE *yylval = parse_events_get_lval(scanner);
-       char *text = parse_events_get_text(scanner);
-
-       yylval->str = strdup(text);
-
-       /*
-        * If we're not testing then parse check determines the PMU event type
-        * which if it isn't a PMU returns PE_NAME. When testing the result of
-        * parse check can't be trusted so we return PE_PMU_EVENT_FAKE unless
-        * an '!' is present in which case the text can't be a PMU name.
-        */
-       switch (perf_pmu__parse_check(text)) {
-               case PMU_EVENT_SYMBOL_PREFIX:
-                       return PE_PMU_EVENT_PRE;
-               case PMU_EVENT_SYMBOL_SUFFIX:
-                       return PE_PMU_EVENT_SUF;
-               case PMU_EVENT_SYMBOL_SUFFIX2:
-                       return PE_PMU_EVENT_SUF2;
-               case PMU_EVENT_SYMBOL:
-                       return parse_state->fake_pmu
-                               ? PE_PMU_EVENT_FAKE : PE_KERNEL_PMU_EVENT;
-               default:
-                       return parse_state->fake_pmu && !strchr(text,'!')
-                               ? PE_PMU_EVENT_FAKE : PE_NAME;
-       }
-}
-
 static int sym(yyscan_t scanner, int type, int config)
 {
        YYSTYPE *yylval = parse_events_get_lval(scanner);
 num_dec                [0-9]+
 num_hex                0x[a-fA-F0-9]+
 num_raw_hex    [a-fA-F0-9]+
-name           [a-zA-Z_*?\[\]][a-zA-Z0-9_*?.\[\]!]*
+name           [a-zA-Z_*?\[\]][a-zA-Z0-9_*?.\[\]!\-]*
 name_tag       [\'][a-zA-Z_*?\[\]][a-zA-Z0-9_*?\-,\.\[\]:=]*[\']
 name_minus     [a-zA-Z_*?][a-zA-Z0-9\-_*?.:]*
 drv_cfg_term   [a-zA-Z0-9_\.]+(=[a-zA-Z0-9_*?\.:]+)?
 /* If you add a modifier you need to update check_modifier() */
 modifier_event [ukhpPGHSDIWeb]+
 modifier_bp    [rwx]{1,3}
+lc_type        (L1-dcache|l1-d|l1d|L1-data|L1-icache|l1-i|l1i|L1-instruction|LLC|L2|dTLB|d-tlb|Data-TLB|iTLB|i-tlb|Instruction-TLB|branch|branches|bpu|btb|bpc|node)
+lc_op_result   (load|loads|read|store|stores|write|prefetch|prefetches|speculative-read|speculative-load|refs|Reference|ops|access|misses|miss)
 
 %%
 
 aux-output             { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT); }
 aux-sample-size                { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE); }
 metric-id              { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_METRIC_ID); }
-r{num_raw_hex}         { return raw(yyscanner); }
-r0x{num_raw_hex}       { return raw(yyscanner); }
+r{num_raw_hex}         { return str(yyscanner, PE_RAW); }
+r0x{num_raw_hex}       { return str(yyscanner, PE_RAW); }
 ,                      { return ','; }
 "/"                    { BEGIN(INITIAL); return '/'; }
 {name_minus}           { return str(yyscanner, PE_NAME); }
 bpf-output                                     { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_BPF_OUTPUT); }
 cgroup-switches                                        { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CGROUP_SWITCHES); }
 
-       /*
-        * We have to handle the kernel PMU event cycles-ct/cycles-t/mem-loads/mem-stores separately.
-        * Because the prefix cycles is mixed up with cpu-cycles.
-        * loads and stores are mixed up with cache event
-        */
-cycles-ct                              |
-cycles-t                               |
-mem-loads                              |
-mem-loads-aux                          |
-mem-stores                             |
-topdown-[a-z-]+                                |
-tx-capacity-[a-z-]+                    |
-el-capacity-[a-z-]+                    { return str(yyscanner, PE_KERNEL_PMU_EVENT); }
-
-L1-dcache|l1-d|l1d|L1-data             |
-L1-icache|l1-i|l1i|L1-instruction      |
-LLC|L2                                 |
-dTLB|d-tlb|Data-TLB                    |
-iTLB|i-tlb|Instruction-TLB             |
-branch|branches|bpu|btb|bpc            |
-node                                   { return str(yyscanner, PE_NAME_CACHE_TYPE); }
-
-load|loads|read                                |
-store|stores|write                     |
-prefetch|prefetches                    |
-speculative-read|speculative-load      |
-refs|Reference|ops|access              |
-misses|miss                            { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); }
-
+{lc_type}                      { return str(yyscanner, PE_LEGACY_CACHE); }
+{lc_type}-{lc_op_result}       { return str(yyscanner, PE_LEGACY_CACHE); }
+{lc_type}-{lc_op_result}-{lc_op_result}        { return str(yyscanner, PE_LEGACY_CACHE); }
 mem:                   { BEGIN(mem); return PE_PREFIX_MEM; }
-r{num_raw_hex}         { return raw(yyscanner); }
+r{num_raw_hex}         { return str(yyscanner, PE_RAW); }
 {num_dec}              { return value(yyscanner, 10); }
 {num_hex}              { return value(yyscanner, 16); }
 
 {modifier_event}       { return str(yyscanner, PE_MODIFIER_EVENT); }
 {bpf_object}           { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_OBJECT); }
 {bpf_source}           { if (!isbpf(yyscanner)) { USER_REJECT }; return str(yyscanner, PE_BPF_SOURCE); }
-{name}                 { return pmu_str_check(yyscanner, _parse_state); }
+{name}                 { return str(yyscanner, PE_NAME); }
 {name_tag}             { return str(yyscanner, PE_NAME); }
 "/"                    { BEGIN(config); return '/'; }
--                      { return '-'; }
 ,                      { BEGIN(event); return ','; }
 :                      { return ':'; }
 "{"                    { BEGIN(event); return '{'; }
 
 
 #define YYDEBUG 1
 
+#include <errno.h>
 #include <fnmatch.h>
 #include <stdio.h>
 #include <linux/compiler.h>
 %}
 
 %token PE_START_EVENTS PE_START_TERMS
-%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM
+%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_TERM
 %token PE_VALUE_SYM_TOOL
 %token PE_EVENT_NAME
-%token PE_NAME
+%token PE_RAW PE_NAME
 %token PE_BPF_OBJECT PE_BPF_SOURCE
 %token PE_MODIFIER_EVENT PE_MODIFIER_BP
-%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
+%token PE_LEGACY_CACHE
 %token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
 %token PE_ERROR
-%token PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_PMU_EVENT_SUF2 PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE
+%token PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE
 %token PE_ARRAY_ALL PE_ARRAY_RANGE
 %token PE_DRV_CFG_TERM
 %type <num> PE_VALUE
 %type <num> PE_VALUE_SYM_HW
 %type <num> PE_VALUE_SYM_SW
 %type <num> PE_VALUE_SYM_TOOL
-%type <num> PE_RAW
 %type <num> PE_TERM
 %type <num> value_sym
+%type <str> PE_RAW
 %type <str> PE_NAME
 %type <str> PE_BPF_OBJECT
 %type <str> PE_BPF_SOURCE
-%type <str> PE_NAME_CACHE_TYPE
-%type <str> PE_NAME_CACHE_OP_RESULT
+%type <str> PE_LEGACY_CACHE
 %type <str> PE_MODIFIER_EVENT
 %type <str> PE_MODIFIER_BP
 %type <str> PE_EVENT_NAME
-%type <str> PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_PMU_EVENT_SUF2 PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE
+%type <str> PE_KERNEL_PMU_EVENT PE_PMU_EVENT_FAKE
 %type <str> PE_DRV_CFG_TERM
-%type <str> event_pmu_name
+%type <str> name_or_raw
 %destructor { free ($$); } <str>
 %type <term> event_term
 %destructor { parse_events_term__delete ($$); } <term>
           event_legacy_raw sep_dc |
           event_bpf_file
 
-event_pmu_name:
-PE_NAME | PE_PMU_EVENT_PRE
-
 event_pmu:
-event_pmu_name opt_pmu_config
+PE_NAME opt_pmu_config
 {
        struct parse_events_state *parse_state = _parse_state;
        struct parse_events_error *error = parse_state->error;
        list = alloc_list();
        if (!list)
                CLEANUP_YYABORT;
+       /* Attempt to add to list assuming $1 is a PMU name. */
        if (parse_events_add_pmu(_parse_state, list, $1, $2, /*auto_merge_stats=*/false)) {
                struct perf_pmu *pmu = NULL;
                int ok = 0;
 
+               /* Failure to add, try wildcard expansion of $1 as a PMU name. */
                if (asprintf(&pattern, "%s*", $1) < 0)
                        CLEANUP_YYABORT;
 
                        }
                }
 
+               if (!ok) {
+                       /* Failure to add, assume $1 is an event name. */
+                       zfree(&list);
+                       ok = !parse_events_multi_pmu_add(_parse_state, $1, $2, &list);
+                       $2 = NULL;
+               }
                if (!ok)
                        CLEANUP_YYABORT;
        }
        $$ = list;
 }
 |
-PE_KERNEL_PMU_EVENT opt_pmu_config
+PE_NAME sep_dc
 {
        struct list_head *list;
        int err;
 
-       /* frees $2 */
-       err = parse_events_multi_pmu_add(_parse_state, $1, $2, &list);
+       err = parse_events_multi_pmu_add(_parse_state, $1, NULL, &list);
        free($1);
        if (err < 0)
                YYABORT;
        $$ = list;
 }
 |
-PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF '-' PE_PMU_EVENT_SUF2 sep_dc
-{
-       struct list_head *list;
-       char pmu_name[128];
-       snprintf(pmu_name, sizeof(pmu_name), "%s-%s-%s", $1, $3, $5);
-       free($1);
-       free($3);
-       free($5);
-       if (parse_events_multi_pmu_add(_parse_state, pmu_name, NULL, &list) < 0)
-               YYABORT;
-       $$ = list;
-}
-|
-PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc
+PE_KERNEL_PMU_EVENT opt_pmu_config
 {
        struct list_head *list;
-       char pmu_name[128];
+       int err;
 
-       snprintf(pmu_name, sizeof(pmu_name), "%s-%s", $1, $3);
+       /* frees $2 */
+       err = parse_events_multi_pmu_add(_parse_state, $1, $2, &list);
        free($1);
-       free($3);
-       if (parse_events_multi_pmu_add(_parse_state, pmu_name, NULL, &list) < 0)
+       if (err < 0)
                YYABORT;
        $$ = list;
 }
 }
 
 event_legacy_cache:
-PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT opt_event_config
+PE_LEGACY_CACHE opt_event_config
 {
        struct parse_events_state *parse_state = _parse_state;
        struct parse_events_error *error = parse_state->error;
 
        list = alloc_list();
        ABORT_ON(!list);
-       err = parse_events_add_cache(list, &parse_state->idx, $1, $3, $5, error, $6,
-                                    parse_state);
-       parse_events_terms__delete($6);
-       free($1);
-       free($3);
-       free($5);
-       if (err) {
-               free_list_evsel(list);
-               YYABORT;
-       }
-       $$ = list;
-}
-|
-PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT opt_event_config
-{
-       struct parse_events_state *parse_state = _parse_state;
-       struct parse_events_error *error = parse_state->error;
-       struct list_head *list;
-       int err;
+       err = parse_events_add_cache(list, &parse_state->idx, $1, error, $2, parse_state);
 
-       list = alloc_list();
-       ABORT_ON(!list);
-       err = parse_events_add_cache(list, &parse_state->idx, $1, $3, NULL, error, $4,
-                                    parse_state);
-       parse_events_terms__delete($4);
-       free($1);
-       free($3);
-       if (err) {
-               free_list_evsel(list);
-               YYABORT;
-       }
-       $$ = list;
-}
-|
-PE_NAME_CACHE_TYPE opt_event_config
-{
-       struct parse_events_state *parse_state = _parse_state;
-       struct parse_events_error *error = parse_state->error;
-       struct list_head *list;
-       int err;
-
-       list = alloc_list();
-       ABORT_ON(!list);
-       err = parse_events_add_cache(list, &parse_state->idx, $1, NULL, NULL, error, $2,
-                                    parse_state);
        parse_events_terms__delete($2);
        free($1);
        if (err) {
 }
 
 tracepoint_name:
-PE_NAME '-' PE_NAME ':' PE_NAME
-{
-       struct tracepoint_name tracepoint;
-
-       ABORT_ON(asprintf(&tracepoint.sys, "%s-%s", $1, $3) < 0);
-       tracepoint.event = $5;
-       free($1);
-       free($3);
-       $$ = tracepoint;
-}
-|
 PE_NAME ':' PE_NAME
 {
        struct tracepoint_name tracepoint = {$1, $3};
 {
        struct list_head *list;
        int err;
+       u64 num;
 
        list = alloc_list();
        ABORT_ON(!list);
-       err = parse_events_add_numeric(_parse_state, list, PERF_TYPE_RAW, $1, $2);
+       errno = 0;
+       num = strtoull($1 + 1, NULL, 16);
+       ABORT_ON(errno);
+       free($1);
+       err = parse_events_add_numeric(_parse_state, list, PERF_TYPE_RAW, num, $2);
        parse_events_terms__delete($2);
        if (err) {
                free(list);
        $$ = head;
 }
 
+name_or_raw: PE_RAW | PE_NAME
+
 event_term:
 PE_RAW
 {
        struct parse_events_term *term;
 
-       ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_CONFIG,
-                                       NULL, $1, false, &@1, NULL));
+       if (parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_RAW,
+                                       strdup("raw"), $1, &@1, &@1)) {
+               free($1);
+               YYABORT;
+       }
        $$ = term;
 }
 |
-PE_NAME '=' PE_NAME
+name_or_raw '=' PE_NAME
 {
        struct parse_events_term *term;
 
        $$ = term;
 }
 |
-PE_NAME '=' PE_VALUE
+name_or_raw '=' PE_VALUE
 {
        struct parse_events_term *term;
 
        $$ = term;
 }
 |
-PE_NAME '=' PE_VALUE_SYM_HW
+name_or_raw '=' PE_VALUE_SYM_HW
 {
        struct parse_events_term *term;
        int config = $3 & 255;
        $$ = term;
 }
 |
-PE_NAME array '=' PE_NAME
+name_or_raw array '=' PE_NAME
 {
        struct parse_events_term *term;
 
        $$ = term;
 }
 |
-PE_NAME array '=' PE_VALUE
+name_or_raw array '=' PE_VALUE
 {
        struct parse_events_term *term;