gem5  v22.1.0.0
stack_dist_calc.cc
Go to the documentation of this file.
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 
38 #include "mem/stack_dist_calc.hh"
39 
40 #include "base/chunk_generator.hh"
41 #include "base/intmath.hh"
42 #include "base/logging.hh"
43 #include "base/trace.hh"
44 #include "debug/StackDist.hh"
45 
46 namespace gem5
47 {
48 
49 StackDistCalc::StackDistCalc(bool verify_stack)
50  : index(0),
51  verifyStack(verify_stack)
52 {
53  // Instantiate a new root and leaf layer
54  // Map type variable, representing a layer in the tree
55  IndexNodeMap tree_level;
56 
57  // Initialize tree count for leaves
58  nextIndex.push_back(0);
59 
60  // Add the initial leaf layer to the tree
61  tree.push_back(tree_level);
62 
63  // Create a root node. Node type variable in the topmost layer
64  Node* root_node = new Node();
65 
66  // Initialize tree count for root
67  nextIndex.push_back(1);
68 
69  // Add the empty root layer to the tree
70  tree.push_back(tree_level);
71 
72  // Add the initial root to the tree
73  tree[1][root_node->nodeIndex] = root_node;
74 }
75 
77 {
78  // Walk through each layer and destroy the nodes
79  for (auto& layer : tree) {
80  for (auto& index_node : layer) {
81  // each map entry contains an index and a node
82  delete index_node.second;
83  }
84  // Clear each layer in the tree
85  layer.clear();
86  }
87 
88  // Clear the tree
89  tree.clear();
90  aiMap.clear();
91  nextIndex.clear();
92 
93  // For verification
94  stack.clear();
95 }
96 
97 // The updateSum method is a recursive function which updates
98 // the node sums till the root. It also deletes the nodes that
99 // are not used anymore.
100 uint64_t
101 StackDistCalc::updateSum(Node* node, bool from_left,
102  uint64_t sum_from_below, uint64_t level,
103  uint64_t stack_dist, bool discard_node)
104 {
105  ++level;
106 
107  // Make a copy of the node variables and work on them
108  // as a node may be deleted by this function
109  uint64_t node_sum_l = node->sumLeft;
110  uint64_t node_sum_r = node->sumRight;
111  bool node_left = node->isLeftNode;
112  bool node_discard_left = node->discardLeft;
113  bool node_discard_right = node->discardRight;
114  uint64_t node_n_index = node->nodeIndex;
115  Node* node_parent_ptr = node->parent;
116 
117  // For verification
118  if (verifyStack) {
119  // This sanity check makes sure that the left_sum and
120  // right_sum of the node is not greater than the
121  // maximum possible value given by the leaves below it
122  // for example a node in layer 3 (tree[3]) can at most
123  // have 8 leaves (4 to the left and 4 to the right)
124  // thus left_sum and right_sum should be <= 4
125  panic_if(node_sum_l > (1 << (level - 1)),
126  "Error in sum left of level %ul, node index %ull, "
127  "Sum = %ull \n", level, node_n_index, node_sum_l);
128 
129  panic_if(node_sum_r > (1 << (level - 1)),
130  "Error in sum right of level %ul, node index %ull, "
131  "Sum = %ull \n", level, node_n_index, node_sum_r);
132  }
133 
134  // Update the left sum or the right sum depending on the
135  // from_left flag. Variable stack_dist is updated only
136  // when arriving from Left.
137  if (from_left) {
138  // update sumLeft
139  node_sum_l = sum_from_below;
140  stack_dist += node_sum_r;
141  } else {
142  // update sum_r
143  node_sum_r = sum_from_below;
144  }
145 
146  // sum_from_below == 0 can be a leaf discard operation
147  if (discard_node && !sum_from_below) {
148  if (from_left)
149  node_discard_left = true;
150  else
151  node_discard_right = true;
152  }
153 
154  // Update the node variables with new values
155  node->nodeIndex = node_n_index;
156  node->sumLeft = node_sum_l;
157  node->sumRight = node_sum_r;
158  node->isLeftNode = node_left;
159  node->discardLeft = node_discard_left;
160  node->discardRight = node_discard_right;
161 
162  // Delete the node if it is not required anymore
163  if (node_discard_left && node_discard_right &&
164  discard_node && node_parent_ptr && !sum_from_below) {
165  delete node;
166  tree[level].erase(node_n_index);
167  discard_node = true;
168  } else {
169  // propogate discard_node as false upwards if the
170  // above conditions are not met.
171  discard_node = false;
172  }
173 
174  // Recursively call the updateSum operation till the
175  // root node is reached
176  if (node_parent_ptr) {
177  stack_dist = updateSum(node_parent_ptr, node_left,
178  node_sum_l + node_sum_r,
179  level, stack_dist, discard_node);
180  }
181 
182  return stack_dist;
183 }
184 
185 // This function is called by the calcStackDistAndUpdate function
186 // If is_new_leaf is true then a new leaf is added otherwise a leaf
187 // removed from the tree. In both cases the tree is updated using
188 // the updateSum operation.
189 uint64_t
191 {
192  uint64_t level = 0;
193  uint64_t stack_dist = 0;
194 
195  if (is_new_leaf) {
196  node->sumLeft = 1;
197  updateSum(node->parent,
198  node->isLeftNode, node->sumLeft,
199  level, 0, false);
200 
201  stack_dist = Infinity;
202  } else {
203  node->sumLeft = 0;
204  stack_dist = updateSum(node->parent,
205  node->isLeftNode, 0,
206  level, stack_dist, true);
207  }
208 
209  return stack_dist;
210 }
211 
212 // This method is a recursive function which calculates
213 // the node sums till the root.
214 uint64_t
215 StackDistCalc::getSum(Node* node, bool from_left, uint64_t sum_from_below,
216  uint64_t stack_dist, uint64_t level) const
217 {
218  ++level;
219  // Variable stack_dist is updated only
220  // when arriving from Left.
221  if (from_left) {
222  stack_dist += node->sumRight;
223  }
224 
225  // Recursively call the getSum operation till the
226  // root node is reached
227  if (node->parent) {
228  stack_dist = getSum(node->parent, node->isLeftNode,
229  node->sumLeft + node->sumRight,
230  stack_dist, level);
231  }
232 
233  return stack_dist;
234 }
235 
236 // This function is called by the calcStackDistance function
237 uint64_t
239 {
240  return getSum(node->parent, node->isLeftNode, 0, 0, 0);
241 }
242 
243 // Update tree is a tree balancing operation which maintains
244 // the binary tree structure. This method is called whenever
245 // index%2 == 0 (i.e. every alternate cycle)
246 // The two main operation are :
247 // OP1. moving the root node one layer up if index counter
248 // crosses power of 2
249 // OP2. Addition of intermediate nodes as and when required
250 // and linking them to their parents in the layer above.
251 void
253 {
254  uint64_t i;
255 
256  if (isPowerOf2(index)) {
257  // OP1. moving the root node one layer up if index counter
258  // crosses power of 2
259  // If index counter crosses a power of 2, then add a
260  // new tree layer above and create a new Root node in it.
261  // After the root is created the old node
262  // in the layer below is updated to point to this
263  // newly created root node. The sum_l of this new root node
264  // becomes the sum_l + sum_r of the old node.
265  //
266  // After this root update operation a chain of intermediate
267  // nodes is created from root layer to tree[1](one layer
268  // above the leaf layer)
269 
270  // Create a new root node
271  Node* newRootNode = new Node();
272 
273  // Update its sum_l as the sum_l+sum_r from below
274  newRootNode->sumLeft = tree[getTreeDepth()][0]->sumRight +
275  tree[getTreeDepth()][0]->sumLeft;
276  // Update its discard left flag if the node below has
277  // has both discardLeft and discardRight set.
278  newRootNode->discardLeft = tree[getTreeDepth()][0]->discardLeft &&
279  tree[getTreeDepth()][0]->discardRight;
280 
281  // Map type variable, representing a layer in the tree
282  IndexNodeMap treeLevel;
283  // Add a new layer to the tree
284  tree.push_back(treeLevel);
285  nextIndex.push_back(1);
286  tree[getTreeDepth()][newRootNode->nodeIndex] = newRootNode;
287 
288  // Update the parent pointer at lower layer to
289  // point to newly created root node
290  tree[getTreeDepth() - 1][0]->parent = tree[getTreeDepth()][0];
291 
292  // Add intermediate nodes from root till bottom (one layer above the
293  // leaf layer)
294  for (i = getTreeDepth() - 1; i >= 1; --i) {
295  Node* newINode = new Node();
296  // newNode is left or right child depending on the number of nodes
297  // in that layer
298  if (nextIndex[i] % 2 == 0) {
299  newINode->isLeftNode = true;
300  } else {
301  newINode->isLeftNode = false;
302  }
303 
304  newINode->parent = tree[i + 1][nextIndex[i + 1] - 1];
305  newINode->nodeIndex = ++nextIndex[i] - 1;
306  tree[i][newINode->nodeIndex] = newINode;
307  }
308  } else {
309  // OP2. Addition of intermediate nodes as and when required
310  // and linking them to their parents in the layer above.
311  //
312  // At layer 1 a new INode is added whenever index%(2^1)==0
313  // (multiples of 2)
314  //
315  // At layer 2 a new INode is added whenever index%(2^2)==0
316  // (multiples of 4)
317  //
318  // At layer 3 a new INode is added whenever index%(2^3)==0
319  // (multiples of 8)
320  //...
321  //
322  // At layer N a new INode is added whenever index%(2^N)==0
323  // (multiples of 2^N)
324  for (i = getTreeDepth() - 1; i >= 1; --i) {
325  // Traverse each layer from root to leaves and add a new
326  // intermediate node if required. Link the parent_ptr of
327  // the new node to the parent in the above layer.
328 
329  if ((index % (1 << i)) == 0) {
330  // Checks if current (index % 2^treeDepth) == 0 if true
331  // a new node at that layer is created
332  Node* newINode = new Node();
333 
334  // newNode is left or right child depending on the
335  // number of nodes in that layer.
336  if (nextIndex[i] % 2 == 0) {
337  newINode->isLeftNode = true;
338  } else {
339  newINode->isLeftNode = false;
340  }
341 
342  // Pointing to its parent in the upper layer
343  newINode->parent = tree[i + 1][nextIndex[i + 1] - 1];
344  newINode->nodeIndex = ++nextIndex[i] - 1;
345  tree[i][newINode->nodeIndex] = newINode;
346  }
347  }
348  }
349 }
350 
351 // This function is called everytime to get the stack distance and add
352 // a new node. A feature to mark an old node in the tree is
353 // added. This is useful if it is required to see the reuse
354 // pattern. For example, BackInvalidates from the lower level (Membus)
355 // to L2, can be marked (isMarked flag of Node set to True). And then
356 // later if this same address is accessed by L1, the value of the
357 // isMarked flag would be True. This would give some insight on how
358 // the BackInvalidates policy of the lower level affect the read/write
359 // accesses in an application.
361 StackDistCalc::calcStackDistAndUpdate(const Addr r_address, bool addNewNode)
362 {
363  Node* newLeafNode;
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  uint64_t 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
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.
463 StackDistCalc::calcStackDist(const Addr r_address, bool mark)
464 {
465  // Default value of isMarked flag for each node.
466  bool _mark = false;
467 
468  auto ai = aiMap.lower_bound(r_address);
469 
470  // By default stackDistacne is treated as infinity
471  uint64_t stack_dist = 0;
472 
473  // Lookup aiMap by giving address as the key:
474  // If found take address and Lookup in tree
475  // Add sums to right till root
476  // Stack Distance of that address sums to right
477  if (ai != aiMap.end() && !(aiMap.key_comp()(r_address, ai->first))) {
478  // key already exists
479  // save the index counter value when this address was
480  // encountered before
481  uint64_t r_index = ai->second;
482 
483  // Get the value of mark flag if previously marked
484  _mark = tree[0][r_index]->isMarked;
485  // Mark the leaf node if required
486  tree[0][r_index]->isMarked = mark;
487 
488  // Call get sums operation on the tree starting with
489  // the r_index value found above. This function would return
490  // the value of the stack distcance.
491  stack_dist = getSumsLeavesToRoot(tree[0][r_index]);
492  } else {
493  // Update infinity bin count
494  // By default stackDistacne is treated as infinity
495  stack_dist = Infinity;
496  }
497 
498  // For verification
499  if (verifyStack) {
500  // Calculate the SD of the same address in the debug stack
501  uint64_t verify_stack_dist = verifyStackDist(r_address);
502  panic_if(verify_stack_dist != stack_dist,
503  "Expected stack-distance for address \
504  %#lx is %#lx but found %#lx",
505  r_address, verify_stack_dist, stack_dist);
506 
507  printStack();
508  }
509 
510  return std::make_pair(stack_dist, _mark);
511 }
512 
513 // For verification
514 // Simple sanity check for the tree
515 void
516 StackDistCalc::sanityCheckTree(const Node* node, uint64_t level) const
517 {
518  const Node* next_up = node->parent;
519 
520  for (uint64_t i = level + 1; i < getTreeDepth() - level; ++i) {
521  next_up = next_up->parent;
522  panic_if(!next_up, "Sanity check failed for node %ull \n",
523  node->nodeIndex);
524  }
525 
526  // At the root layer the parent_ptr should be null
527  panic_if(next_up->parent, "Sanity check failed for node %ull \n",
528  node->nodeIndex);
529 }
530 
531 // This method can be called to compute the stack distance in a naive
532 // way It can be used to verify the functionality of the stack
533 // distance calculator. It uses std::vector to compute the stack
534 // distance using a naive stack.
535 uint64_t
536 StackDistCalc::verifyStackDist(const Addr r_address, bool update_stack)
537 {
538  bool found = false;
539  uint64_t stack_dist = 0;
540  auto a = stack.rbegin();
541 
542  for (; a != stack.rend(); ++a) {
543  if (*a == r_address) {
544  found = true;
545  break;
546  } else {
547  ++stack_dist;
548  }
549  }
550 
551  if (found) {
552  ++a;
553  if (update_stack)
554  stack.erase(a.base());
555  } else {
556  stack_dist = Infinity;
557  }
558 
559  if (update_stack)
560  stack.push_back(r_address);
561 
562  return stack_dist;
563 }
564 
565 // This method is useful to print top n entities in the stack.
566 void
568 {
569  Node* node;
570  int count = 0;
571 
572  DPRINTF(StackDist, "Printing last %d entries in tree\n", n);
573 
574  // Walk through leaf layer to display the last n nodes
575  for (auto it = tree[0].rbegin(); (count < n) && (it != tree[0].rend());
576  ++it, ++count) {
577  node = it->second;
578  uint64_t r_index = node->nodeIndex;
579 
580  // Lookup aiMap using the index returned by the leaf iterator
581  for (auto ai = aiMap.rbegin(); ai != aiMap.rend(); ++ai) {
582  if (ai->second == r_index) {
583  DPRINTF(StackDist,"Tree leaves, Rightmost-[%d] = %#lx\n",
584  count, ai->first);
585  break;
586  }
587  }
588  }
589 
590  DPRINTF(StackDist,"Tree depth = %#ld\n", getTreeDepth());
591 
592  if (verifyStack) {
593  DPRINTF(StackDist,"Printing Last %d entries in VerifStack \n", n);
594  count = 0;
595  for (auto a = stack.rbegin(); (count < n) && (a != stack.rend());
596  ++a, ++count) {
597  DPRINTF(StackDist, "Verif Stack, Top-[%d] = %#lx\n", count, *a);
598  }
599  }
600 }
601 
602 } // namespace gem5
#define DPRINTF(x,...)
Definition: trace.hh:186
Declaration and inline definition of ChunkGenerator object.
std::map< uint64_t, Node * > IndexNodeMap
std::vector< uint64_t > nextIndex
void updateTree()
updateTree is a tree balancing operation, which maintains the binary tree structure.
uint64_t getTreeDepth() const
Query depth of the tree (tree[0] represents leaf layer while tree[treeDepth] represents the root laye...
std::pair< uint64_t, bool > calcStackDist(const Addr r_address, bool mark=false)
Process the given address.
uint64_t getSumsLeavesToRoot(Node *node) const
Gets the sum from the leaf node specified.
AddressIndexMap aiMap
static constexpr uint64_t Infinity
A convenient way of refering to infinity.
uint64_t getSum(Node *node, bool from_left, uint64_t sum_from_below, uint64_t stack_dist, uint64_t level) const
Gets sum from the node upwards recursively till the root.
uint64_t updateSumsLeavesToRoot(Node *node, bool is_new_leaf)
Updates the leaf nodes and nodes above.
std::pair< uint64_t, bool > calcStackDistAndUpdate(const Addr r_address, bool addNewNode=true)
Process the given address:
uint64_t verifyStackDist(const Addr r_address, bool update_stack=false)
This is an alternative implementation of the stack-distance in a naive way.
void sanityCheckTree(const Node *node, uint64_t level=0) const
This method is used for verification purposes It recursively traverses upwards from the given node ti...
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 nodes upwards recursively till the root.
StackDistCalc(bool verify_stack=false)
std::vector< uint64_t > stack
void printStack(int n=5) const
Print the last n items on the stack.
uint64_t index
Internal counter for address accesses (unique and non-unique) This counter increments everytime the c...
STL pair class.
Definition: stl.hh:58
static constexpr bool isPowerOf2(const T &n)
Definition: intmath.hh:98
#define panic_if(cond,...)
Conditional panic macro that checks the supplied condition and only panics if the condition is true a...
Definition: logging.hh:204
Bitfield< 31 > n
Definition: misc_types.hh:462
Bitfield< 7 > i
Definition: misc_types.hh:67
Bitfield< 8 > a
Definition: misc_types.hh:66
Bitfield< 30, 0 > index
Bitfield< 20 > level
Definition: intmessage.hh:51
Reference material can be found at the JEDEC website: UFS standard http://www.jedec....
uint64_t Addr
Address type This will probably be moved somewhere else in the near future.
Definition: types.hh:147
Node which takes form of Leaf, INode or Root.
bool isMarked
Flag to indicate if this address is marked.

Generated on Wed Dec 21 2022 10:22:39 for gem5 by doxygen 1.9.1