This is no longer used. Change-Id: I0ec170fb3b450430bbeff0a3c37bcdafe70c92b0 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/52053 Tested-by: kokoro <noreply+kokoro@google.com> Maintainer: Gabe Black <gabe.black@gmail.com> Reviewed-by: Daniel Carvalho <odanrc@yahoo.com.br>
582 lines
13 KiB
C++
582 lines
13 KiB
C++
/*
|
|
* Copyright (c) 2020 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) 2010 Gabe Black
|
|
* 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.
|
|
*/
|
|
|
|
#ifndef __ARCH_GENERIC_TYPES_HH__
|
|
#define __ARCH_GENERIC_TYPES_HH__
|
|
|
|
#include <iostream>
|
|
#include <memory>
|
|
#include <type_traits>
|
|
|
|
#include "base/compiler.hh"
|
|
#include "base/trace.hh"
|
|
#include "base/types.hh"
|
|
#include "sim/serialize.hh"
|
|
|
|
namespace gem5
|
|
{
|
|
|
|
// The guaranteed interface.
|
|
class PCStateBase : public Serializable
|
|
{
|
|
protected:
|
|
Addr _pc = 0;
|
|
MicroPC _upc = 0;
|
|
|
|
PCStateBase(const PCStateBase &other) : _pc(other._pc), _upc(other._upc) {}
|
|
PCStateBase &operator=(const PCStateBase &other) = default;
|
|
PCStateBase() {}
|
|
|
|
public:
|
|
virtual ~PCStateBase() = default;
|
|
|
|
template<class Target>
|
|
Target &
|
|
as()
|
|
{
|
|
return static_cast<Target &>(*this);
|
|
}
|
|
|
|
template<class Target>
|
|
const Target &
|
|
as() const
|
|
{
|
|
return static_cast<const Target &>(*this);
|
|
}
|
|
|
|
virtual PCStateBase *clone() const = 0;
|
|
virtual void
|
|
update(const PCStateBase &other)
|
|
{
|
|
_pc = other._pc;
|
|
_upc = other._upc;
|
|
}
|
|
void update(const PCStateBase *ptr) { update(*ptr); }
|
|
|
|
virtual void output(std::ostream &os) const = 0;
|
|
|
|
virtual bool
|
|
equals(const PCStateBase &other) const
|
|
{
|
|
return _pc == other._pc && _upc == other._upc;
|
|
}
|
|
|
|
/**
|
|
* Returns the memory address of the instruction this PC points to.
|
|
*
|
|
* @return Memory address of the instruction this PC points to.
|
|
*/
|
|
Addr
|
|
instAddr() const
|
|
{
|
|
return _pc;
|
|
}
|
|
|
|
/**
|
|
* Returns the current micropc.
|
|
*
|
|
* @return The current micropc.
|
|
*/
|
|
MicroPC
|
|
microPC() const
|
|
{
|
|
return _upc;
|
|
}
|
|
|
|
void
|
|
serialize(CheckpointOut &cp) const override
|
|
{
|
|
SERIALIZE_SCALAR(_pc);
|
|
SERIALIZE_SCALAR(_upc);
|
|
}
|
|
|
|
void
|
|
unserialize(CheckpointIn &cp) override
|
|
{
|
|
UNSERIALIZE_SCALAR(_pc);
|
|
UNSERIALIZE_SCALAR(_upc);
|
|
}
|
|
};
|
|
|
|
static inline std::ostream &
|
|
operator<<(std::ostream & os, const PCStateBase &pc)
|
|
{
|
|
pc.output(os);
|
|
return os;
|
|
}
|
|
|
|
static inline bool
|
|
operator==(const PCStateBase &a, const PCStateBase &b)
|
|
{
|
|
return a.equals(b);
|
|
}
|
|
|
|
static inline bool
|
|
operator!=(const PCStateBase &a, const PCStateBase &b)
|
|
{
|
|
return !a.equals(b);
|
|
}
|
|
|
|
namespace
|
|
{
|
|
|
|
inline void
|
|
set(PCStateBase *&dest, const PCStateBase *src)
|
|
{
|
|
if (GEM5_LIKELY(dest)) {
|
|
if (GEM5_LIKELY(src)) {
|
|
// Both src and dest already have storage, so just copy contents.
|
|
dest->update(src);
|
|
} else {
|
|
// src is empty, so clear out dest.
|
|
dest = nullptr;
|
|
}
|
|
} else {
|
|
if (GEM5_LIKELY(src)) {
|
|
// dest doesn't have storage, so create some as a copy of src.
|
|
dest = src->clone();
|
|
} else {
|
|
// dest is already nullptr, so nothing to do.
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void
|
|
set(std::unique_ptr<PCStateBase> &dest, const PCStateBase *src)
|
|
{
|
|
PCStateBase *dest_ptr = dest.get();
|
|
set(dest_ptr, src);
|
|
if (dest.get() != dest_ptr)
|
|
dest.reset(dest_ptr);
|
|
}
|
|
|
|
inline void
|
|
set(PCStateBase *&dest, const std::unique_ptr<PCStateBase> &src)
|
|
{
|
|
const PCStateBase *src_ptr = src.get();
|
|
set(dest, src_ptr);
|
|
}
|
|
|
|
inline void
|
|
set(std::unique_ptr<PCStateBase> &dest,
|
|
const std::unique_ptr<PCStateBase> &src)
|
|
{
|
|
PCStateBase *dest_ptr = dest.get();
|
|
const PCStateBase *src_ptr = src.get();
|
|
set(dest_ptr, src_ptr);
|
|
if (dest.get() != dest_ptr)
|
|
dest.reset(dest_ptr);
|
|
}
|
|
|
|
inline void
|
|
set(PCStateBase *&dest, const PCStateBase &src)
|
|
{
|
|
if (GEM5_LIKELY(dest)) {
|
|
// Update dest with the contents of src.
|
|
dest->update(src);
|
|
} else {
|
|
// Clone src over to dest.
|
|
dest = src.clone();
|
|
}
|
|
}
|
|
|
|
inline void
|
|
set(std::unique_ptr<PCStateBase> &dest, const PCStateBase &src)
|
|
{
|
|
PCStateBase *dest_ptr = dest.get();
|
|
set(dest_ptr, src);
|
|
if (dest.get() != dest_ptr)
|
|
dest.reset(dest_ptr);
|
|
}
|
|
|
|
inline void
|
|
set(PCStateBase &dest, const PCStateBase &src)
|
|
{
|
|
dest.update(src);
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
namespace GenericISA
|
|
{
|
|
|
|
class PCStateCommon : public PCStateBase
|
|
{
|
|
protected:
|
|
Addr _npc = 0;
|
|
|
|
MicroPC _nupc = 1;
|
|
|
|
PCStateCommon(const PCStateCommon &other) : PCStateBase(other),
|
|
_npc(other._npc), _nupc(other._nupc)
|
|
{}
|
|
PCStateCommon &operator=(const PCStateCommon &other) = default;
|
|
PCStateCommon() {}
|
|
|
|
public:
|
|
Addr pc() const { return _pc; }
|
|
void pc(Addr val) { _pc = val; }
|
|
|
|
Addr npc() const { return _npc; }
|
|
void npc(Addr val) { _npc = val; }
|
|
|
|
MicroPC upc() const { return _upc; }
|
|
void upc(MicroPC val) { _upc = val; }
|
|
|
|
MicroPC nupc() const { return _nupc; }
|
|
void nupc(MicroPC val) { _nupc = val; }
|
|
|
|
// Reset the macroop's upc without advancing the regular pc.
|
|
void
|
|
uReset()
|
|
{
|
|
_upc = 0;
|
|
_nupc = 1;
|
|
}
|
|
|
|
void
|
|
setNPC(Addr val)
|
|
{
|
|
npc(val);
|
|
}
|
|
|
|
void
|
|
output(std::ostream &os) const override
|
|
{
|
|
ccprintf(os, "(%#x=>%#x)", this->pc(), this->npc());
|
|
}
|
|
|
|
void
|
|
update(const PCStateBase &other) override
|
|
{
|
|
PCStateBase::update(other);
|
|
auto &pcstate = other.as<PCStateCommon>();
|
|
_npc = pcstate._npc;
|
|
_nupc = pcstate._nupc;
|
|
}
|
|
|
|
bool
|
|
equals(const PCStateBase &other) const override
|
|
{
|
|
auto &ps = other.as<PCStateCommon>();
|
|
return PCStateBase::equals(other) &&
|
|
_npc == ps._npc && _nupc == ps._nupc;
|
|
}
|
|
|
|
void
|
|
serialize(CheckpointOut &cp) const override
|
|
{
|
|
PCStateBase::serialize(cp);
|
|
SERIALIZE_SCALAR(_npc);
|
|
SERIALIZE_SCALAR(_nupc);
|
|
}
|
|
|
|
void
|
|
unserialize(CheckpointIn &cp) override
|
|
{
|
|
PCStateBase::unserialize(cp);
|
|
UNSERIALIZE_SCALAR(_npc);
|
|
UNSERIALIZE_SCALAR(_nupc);
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
* Different flavors of PC state. Only ISA specific code should rely on
|
|
* any particular type of PC state being available. All other code should
|
|
* use the interface above.
|
|
*/
|
|
|
|
// The most basic type of PC.
|
|
template <int InstWidth>
|
|
class SimplePCState : public PCStateCommon
|
|
{
|
|
protected:
|
|
typedef PCStateCommon Base;
|
|
|
|
public:
|
|
SimplePCState(const SimplePCState &other) : Base(other) {}
|
|
SimplePCState &operator=(const SimplePCState &other) = default;
|
|
SimplePCState() {}
|
|
explicit SimplePCState(Addr val) { set(val); }
|
|
|
|
PCStateBase *
|
|
clone() const override
|
|
{
|
|
return new SimplePCState<InstWidth>(*this);
|
|
}
|
|
|
|
/**
|
|
* Force this PC to reflect a particular value, resetting all its other
|
|
* fields around it. This is useful for in place (re)initialization.
|
|
*
|
|
* @param val The value to set the PC to.
|
|
*/
|
|
void
|
|
set(Addr val)
|
|
{
|
|
this->pc(val);
|
|
this->npc(val + InstWidth);
|
|
};
|
|
|
|
bool
|
|
branching() const
|
|
{
|
|
return this->npc() != this->pc() + InstWidth;
|
|
}
|
|
|
|
// Advance the PC.
|
|
void
|
|
advance()
|
|
{
|
|
this->_pc = this->_npc;
|
|
this->_npc += InstWidth;
|
|
}
|
|
};
|
|
|
|
// A PC and microcode PC.
|
|
template <int InstWidth>
|
|
class UPCState : public SimplePCState<InstWidth>
|
|
{
|
|
protected:
|
|
typedef SimplePCState<InstWidth> Base;
|
|
|
|
public:
|
|
void
|
|
output(std::ostream &os) const override
|
|
{
|
|
Base::output(os);
|
|
ccprintf(os, ".(%d=>%d)", this->upc(), this->nupc());
|
|
}
|
|
|
|
PCStateBase *
|
|
clone() const override
|
|
{
|
|
return new UPCState<InstWidth>(*this);
|
|
}
|
|
|
|
void
|
|
set(Addr val)
|
|
{
|
|
Base::set(val);
|
|
this->upc(0);
|
|
this->nupc(1);
|
|
}
|
|
|
|
UPCState(const UPCState &other) : Base(other) {}
|
|
UPCState &operator=(const UPCState &other) = default;
|
|
UPCState() {}
|
|
explicit UPCState(Addr val) { set(val); }
|
|
|
|
bool
|
|
branching() const
|
|
{
|
|
return this->npc() != this->pc() + InstWidth ||
|
|
this->nupc() != this->upc() + 1;
|
|
}
|
|
|
|
// Advance the upc within the instruction.
|
|
void
|
|
uAdvance()
|
|
{
|
|
this->upc(this->nupc());
|
|
this->nupc(this->nupc() + 1);
|
|
}
|
|
|
|
// End the macroop by resetting the upc and advancing the regular pc.
|
|
void
|
|
uEnd()
|
|
{
|
|
this->advance();
|
|
this->upc(0);
|
|
this->nupc(1);
|
|
}
|
|
};
|
|
|
|
// A PC with a delay slot.
|
|
template <int InstWidth>
|
|
class DelaySlotPCState : public SimplePCState<InstWidth>
|
|
{
|
|
protected:
|
|
typedef SimplePCState<InstWidth> Base;
|
|
|
|
Addr _nnpc;
|
|
|
|
public:
|
|
void
|
|
output(std::ostream &os) const override
|
|
{
|
|
ccprintf(os, "(%#x=>%#x=>%#x)", this->pc(), this->npc(), nnpc());
|
|
}
|
|
|
|
PCStateBase *
|
|
clone() const override
|
|
{
|
|
return new DelaySlotPCState<InstWidth>(*this);
|
|
}
|
|
|
|
void
|
|
update(const PCStateBase &other) override
|
|
{
|
|
Base::update(other);
|
|
auto &pcstate = other.as<DelaySlotPCState<InstWidth>>();
|
|
_nnpc = pcstate._nnpc;
|
|
}
|
|
|
|
Addr nnpc() const { return _nnpc; }
|
|
void nnpc(Addr val) { _nnpc = val; }
|
|
|
|
void
|
|
set(Addr val)
|
|
{
|
|
Base::set(val);
|
|
nnpc(val + 2 * InstWidth);
|
|
}
|
|
|
|
DelaySlotPCState(const DelaySlotPCState &other) :
|
|
Base(other), _nnpc(other._nnpc)
|
|
{}
|
|
DelaySlotPCState &operator=(const DelaySlotPCState &other) = default;
|
|
DelaySlotPCState() {}
|
|
explicit DelaySlotPCState(Addr val) { set(val); }
|
|
|
|
bool
|
|
branching() const
|
|
{
|
|
return !(this->nnpc() == this->npc() + InstWidth &&
|
|
(this->npc() == this->pc() + InstWidth ||
|
|
this->npc() == this->pc() + 2 * InstWidth));
|
|
}
|
|
|
|
// Advance the PC.
|
|
void
|
|
advance()
|
|
{
|
|
this->_pc = this->_npc;
|
|
this->_npc = this->_nnpc;
|
|
this->_nnpc += InstWidth;
|
|
}
|
|
|
|
bool
|
|
equals(const PCStateBase &other) const override
|
|
{
|
|
auto &ps = other.as<DelaySlotPCState<InstWidth>>();
|
|
return Base::equals(other) && ps._nnpc == this->_nnpc;
|
|
}
|
|
|
|
void
|
|
serialize(CheckpointOut &cp) const override
|
|
{
|
|
Base::serialize(cp);
|
|
SERIALIZE_SCALAR(_nnpc);
|
|
}
|
|
|
|
void
|
|
unserialize(CheckpointIn &cp) override
|
|
{
|
|
Base::unserialize(cp);
|
|
UNSERIALIZE_SCALAR(_nnpc);
|
|
}
|
|
};
|
|
|
|
// A PC with a delay slot and a microcode PC.
|
|
template <int InstWidth>
|
|
class DelaySlotUPCState : public DelaySlotPCState<InstWidth>
|
|
{
|
|
protected:
|
|
typedef DelaySlotPCState<InstWidth> Base;
|
|
|
|
public:
|
|
void
|
|
output(std::ostream &os) const override
|
|
{
|
|
Base::output(os);
|
|
ccprintf(os, ".(%d=>%d)", this->upc(), this->nupc());
|
|
}
|
|
|
|
PCStateBase *
|
|
clone() const override
|
|
{
|
|
return new DelaySlotUPCState<InstWidth>(*this);
|
|
}
|
|
|
|
void
|
|
set(Addr val)
|
|
{
|
|
Base::set(val);
|
|
this->upc(0);
|
|
this->nupc(1);
|
|
}
|
|
|
|
DelaySlotUPCState(const DelaySlotUPCState &other) : Base(other) {}
|
|
DelaySlotUPCState &operator=(const DelaySlotUPCState &other) = default;
|
|
DelaySlotUPCState() {}
|
|
explicit DelaySlotUPCState(Addr val) { set(val); }
|
|
|
|
bool
|
|
branching() const
|
|
{
|
|
return Base::branching() || this->nupc() != this->upc() + 1;
|
|
}
|
|
|
|
// Advance the upc within the instruction.
|
|
void
|
|
uAdvance()
|
|
{
|
|
this->_upc = this->_nupc;
|
|
this->_nupc++;
|
|
}
|
|
|
|
// End the macroop by resetting the upc and advancing the regular pc.
|
|
void
|
|
uEnd()
|
|
{
|
|
this->advance();
|
|
this->_upc = 0;
|
|
this->_nupc = 1;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
} // namespace gem5
|
|
|
|
#endif
|