arch,cpu,sim: Add mechanism to partially print vector regs (#1234)

Currently, gem5's inst tracer prints the whole vector register container
by default. The size of vector register containers in gem5 is the
maximum size allowed by the ISA. For vector-length agnostic (VLA) vector
registers, this means ARM SVE vector container is 2048 bits long, and
RISC-V vector container is 65535 bits long. Note that VLA implementation
in gem5 allows the vector length to be varied within the limit specified
by the ISAs.

However, in most use cases of gem5, the vector length is much less than
65535 bits. This causes two issues: (1) the vector container requires
allocating and moving around a large amount of unused data while only a
fraction of it is used, and (2) printing the execution trace of a vector
register results in a wall of text with a small amount of useful data.

This change addresses the problem (2) by providing a mechanism to limit
the amount data printed by the instruction tracer. This is done by
adding a function printing the first X bits of a vector register
container, where X is the vector length determined at runtime, as
opposed to the vector container size, which is determined at compilation
time.

Change-Id: I815fa5aa738373510afcfb0d544a5b19c40dc0c7

---------

Signed-off-by: Hoa Nguyen <hn@hnpl.org>
This commit is contained in:
Hoa Nguyen
2024-06-17 14:05:47 -07:00
committed by GitHub
parent fef6a97f93
commit 15e0236a8b
11 changed files with 108 additions and 13 deletions

View File

@@ -435,6 +435,8 @@ namespace ArmISA
void globalClearExclusive() override;
void globalClearExclusive(ExecContext *xc) override;
int64_t getVectorLengthInBytes() const override { return sveVL * 16; }
};
} // namespace ArmISA

View File

@@ -137,6 +137,15 @@ class BaseISA : public SimObject
{
SERIALIZE_SCALAR(isaName);
}
/**
* This function returns the vector length of the Vector Length Agnostic
* extension of the ISA.
* For ARM ISA, this function returns the SVE/SVE2 vector length.
* For RISC-V ISA, this function returns the RVV vector length.
* For other ISAs, this function returns -1.
*/
virtual int64_t getVectorLengthInBytes() const { return -1; }
};
} // namespace gem5

View File

