power: add parsing of Vector2d stats for power eq

Adding parsing of Vector2d stats (such as issue queue instruction class
counts in O3) to the equation parser.  This is a little tedious as

* Vector2d (or rather the associated Info class) does not allow access
  to the actual data; instead, one has to pretend printing the stat and
  the data is copied over into an accessible array (cvec)

* the parsing of the 2d stat names, because well.. parsing :)

Added a cache for Vector2d name to fully parsed out stat + index so that
we don't have to run through the parsing code every time we print stats.

Change-Id: I8eb821c05ee60b53a869bbd73164e91dba53400b
Signed-off-by: Stephan Diestelhorst <stephan.diestelhorst@arm.com>
diff --git a/src/sim/mathexpr.cc b/src/sim/mathexpr.cc
index 10d6cb7..75e9f50 100644
--- a/src/sim/mathexpr.cc
+++ b/src/sim/mathexpr.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 ARM Limited
+ * Copyright (c) 2016,2019 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -71,7 +71,7 @@
  * It will look for operators in priority order to recursively build the
  * tree, respecting parenthesization.
  * Constants can be expressed in any format accepted by std::stod, whereas
- * variables are essentially [A-Za-z0-9\.$\\]+
+ * variables are essentially [A-Za-z0-9\.$:\\]+
  */
 MathExpr::Node *
 MathExpr::parse(std::string expr) {
@@ -135,7 +135,7 @@
                 !( (c >= 'a' && c <= 'z') ||
                    (c >= 'A' && c <= 'Z') ||
                    (c >= '0' && c <= '9') ||
-                   c == '$' || c == '\\' || c == '.' || c == '_');
+                   c == '$' || c == '\\' || c == '.' || c == '_' || c == ':');
 
         if (!contains_non_alpha) {
             Node * n = new Node();
@@ -145,6 +145,8 @@
         }
     }
 
+    warn("Expression parser could not parse %s", expr.c_str());
+
     return NULL;
 }
 
diff --git a/src/sim/power/mathexpr_powermodel.cc b/src/sim/power/mathexpr_powermodel.cc
index ab48720..0e132ed 100644
--- a/src/sim/power/mathexpr_powermodel.cc
+++ b/src/sim/power/mathexpr_powermodel.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2017 ARM Limited
+ * Copyright (c) 2016-2017,2019 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -39,6 +39,8 @@
 
 #include "sim/power/mathexpr_powermodel.hh"
 
+#include <algorithm>
+#include <regex>
 #include <string>
 
 #include "base/statistics.hh"
@@ -116,7 +118,74 @@
     return value;
 }
 
+#define ALL(x) x.cbegin(), x.cend()
+static int
+getVecStatIdx(const std::vector<std::string>& names, const std::string &tag)
+{
+    auto is_empty =[](const std::string &s) {return s.empty();};
+    bool empty = names.empty() ||
+                 std::all_of(ALL(names), is_empty);
 
+    // Indices can either be tags or numerical; the stats system uses the tags
+    // if they exist
+    if (empty)
+        return std::stoi(tag);
+
+    auto pos = find(ALL(names), tag);
+    if (pos == names.cend()) {
+        warn("Could not find %s in vector stats", tag);
+        return -1;
+    }
+    return pos - names.cbegin();
+}
+#undef ALL
+
+struct V2D_Cache {
+    Stats::Vector2dInfo *info;
+    int idx;
+};
+
+bool MathExprPowerModel::parseVec2dStat(std::string name, Stats::Vector2dInfo
+        **out_v2di, int *out_idx) const {
+    using namespace Stats;
+
+    // Prune the name for vector stats, they look like this:
+    // iq.FU_type_0::Alu, really meaning iq.FU_type[0][Alu]
+    std::regex  split("([._[:alnum:]]*)_(\\w*)::(\\w*)");
+    std::smatch results;
+
+    bool matched = regex_match(name, results, split);
+    panic_if(!matched, "Could not match stat name %s", name.c_str());
+
+    auto bname = results[1];
+    auto xtag  = results[2];
+    auto ytag  = results[3];
+
+    auto it = stats_map.find(bname);
+    if (it == stats_map.cend()) {
+        warn("Failed to find stat '%s'\n", name);
+        failed = true;
+        return false;
+    }
+    Info *info = it->second;
+    auto v2di = dynamic_cast<Vector2dInfo *>(info);
+    if (!v2di)
+        return false;
+
+    int xidx = getVecStatIdx(v2di->subnames, xtag);
+    int yidx = getVecStatIdx(v2di->y_subnames, ytag);
+
+    if (xidx == -1 || yidx == -1)
+        return false;
+
+    assert (0 <= xidx && xidx < v2di->x);
+    assert (0 <= yidx && yidx < v2di->y);
+    int idx = xidx * v2di->y + yidx;
+
+    *out_idx  = idx;
+    *out_v2di = v2di;
+    return true;
+}
 double
 MathExprPowerModel::getStatValue(const std::string &name) const
 {
@@ -129,6 +198,27 @@
         return clocked_object->voltage();
     }
 
+    // Check for a Vector 2D stat; first in cache
+    static std::unordered_map<std::string, V2D_Cache> v2d_cache;
+    auto v2d = v2d_cache.find(name);
+    if (v2d != v2d_cache.end()) {
+        // Found, pull out the data
+        auto v2di = v2d->second.info;
+        // cvec data is only initialised in prepare()
+        v2di->prepare();
+        return v2di->cvec[v2d->second.idx];
+    } else if (name.find("::") != name.npos) {
+        // Not found, but looks like a Vector 2D stat -> parse and add
+        Vector2dInfo *v2di;
+        int idx;
+        if (parseVec2dStat(name, &v2di, &idx)) {
+            // Add to cache
+            v2d_cache.emplace(name, V2D_Cache {v2di, idx});
+            v2di->prepare();
+            return v2di->cvec[idx];
+        }
+    }
+
     // Try to cast the stat, only these are supported right now
     const auto it = stats_map.find(name);
     if (it == stats_map.cend()) {
diff --git a/src/sim/power/mathexpr_powermodel.hh b/src/sim/power/mathexpr_powermodel.hh
index b4d0254..e15d6e7 100644
--- a/src/sim/power/mathexpr_powermodel.hh
+++ b/src/sim/power/mathexpr_powermodel.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2017 ARM Limited
+ * Copyright (c) 2016-2017,2019 ARM Limited
  * All rights reserved
  *
  * The license below extends only to copyright in the software and shall
@@ -107,6 +107,9 @@
      */
     double tryEval(const MathExpr &expr) const;
 
+    bool parseVec2dStat(std::string name, Stats::Vector2dInfo **out_v2di,
+                        int *out_idx) const;
+
     // Math expressions for dynamic and static power
     MathExpr dyn_expr, st_expr;