654 lines
20 KiB
C++
654 lines
20 KiB
C++
|
/*
|
||
|
* Copyright (c) 2013-2014 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: Andrew Bardsley
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @file
|
||
|
*
|
||
|
* Classes for buffer, queue and FIFO behaviour.
|
||
|
*/
|
||
|
|
||
|
#ifndef __CPU_MINOR_BUFFERS_HH__
|
||
|
#define __CPU_MINOR_BUFFERS_HH__
|
||
|
|
||
|
#include <iostream>
|
||
|
#include <queue>
|
||
|
#include <sstream>
|
||
|
|
||
|
#include "cpu/minor/trace.hh"
|
||
|
#include "cpu/activity.hh"
|
||
|
#include "cpu/timebuf.hh"
|
||
|
|
||
|
namespace Minor
|
||
|
{
|
||
|
|
||
|
/** Interface class for data with reporting/tracing facilities. This
|
||
|
* interface doesn't actually have to be used as other classes which need
|
||
|
* this interface uses templating rather than inheritance but it's provided
|
||
|
* here to document the interface needed by those classes. */
|
||
|
class ReportIF
|
||
|
{
|
||
|
public:
|
||
|
/** Print the data in a format suitable to be the value in "name=value"
|
||
|
* trace lines */
|
||
|
virtual void reportData(std::ostream &os) const = 0;
|
||
|
|
||
|
virtual ~ReportIF() { }
|
||
|
};
|
||
|
|
||
|
/** Interface class for data with 'bubble' values. This interface doesn't
|
||
|
* actually have to be used as other classes which need this interface uses
|
||
|
* templating rather than inheritance but it's provided here to document
|
||
|
* the interface needed by those classes. */
|
||
|
class BubbleIF
|
||
|
{
|
||
|
public:
|
||
|
virtual bool isBubble() const = 0;
|
||
|
};
|
||
|
|
||
|
/** ...ReportTraits are trait classes with the same functionality as
|
||
|
* ReportIF, but with elements explicitly passed into the report...
|
||
|
* functions. */
|
||
|
|
||
|
/** Allow a template using ReportTraits to call report... functions of
|
||
|
* ReportIF-bearing elements themselves */
|
||
|
template <typename ElemType> /* ElemType should implement ReportIF */
|
||
|
class ReportTraitsAdaptor
|
||
|
{
|
||
|
public:
|
||
|
static void
|
||
|
reportData(std::ostream &os, const ElemType &elem)
|
||
|
{ elem.reportData(os); }
|
||
|
};
|
||
|
|
||
|
/** A similar adaptor but for elements held by pointer
|
||
|
* ElemType should implement ReportIF */
|
||
|
template <typename PtrType>
|
||
|
class ReportTraitsPtrAdaptor
|
||
|
{
|
||
|
public:
|
||
|
static void
|
||
|
reportData(std::ostream &os, const PtrType &elem)
|
||
|
{ elem->reportData(os); }
|
||
|
};
|
||
|
|
||
|
/** ... BubbleTraits are trait classes to add BubbleIF interface
|
||
|
* functionality to templates which process elements which don't necessarily
|
||
|
* implement BubbleIF themselves */
|
||
|
|
||
|
/** Default behaviour, no bubbles */
|
||
|
template <typename ElemType>
|
||
|
class NoBubbleTraits
|
||
|
{
|
||
|
public:
|
||
|
static bool isBubble(const ElemType &) { return false; }
|
||
|
static ElemType bubble() { assert(false); }
|
||
|
};
|
||
|
|
||
|
/** Pass on call to the element */
|
||
|
template <typename ElemType>
|
||
|
class BubbleTraitsAdaptor
|
||
|
{
|
||
|
public:
|
||
|
static bool isBubble(const ElemType &elem)
|
||
|
{ return elem.isBubble(); }
|
||
|
|
||
|
static ElemType bubble() { return ElemType::bubble(); }
|
||
|
};
|
||
|
|
||
|
/** Pass on call to the element where the element is a pointer */
|
||
|
template <typename PtrType, typename ElemType>
|
||
|
class BubbleTraitsPtrAdaptor
|
||
|
{
|
||
|
public:
|
||
|
static bool isBubble(const PtrType &elem)
|
||
|
{ return elem->isBubble(); }
|
||
|
|
||
|
static PtrType bubble() { return ElemType::bubble(); }
|
||
|
};
|
||
|
|
||
|
/** TimeBuffer with MinorTrace and Named interfaces */
|
||
|
template <typename ElemType,
|
||
|
typename ReportTraits = ReportTraitsAdaptor<ElemType>,
|
||
|
typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
|
||
|
class MinorBuffer : public Named, public TimeBuffer<ElemType>
|
||
|
{
|
||
|
protected:
|
||
|
/** The range of elements that should appear in trace lines */
|
||
|
int reportLeft, reportRight;
|
||
|
|
||
|
/** Name to use for the data in a MinorTrace line */
|
||
|
std::string dataName;
|
||
|
|
||
|
public:
|
||
|
MinorBuffer(const std::string &name,
|
||
|
const std::string &data_name,
|
||
|
int num_past, int num_future,
|
||
|
int report_left = -1, int report_right = -1) :
|
||
|
Named(name), TimeBuffer<ElemType>(num_past, num_future),
|
||
|
reportLeft(report_left), reportRight(report_right),
|
||
|
dataName(data_name)
|
||
|
{ }
|
||
|
|
||
|
public:
|
||
|
/* Is this buffer full of only bubbles */
|
||
|
bool
|
||
|
empty() const
|
||
|
{
|
||
|
bool ret = true;
|
||
|
|
||
|
for (int i = -this->past; i <= this->future; i++) {
|
||
|
if (!BubbleTraits::isBubble((*this)[i]))
|
||
|
ret = false;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/** Report buffer states from 'slot' 'from' to 'to'. For example 0,-1
|
||
|
* will produce two slices with current (just assigned) and last (one
|
||
|
* advance() old) slices with the current (0) one on the left.
|
||
|
* Reverse the numbers to change the order of slices */
|
||
|
void
|
||
|
minorTrace() const
|
||
|
{
|
||
|
std::ostringstream data;
|
||
|
|
||
|
int step = (reportLeft > reportRight ? -1 : 1);
|
||
|
int end = reportRight + step;
|
||
|
int i = reportLeft;
|
||
|
|
||
|
while (i != end) {
|
||
|
const ElemType &datum = (*this)[i];
|
||
|
|
||
|
ReportTraits::reportData(data, datum);
|
||
|
i += step;
|
||
|
if (i != end)
|
||
|
data << ',';
|
||
|
}
|
||
|
|
||
|
MINORTRACE("%s=%s\n", dataName, data.str());
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/** Wraps a MinorBuffer with Input/Output interfaces to ensure that units
|
||
|
* within the model can only see the right end of buffers between them. */
|
||
|
template <typename Data>
|
||
|
class Latch
|
||
|
{
|
||
|
public:
|
||
|
typedef MinorBuffer<Data> Buffer;
|
||
|
|
||
|
protected:
|
||
|
/** Delays, in cycles, writing data into the latch and seeing it on the
|
||
|
* latched wires */
|
||
|
Cycles delay;
|
||
|
|
||
|
Buffer buffer;
|
||
|
|
||
|
public:
|
||
|
/** forward/backwardDelay specify the delay from input to output in each
|
||
|
* direction. These arguments *must* be >= 1 */
|
||
|
Latch(const std::string &name,
|
||
|
const std::string &data_name,
|
||
|
Cycles delay_ = Cycles(1),
|
||
|
bool report_backwards = false) :
|
||
|
delay(delay_),
|
||
|
buffer(name, data_name, delay_, 0, (report_backwards ? -delay_ : 0),
|
||
|
(report_backwards ? 0 : -delay_))
|
||
|
{ }
|
||
|
|
||
|
public:
|
||
|
/** Encapsulate wires on either input or output of the latch.
|
||
|
* forward/backward correspond to data direction relative to the
|
||
|
* pipeline. Latched and Immediate specify delay for backward data.
|
||
|
* Immediate data is available to earlier stages *during* the cycle it
|
||
|
* is written */
|
||
|
class Input
|
||
|
{
|
||
|
public:
|
||
|
typename Buffer::wire inputWire;
|
||
|
|
||
|
public:
|
||
|
Input(typename Buffer::wire input_wire) :
|
||
|
inputWire(input_wire)
|
||
|
{ }
|
||
|
};
|
||
|
|
||
|
class Output
|
||
|
{
|
||
|
public:
|
||
|
typename Buffer::wire outputWire;
|
||
|
|
||
|
public:
|
||
|
Output(typename Buffer::wire output_wire) :
|
||
|
outputWire(output_wire)
|
||
|
{ }
|
||
|
};
|
||
|
|
||
|
bool empty() const { return buffer.empty(); }
|
||
|
|
||
|
/** An interface to just the input of the buffer */
|
||
|
Input input() { return Input(buffer.getWire(0)); }
|
||
|
|
||
|
/** An interface to just the output of the buffer */
|
||
|
Output output() { return Output(buffer.getWire(-delay)); }
|
||
|
|
||
|
void minorTrace() const { buffer.minorTrace(); }
|
||
|
|
||
|
void evaluate() { buffer.advance(); }
|
||
|
};
|
||
|
|
||
|
/** A pipeline simulating class that will stall (not advance when advance()
|
||
|
* is called) if a non-bubble value lies at the far end of the pipeline.
|
||
|
* The user can clear the stall before calling advance to unstall the
|
||
|
* pipeline. */
|
||
|
template <typename ElemType,
|
||
|
typename ReportTraits,
|
||
|
typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
|
||
|
class SelfStallingPipeline : public MinorBuffer<ElemType, ReportTraits>
|
||
|
{
|
||
|
protected:
|
||
|
/** Wire at the input end of the pipeline (for convenience) */
|
||
|
typename TimeBuffer<ElemType>::wire pushWire;
|
||
|
/** Wire at the output end of the pipeline (for convenience) */
|
||
|
typename TimeBuffer<ElemType>::wire popWire;
|
||
|
|
||
|
public:
|
||
|
/** If true, advance will not advance the pipeline */
|
||
|
bool stalled;
|
||
|
|
||
|
/** The number of slots with non-bubbles in them */
|
||
|
unsigned int occupancy;
|
||
|
|
||
|
public:
|
||
|
SelfStallingPipeline(const std::string &name,
|
||
|
const std::string &data_name,
|
||
|
unsigned depth) :
|
||
|
MinorBuffer<ElemType, ReportTraits>
|
||
|
(name, data_name, depth, 0, -1, -depth),
|
||
|
pushWire(this->getWire(0)),
|
||
|
popWire(this->getWire(-depth)),
|
||
|
stalled(false),
|
||
|
occupancy(0)
|
||
|
{
|
||
|
assert(depth > 0);
|
||
|
|
||
|
/* Write explicit bubbles to get around the case where the default
|
||
|
* constructor for the element type isn't good enough */
|
||
|
for (unsigned i = 0; i <= depth; i++)
|
||
|
(*this)[-i] = BubbleTraits::bubble();
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
/** Write an element to the back of the pipeline. This doesn't cause
|
||
|
* the pipeline to advance until advance is called. Pushing twice
|
||
|
* without advance-ing will just cause an overwrite of the last push's
|
||
|
* data. */
|
||
|
void push(ElemType &elem)
|
||
|
{
|
||
|
assert(!alreadyPushed());
|
||
|
*pushWire = elem;
|
||
|
if (!BubbleTraits::isBubble(elem))
|
||
|
occupancy++;
|
||
|
}
|
||
|
|
||
|
/** Peek at the end element of the pipe */
|
||
|
ElemType &front() { return *popWire; }
|
||
|
|
||
|
const ElemType &front() const { return *popWire; }
|
||
|
|
||
|
/** Have we already pushed onto this pipe without advancing */
|
||
|
bool alreadyPushed() { return !BubbleTraits::isBubble(*pushWire); }
|
||
|
|
||
|
/** There's data (not a bubble) at the end of the pipe */
|
||
|
bool isPopable() { return !BubbleTraits::isBubble(front()); }
|
||
|
|
||
|
/** Try to advance the pipeline. If we're stalled, don't advance. If
|
||
|
* we're not stalled, advance then check to see if we become stalled
|
||
|
* (a non-bubble at the end of the pipe) */
|
||
|
void
|
||
|
advance()
|
||
|
{
|
||
|
bool data_at_end = isPopable();
|
||
|
|
||
|
if (!stalled) {
|
||
|
TimeBuffer<ElemType>::advance();
|
||
|
/* If there was data at the end of the pipe that has now been
|
||
|
* advanced out of the pipe, we've lost data */
|
||
|
if (data_at_end)
|
||
|
occupancy--;
|
||
|
/* Is there data at the end of the pipe now? */
|
||
|
stalled = isPopable();
|
||
|
/* Insert a bubble into the empty input slot to make sure that
|
||
|
* element is correct in the case where the default constructor
|
||
|
* for ElemType doesn't produce a bubble */
|
||
|
ElemType bubble = BubbleTraits::bubble();
|
||
|
*pushWire = bubble;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/** Base class for space reservation requestable objects */
|
||
|
class Reservable
|
||
|
{
|
||
|
public:
|
||
|
/** Can a slot be reserved? */
|
||
|
virtual bool canReserve() const = 0;
|
||
|
|
||
|
/** Reserve a slot in whatever structure this is attached to */
|
||
|
virtual void reserve() = 0;
|
||
|
|
||
|
/** Free a reserved slot */
|
||
|
virtual void freeReservation() = 0;
|
||
|
};
|
||
|
|
||
|
/** Wrapper for a queue type to act as a pipeline stage input queue.
|
||
|
* Handles capacity management, bubble value suppression and provides
|
||
|
* reporting.
|
||
|
*
|
||
|
* In an ideal world, ElemType would be derived from ReportIF and BubbleIF,
|
||
|
* but here we use traits and allow the Adaptors ReportTraitsAdaptor and
|
||
|
* BubbleTraitsAdaptor to work on data which *does* directly implement
|
||
|
* those interfaces. */
|
||
|
template <typename ElemType,
|
||
|
typename ReportTraits = ReportTraitsAdaptor<ElemType>,
|
||
|
typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
|
||
|
class Queue : public Named, public Reservable
|
||
|
{
|
||
|
private:
|
||
|
std::deque<ElemType> queue;
|
||
|
|
||
|
/** Number of slots currently reserved for future (reservation
|
||
|
* respecting) pushes */
|
||
|
unsigned int numReservedSlots;
|
||
|
|
||
|
/** Need this here as queues usually don't have a limited capacity */
|
||
|
unsigned int capacity;
|
||
|
|
||
|
/** Name to use for the data in MinorTrace */
|
||
|
std::string dataName;
|
||
|
|
||
|
public:
|
||
|
Queue(const std::string &name, const std::string &data_name,
|
||
|
unsigned int capacity_) :
|
||
|
Named(name),
|
||
|
numReservedSlots(0),
|
||
|
capacity(capacity_),
|
||
|
dataName(data_name)
|
||
|
{ }
|
||
|
|
||
|
virtual ~Queue() { }
|
||
|
|
||
|
public:
|
||
|
/** Push an element into the buffer if it isn't a bubble. Bubbles are
|
||
|
* just discarded. It is assummed that any push into a queue with
|
||
|
* reserved space intends to take that space */
|
||
|
void
|
||
|
push(ElemType &data)
|
||
|
{
|
||
|
if (!BubbleTraits::isBubble(data)) {
|
||
|
freeReservation();
|
||
|
queue.push_back(data);
|
||
|
|
||
|
if (queue.size() > capacity) {
|
||
|
warn("%s: No space to push data into queue of capacity"
|
||
|
" %u, pushing anyway\n", name(), capacity);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Clear all allocated space. Be careful how this is used */
|
||
|
void clearReservedSpace() { numReservedSlots = 0; }
|
||
|
|
||
|
/** Clear a single reserved slot */
|
||
|
void freeReservation()
|
||
|
{
|
||
|
if (numReservedSlots != 0)
|
||
|
numReservedSlots--;
|
||
|
}
|
||
|
|
||
|
/** Reserve space in the queue for future pushes. Enquiries about space
|
||
|
* in the queue using unreservedRemainingSpace will only tell about
|
||
|
* space which is not full and not reserved. */
|
||
|
void
|
||
|
reserve()
|
||
|
{
|
||
|
/* Check reservable space */
|
||
|
if (unreservedRemainingSpace() == 0)
|
||
|
warn("%s: No space is reservable in queue", name());
|
||
|
|
||
|
numReservedSlots++;
|
||
|
}
|
||
|
|
||
|
bool canReserve() const { return unreservedRemainingSpace() != 0; }
|
||
|
|
||
|
/** Number of slots available in an empty buffer */
|
||
|
unsigned int totalSpace() const { return capacity; }
|
||
|
|
||
|
/** Number of slots already occupied in this buffer */
|
||
|
unsigned int occupiedSpace() const { return queue.size(); }
|
||
|
|
||
|
/** Number of slots which are reserved. */
|
||
|
unsigned int reservedSpace() const { return numReservedSlots; }
|
||
|
|
||
|
/** Number of slots yet to fill in this buffer. This doesn't include
|
||
|
* reservation. */
|
||
|
unsigned int
|
||
|
remainingSpace() const
|
||
|
{
|
||
|
int ret = capacity - queue.size();
|
||
|
|
||
|
return (ret < 0 ? 0 : ret);
|
||
|
}
|
||
|
|
||
|
/** Like remainingSpace but does not count reserved spaces */
|
||
|
unsigned int
|
||
|
unreservedRemainingSpace() const
|
||
|
{
|
||
|
int ret = capacity - (queue.size() + numReservedSlots);
|
||
|
|
||
|
return (ret < 0 ? 0 : ret);
|
||
|
}
|
||
|
|
||
|
/** Head value. Like std::queue::front */
|
||
|
ElemType &front() { return queue.front(); }
|
||
|
|
||
|
const ElemType &front() const { return queue.front(); }
|
||
|
|
||
|
/** Pop the head item. Like std::queue::pop */
|
||
|
void pop() { queue.pop_front(); }
|
||
|
|
||
|
/** Is the queue empty? */
|
||
|
bool empty() const { return queue.empty(); }
|
||
|
|
||
|
void
|
||
|
minorTrace() const
|
||
|
{
|
||
|
std::ostringstream data;
|
||
|
/* If we become over-full, totalSpace() can actually be smaller than
|
||
|
* occupiedSpace(). Handle this */
|
||
|
unsigned int num_total = (occupiedSpace() > totalSpace() ?
|
||
|
occupiedSpace() : totalSpace());
|
||
|
|
||
|
unsigned int num_reserved = reservedSpace();
|
||
|
unsigned int num_occupied = occupiedSpace();
|
||
|
|
||
|
int num_printed = 1;
|
||
|
/* Bodge to rotate queue to report elements */
|
||
|
while (num_printed <= num_occupied) {
|
||
|
ReportTraits::reportData(data, queue[num_printed - 1]);
|
||
|
num_printed++;
|
||
|
|
||
|
if (num_printed <= num_total)
|
||
|
data << ',';
|
||
|
}
|
||
|
|
||
|
int num_printed_reserved = 1;
|
||
|
/* Show reserved slots */
|
||
|
while (num_printed_reserved <= num_reserved &&
|
||
|
num_printed <= num_total)
|
||
|
{
|
||
|
data << 'R';
|
||
|
num_printed_reserved++;
|
||
|
num_printed++;
|
||
|
|
||
|
if (num_printed <= num_total)
|
||
|
data << ',';
|
||
|
}
|
||
|
|
||
|
/* And finally pad with empty slots (if there are any) */
|
||
|
while (num_printed <= num_total) {
|
||
|
num_printed++;
|
||
|
|
||
|
if (num_printed <= num_total)
|
||
|
data << ',';
|
||
|
}
|
||
|
|
||
|
MINORTRACE("%s=%s\n", dataName, data.str());
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/** Like a Queue but with a restricted interface and a setTail function
|
||
|
* which, when the queue is empty, just takes a reference to the pushed
|
||
|
* item as the single element. Calling pushTail will push that element
|
||
|
* onto the queue.
|
||
|
*
|
||
|
* The purpose of this class is to allow the faster operation of queues of
|
||
|
* items which usually don't get deeper than one item and for which the copy
|
||
|
* associated with a push is expensive enough to want to avoid
|
||
|
*
|
||
|
* The intended use case is the input buffer for pipeline stages, hence the
|
||
|
* class name */
|
||
|
template <typename ElemType,
|
||
|
typename ReportTraits = ReportTraitsAdaptor<ElemType>,
|
||
|
typename BubbleTraits = BubbleTraitsAdaptor<ElemType> >
|
||
|
class InputBuffer : public Reservable
|
||
|
{
|
||
|
protected:
|
||
|
/** Underlying queue */
|
||
|
mutable Queue<ElemType, ReportTraits, BubbleTraits> queue;
|
||
|
|
||
|
/** Pointer to the single element (if not NULL) */
|
||
|
mutable ElemType *elementPtr;
|
||
|
|
||
|
public:
|
||
|
InputBuffer(const std::string &name, const std::string &data_name,
|
||
|
unsigned int capacity_) :
|
||
|
queue(name, data_name, capacity_),
|
||
|
elementPtr(NULL)
|
||
|
{ }
|
||
|
|
||
|
public:
|
||
|
/** Set the tail of the queue, this is like push but needs
|
||
|
* to be followed by pushTail for the new tail to make its
|
||
|
* way into the queue proper */
|
||
|
void
|
||
|
setTail(ElemType &new_element)
|
||
|
{
|
||
|
assert(!elementPtr);
|
||
|
if (!BubbleTraits::isBubble(new_element)) {
|
||
|
if (queue.empty())
|
||
|
elementPtr = &new_element;
|
||
|
else
|
||
|
queue.push(new_element);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** No single element or queue entries */
|
||
|
bool empty() const { return !elementPtr && queue.empty(); }
|
||
|
|
||
|
/** Return the element, or the front of the queue */
|
||
|
const ElemType &front() const
|
||
|
{ return (elementPtr ? *elementPtr : queue.front()); }
|
||
|
|
||
|
ElemType &front()
|
||
|
{ return (elementPtr ? *elementPtr : queue.front()); }
|
||
|
|
||
|
/** Pop either the head, or if none, the head of the queue */
|
||
|
void
|
||
|
pop()
|
||
|
{
|
||
|
if (elementPtr) {
|
||
|
/* A popped element was expected to be pushed into queue
|
||
|
* and so take a reserved space */
|
||
|
elementPtr = NULL;
|
||
|
queue.freeReservation();
|
||
|
} else {
|
||
|
queue.pop();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Push the single element (if any) into the queue proper. If the
|
||
|
* element's reference points to a transient object, remember to
|
||
|
* always do this before the end of that object's life */
|
||
|
void
|
||
|
pushTail() const
|
||
|
{
|
||
|
if (elementPtr)
|
||
|
queue.push(*elementPtr);
|
||
|
elementPtr = NULL;
|
||
|
}
|
||
|
|
||
|
/** Report elements */
|
||
|
void
|
||
|
minorTrace() const
|
||
|
{
|
||
|
pushTail();
|
||
|
queue.minorTrace();
|
||
|
}
|
||
|
|
||
|
/** Reservable interface, passed on to queue */
|
||
|
bool canReserve() const { return queue.canReserve(); }
|
||
|
void reserve() { queue.reserve(); }
|
||
|
void freeReservation() { queue.freeReservation(); }
|
||
|
|
||
|
/** Like remainingSpace but does not count reserved spaces */
|
||
|
unsigned int
|
||
|
unreservedRemainingSpace()
|
||
|
{
|
||
|
pushTail();
|
||
|
return queue.unreservedRemainingSpace();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
}
|
||
|
|
||
|
#endif /* __CPU_MINOR_BUFFERS_HH__ */
|