@@ -99,7 +99,9 @@
#include <array>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include "base/cprintf.hh"
@@ -218,6 +220,24 @@ class VecRegContainer
return os;
}
std::string
getString(const uint64_t& size) const
{
std::stringstream s;
size_t count = 0;
s << "[";
for (auto& b: container) {
if (count && (count % 4) == 0)
s << "_";
s << std::hex << std::setfill('0') << std::setw(2) << (uint16_t)b;
count++;
if (count == size)
break;
}
s << "]";
return s.str();
}
/** @} */
/**
* Used for serialization.

View File

@@ -464,8 +464,9 @@ VMaskMergeMicroInst::execute(ExecContext* xc,
memcpy(Vd + i * byte_offset, s + i * byte_offset, byte_offset);
}
}
if (traceData)
if (traceData) {
traceData->setData(vecRegClass, &tmp_d0);
}
return NoFault;
}
@@ -666,8 +667,9 @@ VlSegDeIntrlvMicroInst::execute(ExecContext* xc, trace::InstRecord* traceData) c
elem++;
}
}
if (traceData)
if (traceData) {
traceData->setData(vecRegClass, &tmp_d0);
}
return NoFault;
}
@@ -776,8 +778,9 @@ VsSegIntrlvMicroInst::execute(ExecContext* xc,
}
}
if (traceData)
if (traceData) {
traceData->setData(vecRegClass, &tmp_d0);
}
return NoFault;
}

View File

@@ -166,6 +166,8 @@ class ISA : public BaseISA
unsigned getVecLenInBytes() { return vlen >> 3; }
unsigned getVecElemLenInBits() { return elen; }
int64_t getVectorLengthInBytes() const override { return vlen >> 3; }
PrivilegeModeSet getPrivilegeModeSet() { return _privilegeModeSet; }
virtual Addr getFaultHandlerAddr(

View File

@@ -115,10 +115,15 @@ ExeTracerRecord::traceInst(const StaticInstPtr &inst, bool ran)
}
if (debug::ExecResult && dataStatus != DataInvalid) {
if (dataStatus == DataReg)
ccprintf(outs, " D=%s", data.asReg.asString());
else
if (dataStatus == DataReg) {
if (vectorLengthInBytes > 0 && inst->isVector()) {
outs << " D=" << data.asReg.asString(vectorLengthInBytes);
} else {
ccprintf(outs, " D=%s", data.asReg.asString());
}
} else {
ccprintf(outs, " D=%#018x", data.asInt);
}
}
if (debug::ExecEffAddr && getMemValid())

View File

@@ -66,6 +66,7 @@ class ExeTracerRecord : public InstRecord
: InstRecord(_when, _thread, _staticInst, _pc, _macroStaticInst),
tracer(_tracer)
{
vectorLengthInBytes = _thread->getIsaPtr()->getVectorLengthInBytes();
}
void traceInst(const StaticInstPtr &inst, bool ran);
@@ -74,6 +75,7 @@ class ExeTracerRecord : public InstRecord
protected:
const ExeTracer &tracer;
int64_t vectorLengthInBytes;
};
class ExeTracer : public InstTracer

View File

@@ -176,6 +176,13 @@ class InstResult
return _regClass->valString(&reg);
}
}
std::string
asString(const int64_t& num_bytes) const
{
assert(blob());
return _regClass->valString(getBlob(), num_bytes);
}
};
} // namespace gem5

View File

@@ -56,7 +56,7 @@ RegClassOps::regName(const RegId &id) const
}
std::string
RegClassOps::valString(const void *val, size_t size) const
RegClassOps::valString(const void *val, const size_t& size) const
{
// If this is just a RegVal, or could be interpreted as one, print it
// that way.

View File

@@ -45,6 +45,7 @@
#include <iterator>
#include <string>
#include "arch/generic/vec_reg.hh"
#include "base/cprintf.hh"
#include "base/debug.hh"
#include "base/intmath.hh"
@@ -170,7 +171,7 @@ class RegClassOps
/** Print the name of the register specified in id. */
virtual std::string regName(const RegId &id) const;
/** Print the value of a register pointed to by val of size size. */
virtual std::string valString(const void *val, size_t size) const;
virtual std::string valString(const void *val, const size_t& size) const;
/** Flatten register id id using information in the ISA object isa. */
virtual RegId
flatten(const BaseISA &isa, const RegId &id) const
@@ -246,6 +247,11 @@ class RegClass
{
return _ops->valString(val, regBytes());
}
std::string
valString(const void *val, const uint64_t& num_bytes) const
{
return _ops->valString(val, std::min(regBytes(), num_bytes));
}
RegId
flatten(const BaseISA &isa, const RegId &id) const
{
@@ -354,15 +360,30 @@ RegClass::operator[](RegIndex idx) const
return RegId(*this, idx);
}
// Type matching for gem5::VecRegContainer class
// This is used in TypedRegClassOps.
template<typename>
struct is_vec_reg_container : std::false_type {};
template<std::size_t SIZE>
struct is_vec_reg_container<gem5::VecRegContainer<SIZE>> : std::true_type {};
template <typename ValueType>
class TypedRegClassOps : public RegClassOps
{
public:
std::string
valString(const void *val, size_t size) const override
valString(const void *val, const size_t& size) const override
{
assert(size == sizeof(ValueType));
return csprintf("%s", *(const ValueType *)val);
if constexpr (is_vec_reg_container<ValueType>::value) {
if (size == sizeof(ValueType)) {
return csprintf("%s", *(const ValueType *)val);
} else {
return ((const ValueType *)val)->getString(size);
}
} else {
assert(size == sizeof(ValueType));
return csprintf("%s", *(const ValueType *)val);
}
}
};

View File

@@ -232,14 +232,38 @@ class InstRecord
setData(const RegClass &reg_class, RegVal val)
{
new(&data.asReg) InstResult(reg_class, val);
dataStatus = DataReg;
switch (reg_class.type()) {
case IntRegClass:
case MiscRegClass:
case CCRegClass:
dataStatus = DataInt64;
break;
case FloatRegClass:
dataStatus = DataDouble;
break;
default:
dataStatus = DataReg;
break;
}
}
void
setData(const RegClass &reg_class, const void *val)
{
new(&data.asReg) InstResult(reg_class, val);
dataStatus = DataReg;
switch (reg_class.type()) {
case IntRegClass:
case MiscRegClass:
case CCRegClass:
dataStatus = DataInt64;
break;
case FloatRegClass:
dataStatus = DataDouble;
break;
default:
dataStatus = DataReg;
break;
}
}
void