stack_dist_calc.cc revision 10995:a114e2712642
1/* 2 * Copyright (c) 2014-2015 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Kanishk Sugand 38 */ 39 40#include "mem/stack_dist_calc.hh" 41 42#include "base/chunk_generator.hh" 43#include "base/intmath.hh" 44#include "base/trace.hh" 45#include "debug/StackDist.hh" 46 47StackDistCalc::StackDistCalc(bool verify_stack) 48 : index(0), 49 verifyStack(verify_stack) 50{ 51 // Instantiate a new root and leaf layer 52 // Map type variable, representing a layer in the tree 53 IndexNodeMap tree_level; 54 55 // Initialize tree count for leaves 56 nextIndex.push_back(0); 57 58 // Add the initial leaf layer to the tree 59 tree.push_back(tree_level); 60 61 // Create a root node. Node type variable in the topmost layer 62 Node* root_node = new Node(); 63 64 // Initialize tree count for root 65 nextIndex.push_back(1); 66 67 // Add the empty root layer to the tree 68 tree.push_back(tree_level); 69 70 // Add the initial root to the tree 71 tree[1][root_node->nodeIndex] = root_node; 72} 73 74StackDistCalc::~StackDistCalc() 75{ 76 // Walk through each layer and destroy the nodes 77 for (auto& layer : tree) { 78 for (auto& index_node : layer) { 79 // each map entry contains an index and a node 80 delete index_node.second; 81 } 82 // Clear each layer in the tree 83 layer.clear(); 84 } 85 86 // Clear the tree 87 tree.clear(); 88 aiMap.clear(); 89 nextIndex.clear(); 90 91 // For verification 92 stack.clear(); 93} 94 95// The updateSum method is a recursive function which updates 96// the node sums till the root. It also deletes the nodes that 97// are not used anymore. 98uint64_t 99StackDistCalc::updateSum(Node* node, bool from_left, 100 uint64_t sum_from_below, uint64_t level, 101 uint64_t stack_dist, bool discard_node) 102{ 103 ++level; 104 105 // Make a copy of the node variables and work on them 106 // as a node may be deleted by this function 107 uint64_t node_sum_l = node->sumLeft; 108 uint64_t node_sum_r = node->sumRight; 109 bool node_left = node->isLeftNode; 110 bool node_discard_left = node->discardLeft; 111 bool node_discard_right = node->discardRight; 112 uint64_t node_n_index = node->nodeIndex; 113 Node* node_parent_ptr = node->parent; 114 115 // For verification 116 if (verifyStack) { 117 // This sanity check makes sure that the left_sum and 118 // right_sum of the node is not greater than the 119 // maximum possible value given by the leaves below it 120 // for example a node in layer 3 (tree[3]) can at most 121 // have 8 leaves (4 to the left and 4 to the right) 122 // thus left_sum and right_sum should be <= 4 123 panic_if(node_sum_l > (1 << (level - 1)), 124 "Error in sum left of level %ul, node index %ull, " 125 "Sum = %ull \n", level, node_n_index, node_sum_l); 126 127 panic_if(node_sum_r > (1 << (level - 1)), 128 "Error in sum right of level %ul, node index %ull, " 129 "Sum = %ull \n", level, node_n_index, node_sum_r); 130 } 131 132 // Update the left sum or the right sum depending on the 133 // from_left flag. Variable stack_dist is updated only 134 // when arriving from Left. 135 if (from_left) { 136 // update sumLeft 137 node_sum_l = sum_from_below; 138 stack_dist += node_sum_r; 139 } else { 140 // update sum_r 141 node_sum_r = sum_from_below; 142 } 143 144 // sum_from_below == 0 can be a leaf discard operation 145 if (discard_node && !sum_from_below) { 146 if (from_left) 147 node_discard_left = true; 148 else 149 node_discard_right = true; 150 } 151 152 // Update the node variables with new values 153 node->nodeIndex = node_n_index; 154 node->sumLeft = node_sum_l; 155 node->sumRight = node_sum_r; 156 node->isLeftNode = node_left; 157 node->discardLeft = node_discard_left; 158 node->discardRight = node_discard_right; 159 160 // Delete the node if it is not required anymore 161 if (node_discard_left && node_discard_right && 162 discard_node && node_parent_ptr && !sum_from_below) { 163 delete node; 164 tree[level].erase(node_n_index); 165 discard_node = true; 166 } else { 167 // propogate discard_node as false upwards if the 168 // above conditions are not met. 169 discard_node = false; 170 } 171 172 // Recursively call the updateSum operation till the 173 // root node is reached 174 if (node_parent_ptr) { 175 stack_dist = updateSum(node_parent_ptr, node_left, 176 node_sum_l + node_sum_r, 177 level, stack_dist, discard_node); 178 } 179 180 return stack_dist; 181} 182 183// This function is called by the calcStackDistAndUpdate function 184// If is_new_leaf is true then a new leaf is added otherwise a leaf 185// removed from the tree. In both cases the tree is updated using 186// the updateSum operation. 187uint64_t 188StackDistCalc::updateSumsLeavesToRoot(Node* node, bool is_new_leaf) 189{ 190 uint64_t level = 0; 191 uint64_t stack_dist = 0; 192 193 if (is_new_leaf) { 194 node->sumLeft = 1; 195 updateSum(node->parent, 196 node->isLeftNode, node->sumLeft, 197 level, 0, false); 198 199 stack_dist = Infinity; 200 } else { 201 node->sumLeft = 0; 202 stack_dist = updateSum(node->parent, 203 node->isLeftNode, 0, 204 level, stack_dist, true); 205 } 206 207 return stack_dist; 208} 209 210// This method is a recursive function which calculates 211// the node sums till the root. 212uint64_t 213StackDistCalc::getSum(Node* node, bool from_left, uint64_t sum_from_below, 214 uint64_t stack_dist, uint64_t level) const 215{ 216 ++level; 217 // Variable stack_dist is updated only 218 // when arriving from Left. 219 if(from_left) { 220 stack_dist += node->sumRight; 221 } 222 223 // Recursively call the getSum operation till the 224 // root node is reached 225 if(node->parent) { 226 stack_dist = getSum(node->parent, node->isLeftNode, 227 node->sumLeft + node->sumRight, 228 stack_dist, level); 229 } 230 231 return stack_dist; 232} 233 234// This function is called by the calcStackDistance function 235uint64_t 236StackDistCalc::getSumsLeavesToRoot(Node* node) const 237{ 238 return getSum(node->parent, node->isLeftNode, 0, 0, 0); 239} 240 241// Update tree is a tree balancing operation which maintains 242// the binary tree structure. This method is called whenever 243// index%2 == 0 (i.e. every alternate cycle) 244// The two main operation are : 245// OP1. moving the root node one layer up if index counter 246// crosses power of 2 247// OP2. Addition of intermediate nodes as and when required 248// and linking them to their parents in the layer above. 249void 250StackDistCalc::updateTree() 251{ 252 uint64_t i; 253 254 if (isPowerOf2(index)) { 255 // OP1. moving the root node one layer up if index counter 256 // crosses power of 2 257 // If index counter crosses a power of 2, then add a 258 // new tree layer above and create a new Root node in it. 259 // After the root is created the old node 260 // in the layer below is updated to point to this 261 // newly created root node. The sum_l of this new root node 262 // becomes the sum_l + sum_r of the old node. 263 // 264 // After this root update operation a chain of intermediate 265 // nodes is created from root layer to tree[1](one layer 266 // above the leaf layer) 267 268 // Create a new root node 269 Node* newRootNode = new Node(); 270 271 // Update its sum_l as the sum_l+sum_r from below 272 newRootNode->sumLeft = tree[getTreeDepth()][0]->sumRight + 273 tree[getTreeDepth()][0]->sumLeft; 274 // Update its discard left flag if the node below has 275 // has both discardLeft and discardRight set. 276 newRootNode->discardLeft = tree[getTreeDepth()][0]->discardLeft && 277 tree[getTreeDepth()][0]->discardRight; 278 279 // Map type variable, representing a layer in the tree 280 IndexNodeMap treeLevel; 281 // Add a new layer to the tree 282 tree.push_back(treeLevel); 283 nextIndex.push_back(1); 284 tree[getTreeDepth()][newRootNode->nodeIndex] = newRootNode; 285 286 // Update the parent pointer at lower layer to 287 // point to newly created root node 288 tree[getTreeDepth() - 1][0]->parent = tree[getTreeDepth()][0]; 289 290 // Add intermediate nodes from root till bottom (one layer above the 291 // leaf layer) 292 for (i = getTreeDepth() - 1; i >= 1; --i) { 293 Node* newINode = new Node(); 294 // newNode is left or right child depending on the number of nodes 295 // in that layer 296 if (nextIndex[i] % 2 == 0) { 297 newINode->isLeftNode = true; 298 } else { 299 newINode->isLeftNode = false; 300 } 301 302 newINode->parent = tree[i + 1][nextIndex[i + 1] - 1]; 303 newINode->nodeIndex = ++nextIndex[i] - 1; 304 tree[i][newINode->nodeIndex] = newINode; 305 } 306 } else { 307 // OP2. Addition of intermediate nodes as and when required 308 // and linking them to their parents in the layer above. 309 // 310 // At layer 1 a new INode is added whenever index%(2^1)==0 311 // (multiples of 2) 312 // 313 // At layer 2 a new INode is added whenever index%(2^2)==0 314 // (multiples of 4) 315 // 316 // At layer 3 a new INode is added whenever index%(2^3)==0 317 // (multiples of 8) 318 //... 319 // 320 // At layer N a new INode is added whenever index%(2^N)==0 321 // (multiples of 2^N) 322 for (i = getTreeDepth() - 1; i >= 1; --i) { 323 // Traverse each layer from root to leaves and add a new 324 // intermediate node if required. Link the parent_ptr of 325 // the new node to the parent in the above layer. 326 327 if ((index % (1 << i)) == 0) { 328 // Checks if current (index % 2^treeDepth) == 0 if true 329 // a new node at that layer is created 330 Node* newINode = new Node(); 331 332 // newNode is left or right child depending on the 333 // number of nodes in that layer. 334 if (nextIndex[i] % 2 == 0) { 335 newINode->isLeftNode = true; 336 } else { 337 newINode->isLeftNode = false; 338 } 339 340 // Pointing to its parent in the upper layer 341 newINode->parent = tree[i + 1][nextIndex[i + 1] - 1]; 342 newINode->nodeIndex = ++nextIndex[i] - 1; 343 tree[i][newINode->nodeIndex] = newINode; 344 } 345 } 346 } 347} 348 349// This function is called everytime to get the stack distance and add 350// a new node. A feature to mark an old node in the tree is 351// added. This is useful if it is required to see the reuse 352// pattern. For example, BackInvalidates from the lower level (Membus) 353// to L2, can be marked (isMarked flag of Node set to True). And then 354// later if this same address is accessed by L1, the value of the 355// isMarked flag would be True. This would give some insight on how 356// the BackInvalidates policy of the lower level affect the read/write 357// accesses in an application. 358std::pair< uint64_t, bool> 359StackDistCalc::calcStackDistAndUpdate(const Addr r_address, bool addNewNode) 360{ 361 Node* newLeafNode; 362 // Return index if the address was already present in stack 363 uint64_t r_index = index; 364 365 auto ai = aiMap.lower_bound(r_address); 366 367 // Default value of flag indicating as the left or right leaf 368 bool isLeft = true; 369 // Default value of isMarked flag for each node. 370 bool _mark = false; 371 // By default stackDistacne is treated as infinity 372 uint64_t stack_dist; 373 374 // Lookup aiMap by giving address as the key: 375 // If found take address and Lookup in tree 376 // Update tree from leaves by making B(found index) = 0 377 // Add sums to right till root while Updating them 378 // Stack Distance of that address sums to right 379 if (ai != aiMap.end() && !(aiMap.key_comp()(r_address, ai->first))) { 380 // key already exists 381 // save the index counter value when this address was 382 // encountered before and update it to the current index value 383 r_index = ai->second; 384 385 if (addNewNode) { 386 // Update aiMap aiMap(Address) = current index 387 ai->second = index; 388 } else { 389 aiMap.erase(r_address); 390 } 391 392 // Call update tree operation on the tree starting with 393 // the r_index value found above. This function would return 394 // the value of the stack distcance. 395 stack_dist = updateSumsLeavesToRoot(tree[0][r_index], false); 396 newLeafNode = tree[0][r_index]; 397 // determine if this node was marked earlier 398 _mark = newLeafNode->isMarked; 399 delete newLeafNode; 400 tree[0].erase(r_index); 401 } else { 402 if (addNewNode) { 403 // Update aiMap aiMap(Address) = current index 404 aiMap[r_address] = index; 405 } 406 // Update infinity bin count 407 // By default stackDistacne is treated as infinity 408 stack_dist = Infinity; 409 } 410 411 if (addNewNode) { 412 // If index%2 == 0 then update tree 413 if (index % 2 == 0) { 414 updateTree(); 415 } else { 416 // At odd values of index counter, a new right-type node is 417 // added to the leaf layer, else a left-type node is added 418 isLeft = false; 419 } 420 421 // Add new leaf node in the leaf layer (tree[0]) 422 // set n_index = current index 423 newLeafNode = new Node(); 424 ++nextIndex[0]; 425 newLeafNode->nodeIndex=index; 426 newLeafNode->isLeftNode=isLeft; 427 // Point the parent pointer to the intermediate node above 428 newLeafNode->parent = tree[1][nextIndex[1] - 1]; 429 tree[0][index] = newLeafNode; 430 // call an update operation which would update the tree after 431 // addition of this new leaf node. 432 updateSumsLeavesToRoot(tree[0][index], true); 433 434 // For verification 435 if (verifyStack) { 436 // This function checks the sanity of the tree to make sure the 437 // last node in the link of parent pointers is the root node. 438 // It takes a leaf node as an argument and traveses upwards till 439 // the root layer to check if the last parent is null 440 sanityCheckTree(tree[0][index]); 441 442 // Push the same element in debug stack, and check 443 uint64_t verify_stack_dist = verifyStackDist(r_address, true); 444 panic_if(verify_stack_dist != stack_dist, 445 "Expected stack-distance for address \ 446 %#lx is %#lx but found %#lx", 447 r_address, verify_stack_dist, stack_dist); 448 printStack(); 449 } 450 451 // The index counter is updated at the end of each transaction 452 // (unique or non-unique) 453 ++index; 454 } 455 456 return (std::make_pair(stack_dist, _mark)); 457} 458 459// This function is called everytime to get the stack distance 460// no new node is added. It can be used to mark a previous access 461// and inspect the value of the mark flag. 462std::pair< uint64_t, bool> 463StackDistCalc::calcStackDist(const Addr r_address, bool mark) 464{ 465 // Return index if the address was already present in stack 466 uint64_t r_index = index; 467 // Default value of isMarked flag for each node. 468 bool _mark = false; 469 470 auto ai = aiMap.lower_bound(r_address); 471 472 // By default stackDistacne is treated as infinity 473 uint64_t stack_dist = 0; 474 475 // Lookup aiMap by giving address as the key: 476 // If found take address and Lookup in tree 477 // Add sums to right till root 478 // Stack Distance of that address sums to right 479 if (ai != aiMap.end() && !(aiMap.key_comp()(r_address, ai->first))) { 480 // key already exists 481 // save the index counter value when this address was 482 // encountered before 483 r_index = ai->second; 484 485 // Get the value of mark flag if previously marked 486 _mark = tree[0][r_index]->isMarked; 487 // Mark the leaf node if required 488 tree[0][r_index]->isMarked = mark; 489 490 // Call get sums operation on the tree starting with 491 // the r_index value found above. This function would return 492 // the value of the stack distcance. 493 stack_dist = getSumsLeavesToRoot(tree[0][r_index]); 494 } else { 495 // Update infinity bin count 496 // By default stackDistacne is treated as infinity 497 stack_dist = Infinity; 498 } 499 500 // For verification 501 if (verifyStack) { 502 // Calculate the SD of the same address in the debug stack 503 uint64_t verify_stack_dist = verifyStackDist(r_address); 504 panic_if(verify_stack_dist != stack_dist, 505 "Expected stack-distance for address \ 506 %#lx is %#lx but found %#lx", 507 r_address, verify_stack_dist, stack_dist); 508 509 printStack(); 510 } 511 512 return std::make_pair(stack_dist, _mark); 513} 514 515// For verification 516// Simple sanity check for the tree 517void 518StackDistCalc::sanityCheckTree(const Node* node, uint64_t level) const 519{ 520 const Node* next_up = node->parent; 521 522 for (uint64_t i = level + 1; i < getTreeDepth() - level; ++i) { 523 next_up = next_up->parent; 524 panic_if(!next_up, "Sanity check failed for node %ull \n", 525 node->nodeIndex); 526 } 527 528 // At the root layer the parent_ptr should be null 529 panic_if(next_up->parent, "Sanity check failed for node %ull \n", 530 node->nodeIndex); 531} 532 533// This method can be called to compute the stack distance in a naive 534// way It can be used to verify the functionality of the stack 535// distance calculator. It uses std::vector to compute the stack 536// distance using a naive stack. 537uint64_t 538StackDistCalc::verifyStackDist(const Addr r_address, bool update_stack) 539{ 540 bool found = false; 541 uint64_t stack_dist = 0; 542 auto a = stack.rbegin(); 543 544 for (; a != stack.rend(); ++a) { 545 if (*a == r_address) { 546 found = true; 547 break; 548 } else { 549 ++stack_dist; 550 } 551 } 552 553 if (found) { 554 ++a; 555 if (update_stack) 556 stack.erase(a.base()); 557 } else { 558 stack_dist = Infinity; 559 } 560 561 if (update_stack) 562 stack.push_back(r_address); 563 564 return stack_dist; 565} 566 567// This method is useful to print top n entities in the stack. 568void 569StackDistCalc::printStack(int n) const 570{ 571 Node* node; 572 uint64_t r_index; 573 int count = 0; 574 575 DPRINTF(StackDist, "Printing last %d entries in tree\n", n); 576 577 // Walk through leaf layer to display the last n nodes 578 for (auto it = tree[0].rbegin(); (count < n) && (it != tree[0].rend()); 579 ++it, ++count) { 580 node = it->second; 581 r_index = node->nodeIndex; 582 583 // Lookup aiMap using the index returned by the leaf iterator 584 for (auto ai = aiMap.rbegin(); ai != aiMap.rend(); ++ai) { 585 if (ai->second == r_index) { 586 DPRINTF(StackDist,"Tree leaves, Rightmost-[%d] = %#lx\n", 587 count, ai->first); 588 break; 589 } 590 } 591 } 592 593 DPRINTF(StackDist,"Tree depth = %#ld\n", getTreeDepth()); 594 595 if (verifyStack) { 596 DPRINTF(StackDist,"Printing Last %d entries in VerifStack \n", n); 597 count = 0; 598 for (auto a = stack.rbegin(); (count < n) && (a != stack.rend()); 599 ++a, ++count) { 600 DPRINTF(StackDist, "Verif Stack, Top-[%d] = %#lx\n", count, *a); 601 } 602 } 603} 604