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