| /* |
| * Copyright (c) 2014-2015 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 "mem/stack_dist_calc.hh" |
| |
| #include "base/chunk_generator.hh" |
| #include "base/intmath.hh" |
| #include "base/trace.hh" |
| #include "debug/StackDist.hh" |
| |
| StackDistCalc::StackDistCalc(bool verify_stack) |
| : index(0), |
| verifyStack(verify_stack) |
| { |
| // 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(); |
| } |
| |
| // 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; |
| |
| 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 |
| uint64_t 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) |
| { |
| // 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 |
| uint64_t 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; |
| 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; |
| uint64_t 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); |
| } |
| } |
| } |