blob: 41ab6e94fd7020512698677ec7161fe5dd25b1e9 [file] [log] [blame]
/*
* Copyright (c) 2012 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.
*
* Copyright (c) 2006 The Regents of The University of Michigan
* All rights reserved.
*
* 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: Kevin Lim
*/
#ifndef __CPU_O3_DEP_GRAPH_HH__
#define __CPU_O3_DEP_GRAPH_HH__
#include "cpu/o3/comm.hh"
/** Node in a linked list. */
template <class DynInstPtr>
class DependencyEntry
{
public:
DependencyEntry()
: inst(NULL), next(NULL)
{ }
DynInstPtr inst;
//Might want to include data about what arch. register the
//dependence is waiting on.
DependencyEntry<DynInstPtr> *next;
};
/** Array of linked list that maintains the dependencies between
* producing instructions and consuming instructions. Each linked
* list represents a single physical register, having the future
* producer of the register's value, and all consumers waiting on that
* value on the list. The head node of each linked list represents
* the producing instruction of that register. Instructions are put
* on the list upon reaching the IQ, and are removed from the list
* either when the producer completes, or the instruction is squashed.
*/
template <class DynInstPtr>
class DependencyGraph
{
public:
typedef DependencyEntry<DynInstPtr> DepEntry;
/** Default construction. Must call resize() prior to use. */
DependencyGraph()
: numEntries(0), memAllocCounter(0), nodesTraversed(0), nodesRemoved(0)
{ }
~DependencyGraph();
/** Resize the dependency graph to have num_entries registers. */
void resize(int num_entries);
/** Clears all of the linked lists. */
void reset();
/** Inserts an instruction to be dependent on the given index. */
void insert(PhysRegIndex idx, DynInstPtr &new_inst);
/** Sets the producing instruction of a given register. */
void setInst(PhysRegIndex idx, DynInstPtr &new_inst)
{ dependGraph[idx].inst = new_inst; }
/** Clears the producing instruction. */
void clearInst(PhysRegIndex idx)
{ dependGraph[idx].inst = NULL; }
/** Removes an instruction from a single linked list. */
void remove(PhysRegIndex idx, DynInstPtr &inst_to_remove);
/** Removes and returns the newest dependent of a specific register. */
DynInstPtr pop(PhysRegIndex idx);
/** Checks if the entire dependency graph is empty. */
bool empty() const;
/** Checks if there are any dependents on a specific register. */
bool empty(PhysRegIndex idx) const { return !dependGraph[idx].next; }
/** Debugging function to dump out the dependency graph.
*/
void dump();
private:
/** Array of linked lists. Each linked list is a list of all the
* instructions that depend upon a given register. The actual
* register's index is used to index into the graph; ie all
* instructions in flight that are dependent upon r34 will be
* in the linked list of dependGraph[34].
*/
DepEntry *dependGraph;
/** Number of linked lists; identical to the number of registers. */
int numEntries;
// Debug variable, remove when done testing.
unsigned memAllocCounter;
public:
// Debug variable, remove when done testing.
uint64_t nodesTraversed;
// Debug variable, remove when done testing.
uint64_t nodesRemoved;
};
template <class DynInstPtr>
DependencyGraph<DynInstPtr>::~DependencyGraph()
{
delete [] dependGraph;
}
template <class DynInstPtr>
void
DependencyGraph<DynInstPtr>::resize(int num_entries)
{
numEntries = num_entries;
dependGraph = new DepEntry[numEntries];
}
template <class DynInstPtr>
void
DependencyGraph<DynInstPtr>::reset()
{
// Clear the dependency graph
DepEntry *curr;
DepEntry *prev;
for (int i = 0; i < numEntries; ++i) {
curr = dependGraph[i].next;
while (curr) {
memAllocCounter--;
prev = curr;
curr = prev->next;
prev->inst = NULL;
delete prev;
}
if (dependGraph[i].inst) {
dependGraph[i].inst = NULL;
}
dependGraph[i].next = NULL;
}
}
template <class DynInstPtr>
void
DependencyGraph<DynInstPtr>::insert(PhysRegIndex idx, DynInstPtr &new_inst)
{
//Add this new, dependent instruction at the head of the dependency
//chain.
// First create the entry that will be added to the head of the
// dependency chain.
DepEntry *new_entry = new DepEntry;
new_entry->next = dependGraph[idx].next;
new_entry->inst = new_inst;
// Then actually add it to the chain.
dependGraph[idx].next = new_entry;
++memAllocCounter;
}
template <class DynInstPtr>
void
DependencyGraph<DynInstPtr>::remove(PhysRegIndex idx,
DynInstPtr &inst_to_remove)
{
DepEntry *prev = &dependGraph[idx];
DepEntry *curr = dependGraph[idx].next;
// Make sure curr isn't NULL. Because this instruction is being
// removed from a dependency list, it must have been placed there at
// an earlier time. The dependency chain should not be empty,
// unless the instruction dependent upon it is already ready.
if (curr == NULL) {
return;
}
nodesRemoved++;
// Find the instruction to remove within the dependency linked list.
while (curr->inst != inst_to_remove) {
prev = curr;
curr = curr->next;
nodesTraversed++;
assert(curr != NULL);
}
// Now remove this instruction from the list.
prev->next = curr->next;
--memAllocCounter;
// Could push this off to the destructor of DependencyEntry
curr->inst = NULL;
delete curr;
}
template <class DynInstPtr>
DynInstPtr
DependencyGraph<DynInstPtr>::pop(PhysRegIndex idx)
{
DepEntry *node;
node = dependGraph[idx].next;
DynInstPtr inst = NULL;
if (node) {
inst = node->inst;
dependGraph[idx].next = node->next;
node->inst = NULL;
memAllocCounter--;
delete node;
}
return inst;
}
template <class DynInstPtr>
bool
DependencyGraph<DynInstPtr>::empty() const
{
for (int i = 0; i < numEntries; ++i) {
if (!empty(i))
return false;
}
return true;
}
template <class DynInstPtr>
void
DependencyGraph<DynInstPtr>::dump()
{
DepEntry *curr;
for (int i = 0; i < numEntries; ++i)
{
curr = &dependGraph[i];
if (curr->inst) {
cprintf("dependGraph[%i]: producer: %s [sn:%lli] consumer: ",
i, curr->inst->pcState(), curr->inst->seqNum);
} else {
cprintf("dependGraph[%i]: No producer. consumer: ", i);
}
while (curr->next != NULL) {
curr = curr->next;
cprintf("%s [sn:%lli] ",
curr->inst->pcState(), curr->inst->seqNum);
}
cprintf("\n");
}
cprintf("memAllocCounter: %i\n", memAllocCounter);
}
#endif // __CPU_O3_DEP_GRAPH_HH__