base,mem: Support AtomicOpFunctor in the classic memory system

AtomicOpFunctor can be used to implement atomic memory operations.
AtomicOpFunctor is captured inside a memory request and executed directly
in the memory hierarchy in a single step.

This patch enables AtomicOpFunctor pointers to be included in a memory
request and executed in a single step in the classic cache system.

This patch also makes the copy constructor of Request class do a deep
copy of AtomicOpFunctor object. This prevents a copy of a Request object
from accessing a deleted AtomicOpFunctor object.

Change-Id: I6649532b37f711e55f4552ad26893efeb300dd37
Reviewed-on: https://gem5-review.googlesource.com/8185
Reviewed-by: Nikos Nikoleris <nikos.nikoleris@arm.com>
Maintainer: Nikos Nikoleris <nikos.nikoleris@arm.com>
This commit is contained in:
Tuan Ta
2018-01-22 12:54:14 -05:00
parent 74a8947fcb
commit 7bab1d0aff
4 changed files with 41 additions and 5 deletions

View File

@@ -191,6 +191,7 @@ constexpr decltype(nullptr) NoFault = nullptr;
struct AtomicOpFunctor
{
virtual void operator()(uint8_t *p) = 0;
virtual AtomicOpFunctor* clone() = 0;
virtual ~AtomicOpFunctor() {}
};
@@ -198,6 +199,7 @@ template <class T>
struct TypedAtomicOpFunctor : public AtomicOpFunctor
{
void operator()(uint8_t *p) { execute((T *)p); }
virtual AtomicOpFunctor* clone() = 0;
virtual void execute(T * p) = 0;
};

17
src/mem/cache/base.cc vendored
View File

@@ -836,7 +836,22 @@ BaseCache::satisfyRequest(PacketPtr pkt, CacheBlk *blk, bool, bool)
// Check RMW operations first since both isRead() and
// isWrite() will be true for them
if (pkt->cmd == MemCmd::SwapReq) {
cmpAndSwap(blk, pkt);
if (pkt->isAtomicOp()) {
// extract data from cache and save it into the data field in
// the packet as a return value from this atomic op
int offset = tags->extractBlkOffset(pkt->getAddr());
uint8_t *blk_data = blk->data + offset;
std::memcpy(pkt->getPtr<uint8_t>(), blk_data, pkt->getSize());
// execute AMO operation
(*(pkt->getAtomicOp()))(blk_data);
// set block status to dirty
blk->status |= BlkDirty;
} else {
cmpAndSwap(blk, pkt);
}
} else if (pkt->isWrite()) {
// we have the block in a writable state and can go ahead,
// note that the line may be also be considered writable in

View File

@@ -841,7 +841,7 @@ class Packet : public Printable
{
if (req->isLLSC())
return MemCmd::StoreCondReq;
else if (req->isSwap())
else if (req->isSwap() || req->isAtomic())
return MemCmd::SwapReq;
else if (req->isCacheInvalidate()) {
return req->isCacheClean() ? MemCmd::CleanInvalidReq :

View File

@@ -445,12 +445,30 @@ class Request
Request(int asid, Addr vaddr, unsigned size, Flags flags, MasterID mid,
Addr pc, ContextID cid, AtomicOpFunctor *atomic_op)
: atomicOpFunctor(atomic_op)
{
setVirt(asid, vaddr, size, flags, mid, pc);
setVirt(asid, vaddr, size, flags, mid, pc, atomic_op);
setContext(cid);
}
Request(const Request& other)
: _paddr(other._paddr), _size(other._size),
_masterId(other._masterId),
_flags(other._flags),
_memSpaceConfigFlags(other._memSpaceConfigFlags),
privateFlags(other.privateFlags),
_time(other._time),
_taskId(other._taskId), _asid(other._asid), _vaddr(other._vaddr),
_extraData(other._extraData), _contextId(other._contextId),
_pc(other._pc), _reqInstSeqNum(other._reqInstSeqNum),
translateDelta(other.translateDelta),
accessDelta(other.accessDelta), depth(other.depth)
{
if (other.atomicOpFunctor)
atomicOpFunctor = (other.atomicOpFunctor)->clone();
else
atomicOpFunctor = nullptr;
}
~Request()
{
if (hasAtomicOpFunctor()) {
@@ -474,7 +492,7 @@ class Request
*/
void
setVirt(int asid, Addr vaddr, unsigned size, Flags flags, MasterID mid,
Addr pc)
Addr pc, AtomicOpFunctor *amo_op = nullptr)
{
_asid = asid;
_vaddr = vaddr;
@@ -490,6 +508,7 @@ class Request
depth = 0;
accessDelta = 0;
translateDelta = 0;
atomicOpFunctor = amo_op;
}
/**