From 15e0236a8b48a2a0a55167386a8b0fcdfcb96f3e Mon Sep 17 00:00:00 2001 From: Hoa Nguyen Date: Mon, 17 Jun 2024 14:05:47 -0700 Subject: [PATCH] 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 --- src/arch/arm/isa.hh | 2 ++ src/arch/generic/isa.hh | 9 +++++++++ src/arch/generic/vec_reg.hh | 20 ++++++++++++++++++++ src/arch/riscv/insts/vector.cc | 9 ++++++--- src/arch/riscv/isa.hh | 2 ++ src/cpu/exetrace.cc | 11 ++++++++--- src/cpu/exetrace.hh | 2 ++ src/cpu/inst_res.hh | 7 +++++++ src/cpu/reg_class.cc | 2 +- src/cpu/reg_class.hh | 29 +++++++++++++++++++++++++---- src/sim/insttracer.hh | 28 ++++++++++++++++++++++++++-- 11 files changed, 108 insertions(+), 13 deletions(-) diff --git a/src/arch/arm/isa.hh b/src/arch/arm/isa.hh index 7b041f61b2..a60c887391 100644 --- a/src/arch/arm/isa.hh +++ b/src/arch/arm/isa.hh @@ -435,6 +435,8 @@ namespace ArmISA void globalClearExclusive() override; void globalClearExclusive(ExecContext *xc) override; + + int64_t getVectorLengthInBytes() const override { return sveVL * 16; } }; } // namespace ArmISA diff --git a/src/arch/generic/isa.hh b/src/arch/generic/isa.hh index 07ff5a5226..b324591995 100644 --- a/src/arch/generic/isa.hh +++ b/src/arch/generic/isa.hh @@ -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 diff --git a/src/arch/generic/vec_reg.hh b/src/arch/generic/vec_reg.hh index 349811f1f0..d643c9db5b 100644 --- a/src/arch/generic/vec_reg.hh +++ b/src/arch/generic/vec_reg.hh @@ -99,7 +99,9 @@ #include #include +#include #include +#include #include #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. diff --git a/src/arch/riscv/insts/vector.cc b/src/arch/riscv/insts/vector.cc index dca0259bd5..d73fb93882 100644 --- a/src/arch/riscv/insts/vector.cc +++ b/src/arch/riscv/insts/vector.cc @@ -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; } diff --git a/src/arch/riscv/isa.hh b/src/arch/riscv/isa.hh index 8622bf2338..9c0fd70f35 100644 --- a/src/arch/riscv/isa.hh +++ b/src/arch/riscv/isa.hh @@ -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( diff --git a/src/cpu/exetrace.cc b/src/cpu/exetrace.cc index 2be3557d4b..89c9a66e74 100644 --- a/src/cpu/exetrace.cc +++ b/src/cpu/exetrace.cc @@ -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()) diff --git a/src/cpu/exetrace.hh b/src/cpu/exetrace.hh index 3fbeb98bc3..0b77795c5b 100644 --- a/src/cpu/exetrace.hh +++ b/src/cpu/exetrace.hh @@ -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 diff --git a/src/cpu/inst_res.hh b/src/cpu/inst_res.hh index 30bb2b262b..bfcf4fd461 100644 --- a/src/cpu/inst_res.hh +++ b/src/cpu/inst_res.hh @@ -176,6 +176,13 @@ class InstResult return _regClass->valString(®); } } + + std::string + asString(const int64_t& num_bytes) const + { + assert(blob()); + return _regClass->valString(getBlob(), num_bytes); + } }; } // namespace gem5 diff --git a/src/cpu/reg_class.cc b/src/cpu/reg_class.cc index 12d1c7f2e0..a72156195c 100644 --- a/src/cpu/reg_class.cc +++ b/src/cpu/reg_class.cc @@ -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. diff --git a/src/cpu/reg_class.hh b/src/cpu/reg_class.hh index 37618e530a..68a3fb8023 100644 --- a/src/cpu/reg_class.hh +++ b/src/cpu/reg_class.hh @@ -45,6 +45,7 @@ #include #include +#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 +struct is_vec_reg_container : std::false_type {}; +template +struct is_vec_reg_container> : std::true_type {}; + template 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::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); + } } }; diff --git a/src/sim/insttracer.hh b/src/sim/insttracer.hh index 37e29756a2..1a4b3119ae 100644 --- a/src/sim/insttracer.hh +++ b/src/sim/insttracer.hh @@ -232,14 +232,38 @@ class InstRecord setData(const RegClass ®_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 ®_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