--- /dev/null
+/*
+ * Copyright (c) 2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kanishk Sugand
+ */
+
+#include "base/intmath.hh"
+#include "base/trace.hh"
+#include "debug/StackDist.hh"
+#include "mem/stack_dist_calc.hh"
+
+StackDistCalc::StackDistCalc(const StackDistCalcParams* p) :
+ SimObject(p), index(0), verifyStack(p->verify),
+ disableLinearHists(p->disable_linear_hists),
+ disableLogHists(p->disable_log_hists)
+{
+ // Instantiate a new root and leaf layer
+ // Map type variable, representing a layer in the tree
+ IndexNodeMap tree_level;
+
+ // Initialize tree count for leaves
+ nextIndex.push_back(0);
+
+ // Add the initial leaf layer to the tree
+ tree.push_back(tree_level);
+
+ // Create a root node. Node type variable in the topmost layer
+ Node* root_node = new Node();
+
+ // Initialize tree count for root
+ nextIndex.push_back(1);
+
+ // Add the empty root layer to the tree
+ tree.push_back(tree_level);
+
+ // Add the initial root to the tree
+ tree[1][root_node->nodeIndex] = root_node;
+}
+
+StackDistCalc::~StackDistCalc()
+{
+ // Walk through each layer and destroy the nodes
+ for (auto& layer : tree) {
+ for (auto& index_node : layer) {
+ // each map entry contains an index and a node
+ delete index_node.second;
+ }
+ // Clear each layer in the tree
+ layer.clear();
+ }
+
+ // Clear the tree
+ tree.clear();
+ aiMap.clear();
+ nextIndex.clear();
+
+ // For verification
+ stack.clear();
+}
+
+void
+StackDistCalc::update(const MemCmd& cmd, Addr addr)
+{
+ // only capturing read and write requests (which allocate in the
+ // cache)
+ if (cmd.isRead() || cmd.isWrite()) {
+ auto returnType = calcStackDistAndUpdate(addr);
+
+ uint64_t stackDist = returnType.first;
+
+ if (stackDist != Infinity) {
+ // Sample the stack distance of the address in linear bins
+ if (!disableLinearHists) {
+ if (cmd.isRead())
+ readLinearHist.sample(stackDist);
+ else
+ writeLinearHist.sample(stackDist);
+ }
+
+ if (!disableLogHists) {
+ int stackDistLog2 = stackDist == 0 ? 1 : floorLog2(stackDist);
+
+ // Sample the stack distance of the address in log bins
+ if (cmd.isRead())
+ readLogHist.sample(stackDistLog2);
+ else
+ writeLogHist.sample(stackDistLog2);
+ }
+ }
+ }
+}
+
+// The updateSum method is a recursive function which updates
+// the node sums till the root. It also deletes the nodes that
+// are not used anymore.
+uint64_t
+StackDistCalc::updateSum(Node* node, bool from_left,
+ uint64_t sum_from_below, uint64_t level,
+ uint64_t stack_dist, bool discard_node)
+{
+ ++level;
+
+ // Make a copy of the node variables and work on them
+ // as a node may be deleted by this function
+ uint64_t node_sum_l = node->sumLeft;
+ uint64_t node_sum_r = node->sumRight;
+ bool node_left = node->isLeftNode;
+ bool node_discard_left = node->discardLeft;
+ bool node_discard_right = node->discardRight;
+ uint64_t node_n_index = node->nodeIndex;
+ Node* node_parent_ptr = node->parent;
+
+ // For verification
+ if (verifyStack) {
+ // This sanity check makes sure that the left_sum and
+ // right_sum of the node is not greater than the
+ // maximum possible value given by the leaves below it
+ // for example a node in layer 3 (tree[3]) can at most
+ // have 8 leaves (4 to the left and 4 to the right)
+ // thus left_sum and right_sum should be <= 4
+ panic_if(node_sum_l > (1 << (level - 1)),
+ "Error in sum left of level %ul, node index %ull, "
+ "Sum = %ull \n", level, node_n_index, node_sum_l);
+
+ panic_if(node_sum_r > (1 << (level - 1)),
+ "Error in sum right of level %ul, node index %ull, "
+ "Sum = %ull \n", level, node_n_index, node_sum_r);
+ }
+
+ // Update the left sum or the right sum depending on the
+ // from_left flag. Variable stack_dist is updated only
+ // when arriving from Left.
+ if (from_left) {
+ // update sumLeft
+ node_sum_l = sum_from_below;
+ stack_dist += node_sum_r;
+ } else {
+ // update sum_r
+ node_sum_r = sum_from_below;
+ }
+
+ // sum_from_below == 0 can be a leaf discard operation
+ if (discard_node && !sum_from_below) {
+ if (from_left)
+ node_discard_left = true;
+ else
+ node_discard_right = true;
+ }
+
+ // Update the node variables with new values
+ node->nodeIndex = node_n_index;
+ node->sumLeft = node_sum_l;
+ node->sumRight = node_sum_r;
+ node->isLeftNode = node_left;
+ node->discardLeft = node_discard_left;
+ node->discardRight = node_discard_right;
+
+ // Delete the node if it is not required anymore
+ if (node_discard_left && node_discard_right &&
+ discard_node && node_parent_ptr && !sum_from_below) {
+ delete node;
+ tree[level].erase(node_n_index);
+ discard_node = true;
+ } else {
+ // propogate discard_node as false upwards if the
+ // above conditions are not met.
+ discard_node = false;
+ }
+
+ // Recursively call the updateSum operation till the
+ // root node is reached
+ if (node_parent_ptr) {
+ stack_dist = updateSum(node_parent_ptr, node_left,
+ node_sum_l + node_sum_r,
+ level, stack_dist, discard_node);
+ }
+
+ return stack_dist;
+}
+
+// This function is called by the calcStackDistAndUpdate function
+// If is_new_leaf is true then a new leaf is added otherwise a leaf
+// removed from the tree. In both cases the tree is updated using
+// the updateSum operation.
+uint64_t
+StackDistCalc::updateSumsLeavesToRoot(Node* node, bool is_new_leaf)
+{
+ uint64_t level = 0;
+ uint64_t stack_dist = 0;
+
+ if (is_new_leaf) {
+ node->sumLeft = 1;
+ updateSum(node->parent,
+ node->isLeftNode, node->sumLeft,
+ level, 0, false);
+
+ stack_dist = Infinity;
+ } else {
+ node->sumLeft = 0;
+ stack_dist = updateSum(node->parent,
+ node->isLeftNode, 0,
+ level, stack_dist, true);
+ }
+
+ return stack_dist;
+}
+
+// This method is a recursive function which calculates
+// the node sums till the root.
+uint64_t
+StackDistCalc::getSum(Node* node, bool from_left, uint64_t sum_from_below,
+ uint64_t stack_dist, uint64_t level) const
+{
+ ++level;
+ // Variable stack_dist is updated only
+ // when arriving from Left.
+ if(from_left) {
+ stack_dist += node->sumRight;
+ }
+
+ // Recursively call the getSum operation till the
+ // root node is reached
+ if(node->parent) {
+ stack_dist = getSum(node->parent, node->isLeftNode,
+ node->sumLeft + node->sumRight,
+ stack_dist, level);
+ }
+
+ return stack_dist;
+}
+
+// This function is called by the calcStackDistance function
+uint64_t
+StackDistCalc::getSumsLeavesToRoot(Node* node) const
+{
+ return getSum(node->parent, node->isLeftNode, 0, 0, 0);
+}
+
+// Update tree is a tree balancing operation which maintains
+// the binary tree structure. This method is called whenever
+// index%2 == 0 (i.e. every alternate cycle)
+// The two main operation are :
+// OP1. moving the root node one layer up if index counter
+// crosses power of 2
+// OP2. Addition of intermediate nodes as and when required
+// and linking them to their parents in the layer above.
+void
+StackDistCalc::updateTree()
+{
+ uint64_t i;
+
+ if (isPowerOf2(index)) {
+ // OP1. moving the root node one layer up if index counter
+ // crosses power of 2
+ // If index counter crosses a power of 2, then add a
+ // new tree layer above and create a new Root node in it.
+ // After the root is created the old node
+ // in the layer below is updated to point to this
+ // newly created root node. The sum_l of this new root node
+ // becomes the sum_l + sum_r of the old node.
+ //
+ // After this root update operation a chain of intermediate
+ // nodes is created from root layer to tree[1](one layer
+ // above the leaf layer)
+
+ // Create a new root node
+ Node* newRootNode = new Node();
+
+ // Update its sum_l as the sum_l+sum_r from below
+ newRootNode->sumLeft = tree[getTreeDepth()][0]->sumRight +
+ tree[getTreeDepth()][0]->sumLeft;
+ // Update its discard left flag if the node below has
+ // has both discardLeft and discardRight set.
+ newRootNode->discardLeft = tree[getTreeDepth()][0]->discardLeft &&
+ tree[getTreeDepth()][0]->discardRight;
+
+ // Map type variable, representing a layer in the tree
+ IndexNodeMap treeLevel;
+ // Add a new layer to the tree
+ tree.push_back(treeLevel);
+ nextIndex.push_back(1);
+ tree[getTreeDepth()][newRootNode->nodeIndex] = newRootNode;
+
+ // Update the parent pointer at lower layer to
+ // point to newly created root node
+ tree[getTreeDepth() - 1][0]->parent = tree[getTreeDepth()][0];
+
+ // Add intermediate nodes from root till bottom (one layer above the
+ // leaf layer)
+ for (i = getTreeDepth() - 1; i >= 1; --i) {
+ Node* newINode = new Node();
+ // newNode is left or right child depending on the number of nodes
+ // in that layer
+ if (nextIndex[i] % 2 == 0) {
+ newINode->isLeftNode = true;
+ } else {
+ newINode->isLeftNode = false;
+ }
+
+ newINode->parent = tree[i + 1][nextIndex[i + 1] - 1];
+ newINode->nodeIndex = ++nextIndex[i] - 1;
+ tree[i][newINode->nodeIndex] = newINode;
+ }
+ } else {
+ // OP2. Addition of intermediate nodes as and when required
+ // and linking them to their parents in the layer above.
+ //
+ // At layer 1 a new INode is added whenever index%(2^1)==0
+ // (multiples of 2)
+ //
+ // At layer 2 a new INode is added whenever index%(2^2)==0
+ // (multiples of 4)
+ //
+ // At layer 3 a new INode is added whenever index%(2^3)==0
+ // (multiples of 8)
+ //...
+ //
+ // At layer N a new INode is added whenever index%(2^N)==0
+ // (multiples of 2^N)
+ for (i = getTreeDepth() - 1; i >= 1; --i) {
+ // Traverse each layer from root to leaves and add a new
+ // intermediate node if required. Link the parent_ptr of
+ // the new node to the parent in the above layer.
+
+ if ((index % (1 << i)) == 0) {
+ // Checks if current (index % 2^treeDepth) == 0 if true
+ // a new node at that layer is created
+ Node* newINode = new Node();
+
+ // newNode is left or right child depending on the
+ // number of nodes in that layer.
+ if (nextIndex[i] % 2 == 0) {
+ newINode->isLeftNode = true;
+ } else {
+ newINode->isLeftNode = false;
+ }
+
+ // Pointing to its parent in the upper layer
+ newINode->parent = tree[i + 1][nextIndex[i + 1] - 1];
+ newINode->nodeIndex = ++nextIndex[i] - 1;
+ tree[i][newINode->nodeIndex] = newINode;
+ }
+ }
+ }
+}
+
+// This function is called everytime to get the stack distance and add
+// a new node. A feature to mark an old node in the tree is
+// added. This is useful if it is required to see the reuse
+// pattern. For example, BackInvalidates from the lower level (Membus)
+// to L2, can be marked (isMarked flag of Node set to True). And then
+// later if this same address is accessed by L1, the value of the
+// isMarked flag would be True. This would give some insight on how
+// the BackInvalidates policy of the lower level affect the read/write
+// accesses in an application.
+std::pair< uint64_t, bool>
+StackDistCalc::calcStackDistAndUpdate(const Addr r_address, bool addNewNode)
+{
+ Node* newLeafNode;
+ // Return index if the address was already present in stack
+ uint64_t r_index = index;
+
+ auto ai = aiMap.lower_bound(r_address);
+
+ // Default value of flag indicating as the left or right leaf
+ bool isLeft = true;
+ // Default value of isMarked flag for each node.
+ bool _mark = false;
+ // By default stackDistacne is treated as infinity
+ uint64_t stack_dist;
+
+ // Lookup aiMap by giving address as the key:
+ // If found take address and Lookup in tree
+ // Update tree from leaves by making B(found index) = 0
+ // Add sums to right till root while Updating them
+ // Stack Distance of that address sums to right
+ if (ai != aiMap.end() && !(aiMap.key_comp()(r_address, ai->first))) {
+ // key already exists
+ // save the index counter value when this address was
+ // encountered before and update it to the current index value
+ r_index = ai->second;
+
+ if (addNewNode) {
+ // Update aiMap aiMap(Address) = current index
+ ai->second = index;
+ } else {
+ aiMap.erase(r_address);
+ }
+
+ // Call update tree operation on the tree starting with
+ // the r_index value found above. This function would return
+ // the value of the stack distcance.
+ stack_dist = updateSumsLeavesToRoot(tree[0][r_index], false);
+ newLeafNode = tree[0][r_index];
+ // determine if this node was marked earlier
+ _mark = newLeafNode->isMarked;
+ delete newLeafNode;
+ tree[0].erase(r_index);
+ } else {
+ if (addNewNode) {
+ // Update aiMap aiMap(Address) = current index
+ aiMap[r_address] = index;
+ }
+ // Update infinity bin count
+ // By default stackDistacne is treated as infinity
+ stack_dist = Infinity;
+ }
+
+ if (addNewNode) {
+ // If index%2 == 0 then update tree
+ if (index % 2 == 0) {
+ updateTree();
+ } else {
+ // At odd values of index counter, a new right-type node is
+ // added to the leaf layer, else a left-type node is added
+ isLeft = false;
+ }
+
+ // Add new leaf node in the leaf layer (tree[0])
+ // set n_index = current index
+ newLeafNode = new Node();
+ ++nextIndex[0];
+ newLeafNode->nodeIndex=index;
+ newLeafNode->isLeftNode=isLeft;
+ // Point the parent pointer to the intermediate node above
+ newLeafNode->parent = tree[1][nextIndex[1] - 1];
+ tree[0][index] = newLeafNode;
+ // call an update operation which would update the tree after
+ // addition of this new leaf node.
+ updateSumsLeavesToRoot(tree[0][index], true);
+
+ // For verification
+ if (verifyStack) {
+ // This function checks the sanity of the tree to make sure the
+ // last node in the link of parent pointers is the root node.
+ // It takes a leaf node as an argument and traveses upwards till
+ // the root layer to check if the last parent is null
+ sanityCheckTree(tree[0][index]);
+
+ // Push the same element in debug stack, and check
+ uint64_t verify_stack_dist = verifyStackDist(r_address, true);
+ panic_if(verify_stack_dist != stack_dist,
+ "Expected stack-distance for address \
+ %#lx is %#lx but found %#lx",
+ r_address, verify_stack_dist, stack_dist);
+ printStack();
+ }
+
+ // The index counter is updated at the end of each transaction
+ // (unique or non-unique)
+ ++index;
+ }
+
+ return (std::make_pair(stack_dist, _mark));
+}
+
+// This function is called everytime to get the stack distance
+// no new node is added. It can be used to mark a previous access
+// and inspect the value of the mark flag.
+std::pair< uint64_t, bool>
+StackDistCalc::calcStackDist(const Addr r_address, bool mark)
+{
+ // Return index if the address was already present in stack
+ uint64_t r_index = index;
+ // Default value of isMarked flag for each node.
+ bool _mark = false;
+
+ auto ai = aiMap.lower_bound(r_address);
+
+ // By default stackDistacne is treated as infinity
+ uint64_t stack_dist = 0;
+
+ // Lookup aiMap by giving address as the key:
+ // If found take address and Lookup in tree
+ // Add sums to right till root
+ // Stack Distance of that address sums to right
+ if (ai != aiMap.end() && !(aiMap.key_comp()(r_address, ai->first))) {
+ // key already exists
+ // save the index counter value when this address was
+ // encountered before
+ r_index = ai->second;
+
+ // Get the value of mark flag if previously marked
+ _mark = tree[0][r_index]->isMarked;
+ // Mark the leaf node if required
+ tree[0][r_index]->isMarked = mark;
+
+ // Call get sums operation on the tree starting with
+ // the r_index value found above. This function would return
+ // the value of the stack distcance.
+ stack_dist = getSumsLeavesToRoot(tree[0][r_index]);
+ } else {
+ // Update infinity bin count
+ // By default stackDistacne is treated as infinity
+ stack_dist = Infinity;
+ }
+
+ // For verification
+ if (verifyStack) {
+ // Calculate the SD of the same address in the debug stack
+ uint64_t verify_stack_dist = verifyStackDist(r_address);
+ panic_if(verify_stack_dist != stack_dist,
+ "Expected stack-distance for address \
+ %#lx is %#lx but found %#lx",
+ r_address, verify_stack_dist, stack_dist);
+
+ printStack();
+ }
+
+ return std::make_pair(stack_dist, _mark);
+}
+
+// For verification
+// Simple sanity check for the tree
+void
+StackDistCalc::sanityCheckTree(const Node* node, uint64_t level) const
+{
+ const Node* next_up = node->parent;
+
+ for (uint64_t i = level + 1; i < getTreeDepth() - level; ++i) {
+ next_up = next_up->parent;
+ panic_if(!next_up, "Sanity check failed for node %ull \n",
+ node->nodeIndex);
+ }
+
+ // At the root layer the parent_ptr should be null
+ panic_if(next_up->parent, "Sanity check failed for node %ull \n",
+ node->nodeIndex);
+}
+
+// This method can be called to compute the stack distance in a naive
+// way It can be used to verify the functionality of the stack
+// distance calculator. It uses std::vector to compute the stack
+// distance using a naive stack.
+uint64_t
+StackDistCalc::verifyStackDist(const Addr r_address, bool update_stack)
+{
+ bool found = false;
+ uint64_t stack_dist = 0;
+ auto a = stack.rbegin();
+
+ for (; a != stack.rend(); ++a) {
+ if (*a == r_address) {
+ found = true;
+ break;
+ } else {
+ ++stack_dist;
+ }
+ }
+
+ if (found) {
+ ++a;
+ if (update_stack)
+ stack.erase(a.base());
+ } else {
+ stack_dist = Infinity;
+ }
+
+ if (update_stack)
+ stack.push_back(r_address);
+
+ return stack_dist;
+}
+
+// This method is useful to print top n entities in the stack.
+void
+StackDistCalc::printStack(int n) const
+{
+ Node* node;
+ uint64_t r_index;
+ int count = 0;
+
+ DPRINTF(StackDist, "Printing last %d entries in tree\n", n);
+
+ // Walk through leaf layer to display the last n nodes
+ for (auto it = tree[0].rbegin(); (count < n) && (it != tree[0].rend());
+ ++it, ++count) {
+ node = it->second;
+ r_index = node->nodeIndex;
+
+ // Lookup aiMap using the index returned by the leaf iterator
+ for (auto ai = aiMap.rbegin(); ai != aiMap.rend(); ++ai) {
+ if (ai->second == r_index) {
+ DPRINTF(StackDist,"Tree leaves, Rightmost-[%d] = %#lx\n",
+ count, ai->first);
+ break;
+ }
+ }
+ }
+
+ DPRINTF(StackDist,"Tree depth = %#ld\n", getTreeDepth());
+
+ if (verifyStack) {
+ DPRINTF(StackDist,"Printing Last %d entries in VerifStack \n", n);
+ count = 0;
+ for (auto a = stack.rbegin(); (count < n) && (a != stack.rend());
+ ++a, ++count) {
+ DPRINTF(StackDist, "Verif Stack, Top-[%d] = %#lx\n", count, *a);
+ }
+ }
+}
+
+void
+StackDistCalc::regStats()
+{
+ using namespace Stats;
+
+ readLinearHist
+ .init(params()->linear_hist_bins)
+ .name(name() + ".readLinearHist")
+ .desc("Reads linear distribution")
+ .flags(disableLinearHists ? nozero : pdf);
+
+ readLogHist
+ .init(params()->log_hist_bins)
+ .name(name() + ".readLogHist")
+ .desc("Reads logarithmic distribution")
+ .flags(disableLogHists ? nozero : pdf);
+
+ writeLinearHist
+ .init(params()->linear_hist_bins)
+ .name(name() + ".writeLinearHist")
+ .desc("Writes linear distribution")
+ .flags(disableLinearHists ? nozero : pdf);
+
+ writeLogHist
+ .init(params()->log_hist_bins)
+ .name(name() + ".writeLogHist")
+ .desc("Writes logarithmic distribution")
+ .flags(disableLogHists ? nozero : pdf);
+}
+
+StackDistCalc*
+StackDistCalcParams::create()
+{
+ return new StackDistCalc(this);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met: redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer;
+ * redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution;
+ * neither the name of the copyright holders nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Authors: Kanishk Sugand
+ * Andreas Hansson
+ */
+
+#ifndef __MEM_STACK_DIST_CALC_HH__
+#define __MEM_STACK_DIST_CALC_HH__
+
+#include <map>
+#include <vector>
+
+#include "base/types.hh"
+#include "mem/packet.hh"
+#include "params/StackDistCalc.hh"
+#include "sim/sim_object.hh"
+#include "sim/stats.hh"
+
+/**
+ * The stack distance calculator is a passive object that merely
+ * observes the addresses pass to it. It calculates stack distances
+ * of incoming addresses based on the partial sum hierarchy tree
+ * algorithm described by Alamasi et al.
+ * http://doi.acm.org/10.1145/773039.773043.
+ *
+ * A tree structure is maintained and updated at each transaction
+ * (unique or non-unique). The tree is implemented as an STL vector
+ * with layers of the form <map> Each layer in this tree is an
+ * ordered map <uint64_t, Node*>. Nodes are structs which take form
+ * of leaf, intermediate and root nodes. For example, in a tree with 3
+ * layers, tree[0][5] gives a leaf node pointer for key=5 tree[1][1]
+ * gives an intermediate node pointer for key=1 tree[2][0] gives the
+ * root node in the tree.
+ *
+ * At every transaction a hash-map (aiMap) is looked up to check if
+ * the address was already encountered before. Based on this lookup a
+ * transaction can be termed as unique or non-unique.
+ *
+ * In addition to the normal stack distance calculation, a feature to
+ * mark an old node in the tree is added. This is useful if it is
+ * required to see the reuse pattern. For example, BackInvalidates
+ * from a lower level (e.g. membus to L2), can be marked (isMarked
+ * flag of Node set to True). Then later if this same address is
+ * accessed (by L1), the value of the isMarked flag would be
+ * True. This would give some insight on how the BackInvalidates
+ * policy of the lower level affect the read/write accesses in an
+ * application.
+ *
+ * There are two functions provided to interface with the calculator:
+ * 1. pair<uint64_t, bool> calcStackDistAndUpdate(Addr r_address,
+ * bool addNewNode)
+ * At every unique transaction a new leaf node is added at tree[0](leaf layer)
+ * and linked to the layer above (if addNewNode is True). The sums of all
+ * the intermediate nodes is updated till the root. The stack-distance is
+ * returned as a Constant representing INFINITY.
+ *
+ * At every non-unique transaction the tree is traversed from the
+ * leaf at the returned index to the root, the old node is deleted
+ * from the tree, and the sums (to the right are collected) and
+ * decremented. The collected sum represets the stack distance of the
+ * found node. If this node was marked then a bool flag set to True
+ * is returned with the stack_distance. During this operation a node
+ * is discarded at the leaf layer always. Moreover during the
+ * traversal upwards using the updateSum() method, if an intermediate
+ * node is found with no children connected to it, then that is
+ * discarded too.
+ *
+ * The return value of this function is a pair representing the
+ * stack_distance and the value of the marked flag.
+ *
+ * 2. pair<uint64_t , bool> calcStackDist(Addr r_address, bool mark)
+ * This is a stripped down version of the above function which is used to
+ * just inspect the tree, and mark a leaf node (if mark flag is set). The
+ * functionality to add a new node is removed.
+ *
+ * At every unique transaction the stack-distance is returned as a constant
+ * representing INFINITY.
+ *
+ * At every non-unique transaction the tree is traversed from the
+ * leaf at the returned index to the root, and the sums (to the right)
+ * are collected. The collected sum represets the stack distance of
+ * the found node.
+ *
+ * This function does NOT Modify the stack. (No node is added or
+ * deleted). It is just used to mark a node already created and get
+ * its stack distance.
+ *
+ * The return value of this function is a pair representing the stack
+ * distance and the value of the marked flag.
+ *
+ * The table below depicts the usage of the Algorithm using the functions:
+ * pair<uint64_t Stack_dist, bool isMarked> calcStackDistAndUpdate
+ * (Addr r_address, bool addNewNode)
+ * pair<uint64_t Stack_dist, bool isMarked> calcStackDist
+ * (Addr r_address, bool mark)
+ *
+ * | Function | Arguments |Return Val |Use For|
+ * |calcStackDistAndUpdate|r_address, True|I/SD,False |A,GD,GM|
+ * |calcStackDistAndUpdate|r_address,False|SD,prevMark|D,GD,GM|
+ * |calcStackDist |r_address,False|SD,prevMark| GD,GM|
+ * |calcStackDist |r_address, True|SD,prevMark| GD,GM|
+ *
+ * (*A: Allocate an address in stack, if old entry present then it is deleted,
+ * *U: Delete old-address from stack, no new entry is added
+ * *GD: Get-Stack distance of an address,
+ * *GM: Get value of Mark flag, indicates if that address has been touched
+ * before,
+ * *I: stack-distance = infinity,
+ * *SD: Stack Distance
+ * *r_address: address to be added, *prevMark: value of isMarked flag
+ * of the Node)
+ *
+ * Invalidates refer to a type of packet that removes something from
+ * a cache, either autonoumously (due-to cache's own replacement
+ * policy), or snoops from other caches which invalidate something
+ * inside our cache.
+ *
+ * Usage | Function to use |Typical Use |
+ * Add new entry |calcStackDistAndUpdate|Read/Write Allocate |
+ * Delete Old Entry |calcStackDistAndUpdate|Writebacks/Cleanevicts|
+ * Dist.of Old entry|calcStackDist |Cleanevicts/Invalidate|
+ *
+ * Node Balancing: The tree structure is maintained by an
+ * updateTree() operation called when an intermediate node is
+ * required. The update operation is roughly categorized as a root
+ * update or intermediate layer update. When number of leaf nodes
+ * grow over a power of 2 then a new layer is added at the top of the
+ * tree and a new root node is initialized. The old node at the lower
+ * layer is connected to this. In an intermediate node update
+ * operation a new intermediate node is added to the required layer.
+ *
+ * Debugging: Debugging can be enabled by setting the verifyStack flag
+ * true. Debugging is implemented using a dummy stack that behaves in
+ * a naive way, using STL vectors (i.e each unique address is pushed
+ * on the top of an STL vector stack, and SD is returned as
+ * Infinity. If a non unique address is encountered then the previous
+ * entry in the STL vector is removed, all the entities above it are
+ * pushed down, and the address is pushed at the top of the stack).
+ *
+ * A printStack(int numOfEntitiesToPrint) is provided to print top n entities
+ * in both (tree and STL based dummy stack).
+ */
+class StackDistCalc : public SimObject
+{
+
+ private:
+
+ struct Node;
+
+ typedef std::map<uint64_t, Node*> IndexNodeMap;
+ typedef std::map<Addr, uint64_t> AddressIndexMap;
+ typedef std::vector<IndexNodeMap> TreeType;
+
+ /**
+ * Gets sum from the node upwards recursively till the root. This
+ * function is called first by getSumsLeavesToRoot, and then
+ * recursively calls itself.
+ *
+ * @param node pointer to the node which is updated
+ * @param from_left variable which says that the request arrived
+ * from the left
+ * @param sum_from_below Sum of left and right children below
+ * @param level level in the tree the calling node is located
+ * @param stack_dist stack distance of the node below
+ * @return The stack distance of the current address.
+ *
+ */
+ uint64_t getSum(Node* node, bool from_left, uint64_t sum_from_below,
+ uint64_t stack_dist, uint64_t level) const;
+
+ /**
+ * Gets the sum from the leaf node specified. This function
+ * is called by calcStackDist.
+ *
+ * @param node pointer to the node which is updated
+ * @return The stack distance of the current address.
+ *
+ */
+ uint64_t getSumsLeavesToRoot(Node* node) const;
+
+ /**
+ * Updates the nodes upwards recursively till the root.
+ * This function is first called by updateSumsLeavesToRoot,
+ * and then it recursively calls itself.
+ *
+ * @param node pointer to the node which is updated
+ * @param from_left variable which says that the request arrived
+ * from the left
+ * @param sum_from_below Sum of left and right children below
+ * @param level level in the tree the calling node is located
+ * @param stack_dist stack distance of the node below
+ * @param discard_node whether the calling node was discarded or not
+ * @return The stack distance of the current address.
+ *
+ */
+ uint64_t updateSum(Node* node,
+ bool from_left, uint64_t sum_from_below, uint64_t level,
+ uint64_t stack_dist, bool discard_node);
+
+ /**
+ * Updates the leaf nodes and nodes above. This function is
+ * called by the calcStackDistAndUpdate.
+ *
+ * @param node pointer to the node which is updated
+ * @param is_new_leaf is true if this is a newly added node
+ * @return The stack distance of the current address.
+ *
+ */
+ uint64_t updateSumsLeavesToRoot(Node* node, bool is_new_leaf);
+
+ /**
+ * updateTree is a tree balancing operation, which maintains the
+ * binary tree structure.
+ * This method is called whenever index%2 == 0 (i.e. every
+ * alternate cycle) The two main operation are :
+ * OP1. Moving the root node one layer up if index counter
+ * crosses power of 2
+ * OP2. Addition of intermediate nodes as and when required
+ * and linking them to their parents in the layer above.
+ */
+ void updateTree();
+
+ /**
+ * This method is used for verification purposes
+ * It recursively traverses upwards from the given node till
+ * the root to check if the ultimate parent node (root-node) points
+ * to null.
+ *
+ * @param node pointer to the node whose sanity is being checked
+ * @param level the level at which this node is located in the tree
+ *
+ */
+ void sanityCheckTree(const Node* node, uint64_t level = 0) const;
+
+ /**
+ * A convenient way of refering to infinity.
+ */
+ static constexpr uint64_t Infinity = std::numeric_limits<uint64_t>::max();
+
+ /**
+ * Process the given address. If Mark is true then set the
+ * mark flag of the leaf node.
+ * This function returns the stack distance of the incoming
+ * address and the previous status of the mark flag.
+ *
+ * @param r_address The current address to process
+ * @param mark set the mark flag for the address.
+ * @return The stack distance of the current address and the mark flag.
+ */
+ std::pair<uint64_t , bool> calcStackDist(const Addr r_address,
+ bool mark = false);
+
+ /**
+ * Process the given address:
+ * - Lookup the tree for the given address
+ * - delete old node if found in tree
+ * - add a new node (if addNewNode flag is set)
+ * This function returns the stack distance of the incoming
+ * address and the status of the mark flag.
+ *
+ * @param r_address The current address to process
+ * @param addNewNode If true, a new node is added to the tree
+ * @return The stack distance of the current address and the mark flag.
+ */
+ std::pair<uint64_t, bool> calcStackDistAndUpdate(const Addr r_address,
+ bool addNewNode = true);
+
+ /**
+ * Return the counter for address accesses (unique and
+ * non-unique). This is further used to dump stats at
+ * regular intervals.
+ *
+ * @return The stack distance of the current address.
+ */
+ uint64_t getIndex() const { return index; }
+
+ /**
+ * Query depth of the tree (tree[0] represents leaf layer while
+ * tree[treeDepth] represents the root layer, all layers in
+ * between contain intermediate nodes)
+ *
+ * @return Tree depth
+ */
+ uint64_t getTreeDepth() const { return tree.size() - 1; }
+
+ /**
+ * Print the last n items on the stack.
+ * This method prints top n entries in the tree based implementation as
+ * well as dummy stack.
+ * @param n Number of entries to print
+ */
+ void printStack(int n = 5) const;
+
+ /**
+ * This is an alternative implementation of the stack-distance
+ * in a naive way. It uses simple STL vector to represent the stack.
+ * It can be used in parallel for debugging purposes.
+ * It is 10x slower than the tree based implemenation.
+ *
+ * @param r_address The current address to process
+ * @param update_stack Flag to indicate if stack should be updated
+ * @return Stack distance which is calculated by this alternative
+ * implementation
+ *
+ */
+ uint64_t verifyStackDist(const Addr r_address,
+ bool update_stack = false);
+
+ public:
+
+ /**
+ * Convenience method to get the params when registering stats.
+ */
+ const StackDistCalcParams* params() const
+ { return reinterpret_cast<const StackDistCalcParams*>(_params); }
+
+ StackDistCalc(const StackDistCalcParams* p);
+
+ ~StackDistCalc();
+
+ void regStats();
+
+ /**
+ * Update the tree and the statistics.
+ *
+ * @param cmd Command from the packet
+ * @param addr Address to put on the stack
+ */
+ void update(const MemCmd& cmd, Addr addr);
+
+ private:
+
+ /**
+ * Node which takes form of Leaf, INode or Root
+ */
+ struct Node{
+ // Sum of the left children
+ uint64_t sumLeft;
+
+ // Sum of the right children
+ uint64_t sumRight;
+
+ // Flag to indicate that sumLeft has gone from non-zero value to 0
+ bool discardLeft;
+
+ // Flag to indicate that sumRight has gone from non-zero value to 0
+ bool discardRight;
+
+ // Index of the current element in the Map
+ uint64_t nodeIndex;
+
+ // Pointer to the parent
+ Node* parent;
+
+ // Flag to mark the node as the right/left child
+ bool isLeftNode;
+
+ /**
+ * Flag to indicate if this address is marked. Used in case
+ * where stack distance of a touched address is required.
+ */
+ bool isMarked;
+
+ /**
+ * The discard flags are false by default they become true if
+ * the node is reached again in a future lookup.
+ */
+ Node() : sumLeft(0), sumRight(0), discardLeft(false),
+ discardRight(false), nodeIndex(0),
+ parent(nullptr), isLeftNode(true), isMarked(false)
+ { }
+ };
+
+ /**
+ * Internal counter for address accesses (unique and non-unique)
+ * This counter increments everytime the calcStackDist() method is
+ * called. This counter is used as a key for the hash- map at the
+ * leaf layer. Practically at every call to the calculator this
+ * counter is incremented and a new leaf node is added in the tree
+ * at the leaf layer using this counter value as the key.
+ */
+ uint64_t index;
+
+ // Binary tree of partial sums
+ TreeType tree;
+
+ // Hash map which returns last seen index of each address
+ AddressIndexMap aiMap;
+
+ // Keeps count of number of the next unique index for each
+ // level in the tree
+ std::vector<uint64_t> nextIndex;
+
+ // Dummy Stack for verification
+ std::vector<uint64_t> stack;
+
+ // Flag to enable verification of stack. (Slows down the simulation)
+ const bool verifyStack;
+
+ // Disable the linear histograms
+ const bool disableLinearHists;
+
+ // Disable the logarithmic histograms
+ const bool disableLogHists;
+
+ // Reads linear histogram
+ Stats::Histogram readLinearHist;
+
+ // Reads logarithmic histogram
+ Stats::SparseHistogram readLogHist;
+
+ // Writes linear histogram
+ Stats::Histogram writeLinearHist;
+
+ // Writes logarithmic histogram
+ Stats::SparseHistogram writeLogHist;
+
+};
+
+#endif //__STACK_DIST_CALC_HH__