arch, x86: Rework the debug faults and microops.

This makes the non-fatal microops advance the PC, and adds missing
functions. The *_once Faults now also can be run once per *something*.
They would previously be run once per Fault invoke function which is
common to all M5WarnOnceFaults. The warn_once microop will now warn
once per message.

Change-Id: I05974b93f3b2700077a411b243679c2ff0e8c2cb
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/20739
Reviewed-by: Gabe Black <gabeblack@google.com>
Reviewed-by: Brandon Potter <Brandon.Potter@amd.com>
Maintainer: Gabe Black <gabeblack@google.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Gabe Black
2019-09-06 17:14:59 -07:00
parent cd00a363f1
commit f3fd746967
3 changed files with 202 additions and 129 deletions

View File

@@ -50,78 +50,125 @@ namespace GenericISA
class M5DebugFault : public FaultBase
{
public:
enum DebugFunc
{
PanicFunc,
FatalFunc,
WarnFunc,
WarnOnceFunc
};
protected:
std::string message;
DebugFunc func;
public:
M5DebugFault(DebugFunc _func, std::string _message) :
message(_message), func(_func)
{}
FaultName
name() const
std::string _message;
virtual void debugFunc() = 0;
void
advancePC(ThreadContext *tc, const StaticInstPtr &inst)
{
switch (func) {
case PanicFunc:
return "panic fault";
case FatalFunc:
return "fatal fault";
case WarnFunc:
return "warn fault";
case WarnOnceFunc:
return "warn_once fault";
default:
panic("unrecognized debug function number\n");
if (inst) {
auto pc = tc->pcState();
inst->advancePC(pc);
tc->pcState(pc);
}
}
public:
M5DebugFault(std::string _m) : _message(_m) {}
template <class ...Args>
M5DebugFault(const std::string &format, const Args &...args) :
_message(csprintf(format, args...))
{}
std::string message() { return _message; }
void
invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr)
StaticInst::nullStaticInstPtr) override
{
switch (func) {
case PanicFunc:
panic(message);
break;
case FatalFunc:
fatal(message);
break;
case WarnFunc:
warn(message);
break;
case WarnOnceFunc:
warn_once(message);
break;
default:
panic("unrecognized debug function number\n");
}
debugFunc();
advancePC(tc, inst);
}
};
template <int Func>
class M5VarArgsFault : public M5DebugFault
// The "Flavor" template parameter is to keep warn, hack or inform messages
// with the same token from blocking each other.
template <class Flavor>
class M5DebugOnceFault : public M5DebugFault
{
protected:
bool &once;
template <class F, class OnceToken>
static bool &
lookUpToken(const OnceToken &token)
{
static std::map<OnceToken, bool> tokenMap;
return tokenMap[token];
}
public:
template<typename ...Args>
M5VarArgsFault(const std::string &format, const Args &...args) :
M5DebugFault((DebugFunc)Func, csprintf(format, args...))
template <class OnceToken, class ...Args>
M5DebugOnceFault(const OnceToken &token, const std::string &format,
const Args &...args) :
M5DebugFault(format, args...), once(lookUpToken<Flavor>(token))
{}
void
invoke(ThreadContext *tc, const StaticInstPtr &inst =
StaticInst::nullStaticInstPtr) override
{
if (!once) {
once = true;
debugFunc();
}
advancePC(tc, inst);
}
};
typedef M5VarArgsFault<M5DebugFault::PanicFunc> M5PanicFault;
typedef M5VarArgsFault<M5DebugFault::FatalFunc> M5FatalFault;
typedef M5VarArgsFault<M5DebugFault::WarnFunc> M5WarnFault;
typedef M5VarArgsFault<M5DebugFault::WarnOnceFunc> M5WarnOnceFault;
class M5PanicFault : public M5DebugFault
{
public:
using M5DebugFault::M5DebugFault;
void debugFunc() override { panic(message()); }
FaultName name() const override { return "panic fault"; }
};
class M5FatalFault : public M5DebugFault
{
public:
using M5DebugFault::M5DebugFault;
void debugFunc() override { fatal(message()); }
FaultName name() const override { return "fatal fault"; }
};
template <class Base>
class M5WarnFaultBase : public Base
{
public:
using Base::Base;
void debugFunc() override { warn(this->message()); }
FaultName name() const override { return "warn fault"; }
};
using M5WarnFault = M5WarnFaultBase<M5DebugFault>;
using M5WarnOnceFault = M5WarnFaultBase<M5DebugOnceFault<M5WarnFault>>;
template <class Base>
class M5HackFaultBase : public Base
{
public:
using Base::Base;
void debugFunc() override { hack(this->message()); }
FaultName name() const override { return "hack fault"; }
};
using M5HackFault = M5HackFaultBase<M5DebugFault>;
using M5HackOnceFault = M5HackFaultBase<M5DebugOnceFault<M5HackFault>>;
template <class Base>
class M5InformFaultBase : public Base
{
public:
using Base::Base;
void debugFunc() override { inform(this->message()); }
FaultName name() const override { return "inform fault"; }
};
using M5InformFault = M5InformFaultBase<M5DebugFault>;
using M5InformOnceFault =
M5InformFaultBase<M5DebugOnceFault<M5InformFault>>;
} // namespace GenericISA

View File

@@ -39,6 +39,7 @@
#include "arch/x86/insts/badmicroop.hh"
#include "arch/generic/debugfaults.hh"
#include "arch/x86/generated/decoder.hh"
#include "arch/x86/isa_traits.hh"
@@ -56,8 +57,8 @@ namespace X86ISA
// try to delete the static memory when it was destructed.
const StaticInstPtr badMicroop =
new X86ISAInst::MicroPanic(dummyMachInst, "BAD",
new X86ISAInst::MicroDebug(dummyMachInst, "panic", "BAD",
StaticInst::IsMicroop | StaticInst::IsLastMicroop,
"Invalid microop!", 0);
new GenericISA::M5PanicFault("Invalid microop!"));
} // namespace X86ISA

View File

@@ -42,21 +42,21 @@
//////////////////////////////////////////////////////////////////////////
output header {{
class MicroDebugBase : public X86ISA::X86MicroopBase
class MicroDebug : public X86ISA::X86MicroopBase
{
protected:
typedef GenericISA::M5DebugFault::DebugFunc DebugFunc;
DebugFunc func;
std::string message;
uint8_t cc;
std::shared_ptr<GenericISA::M5DebugFault> fault;
public:
MicroDebugBase(ExtMachInst machInst, const char * mnem,
const char * instMnem, uint64_t setFlags,
DebugFunc _func, std::string _message, uint8_t _cc) :
X86MicroopBase(machInst, mnem, instMnem, setFlags, No_OpClass),
func(_func), message(_message), cc(_cc)
{}
MicroDebug(ExtMachInst _machInst, const char *mnem,
const char *instMnem, uint64_t setFlags,
GenericISA::M5DebugFault *_fault);
Fault
execute(ExecContext *xc, Trace::InstRecord *traceData) const
{
return fault;
}
std::string
generateDisassembly(Addr pc, const SymbolTable *symtab) const
@@ -64,25 +64,37 @@ output header {{
std::stringstream response;
printMnemonic(response, instMnem, mnemonic);
response << "\"" << message << "\"";
response << "\"" << fault->message() << "\"";
return response.str();
}
};
}};
def template MicroDebugDeclare {{
class %(class_name)s : public %(base_class)s
class MicroDebugFlags : public MicroDebug
{
protected:
uint8_t cc;
public:
%(class_name)s(ExtMachInst _machInst, const char * instMnem,
uint64_t setFlags, std::string _message, uint8_t _cc);
MicroDebugFlags(ExtMachInst _machInst, const char *mnem,
const char *instMnem, uint64_t setFlags,
GenericISA::M5DebugFault *_fault, uint8_t _cc);
Fault execute(ExecContext *, Trace::InstRecord *) const;
};
}};
def template MicroDebugExecute {{
output decoder {{
MicroDebug::MicroDebug(ExtMachInst _machInst, const char *mnem,
const char *instMnem, uint64_t setFlags,
GenericISA::M5DebugFault *_fault) :
X86ISA::X86MicroopBase(_machInst, mnem, instMnem,
setFlags, No_OpClass),
fault(_fault)
{}
}};
def template MicroDebugFlagsExecute {{
Fault
%(class_name)s::execute(ExecContext *xc,
Trace::InstRecord *traceData) const
@@ -90,82 +102,95 @@ def template MicroDebugExecute {{
%(op_decl)s
%(op_rd)s
if (%(cond_test)s) {
return std::make_shared<GenericISA::M5DebugFault>(func,
message);
return %(base_class)s::execute(xc, traceData);
} else {
return NoFault;
}
}
}};
def template MicroDebugConstructor {{
def template MicroDebugFlagsConstructor {{
%(class_name)s::%(class_name)s(
ExtMachInst machInst, const char * instMnem, uint64_t setFlags,
std::string _message, uint8_t _cc) :
%(base_class)s(machInst, "%(func)s", instMnem,
setFlags, %(func_num)s, _message, _cc)
ExtMachInst machInst, const char *mnem,
const char *instMnem, uint64_t setFlags,
GenericISA::M5DebugFault *_fault, uint8_t _cc) :
%(base_class)s(machInst, mnem, instMnem, setFlags, _fault),
cc(_cc)
{
%(constructor)s;
}
}};
let {{
iop = InstObjParams("", "MicroDebugFlags", "MicroDebug",
{"code": "",
"cond_test": "checkCondition(ccFlagBits | cfofBits | \
dfBit | ecfBit | ezfBit, cc)"})
exec_output = MicroDebugFlagsExecute.subst(iop)
decoder_output = MicroDebugFlagsConstructor.subst(iop)
}};
let {{
class MicroDebug(X86Microop):
def __init__(self, message, flags=None):
def __init__(self, name, fault, message, once, flags):
self.name = name
self.fault = fault
self.message = message
if flags:
if not isinstance(flags, (list, tuple)):
raise Exception, "flags must be a list or tuple of flags"
self.cond = " | ".join(flags)
self.className += "Flags"
else:
self.cond = "0"
self.once = once
self.flags = flags
if flags and not isinstance(flags, (list, tuple)):
raise Exception, "flags must be a list or tuple of flags"
self.className = "MicroDebugFlags" if flags else "MicroDebug"
def getAllocator(self, microFlags):
allocator = '''new %(class_name)s(machInst, macrocodeBlock,
%(flags)s, "%(message)s", %(cc)s)''' % {
"class_name" : self.className,
"flags" : self.microFlagsText(microFlags),
"message" : self.message,
"cc" : self.cond}
return allocator
if self.once:
fault_allocator_template = \
"new %(fault_type)s(%(token)s, %(message)s)"
else:
fault_allocator_template = \
"new %(fault_type)s(%(message)s)"
fault_allocator = fault_allocator_template % {
"fault_type": self.fault,
"token": "std::string(\"%s\")" % self.message,
"message": "\"%s\"" % self.message
}
exec_output = ""
header_output = ""
decoder_output = ""
args = ["machInst", "\"%s\"" % self.name, "macrocodeBlock",
self.microFlagsText(microFlags), fault_allocator]
def buildDebugMicro(func, func_num):
global exec_output, header_output, decoder_output
if self.flags:
args.append(" | ".join(self.flags))
iop = InstObjParams(func, "Micro%sFlags" % func.capitalize(),
"MicroDebugBase",
{"code": "",
"func": func,
"func_num": "GenericISA::M5DebugFault::%s" % func_num,
"cond_test": "checkCondition(ccFlagBits | cfofBits | \
dfBit | ecfBit | ezfBit, cc)"})
exec_output += MicroDebugExecute.subst(iop)
header_output += MicroDebugDeclare.subst(iop)
decoder_output += MicroDebugConstructor.subst(iop)
return "new " + self.className + "(" + ", ".join(args) + ")"
iop = InstObjParams(func, "Micro%s" % func.capitalize(),
"MicroDebugBase",
{"code": "",
"func": func,
"func_num": "GenericISA::M5DebugFault::%s" % func_num,
"cond_test": "true"})
exec_output += MicroDebugExecute.subst(iop)
header_output += MicroDebugDeclare.subst(iop)
decoder_output += MicroDebugConstructor.subst(iop)
def buildDebugMicro(name, with_once=False):
global microopClasses
fault_class = "GenericISA::M5" + name.capitalize() + "Fault"
class MicroDebugChild(MicroDebug):
className = "Micro%s" % func.capitalize()
def __init__(self, message, flags=None):
super(MicroDebugChild, self).__init__(
name, fault_class, message, False, flags)
global microopClasses
microopClasses[func] = MicroDebugChild
microopClasses[name] = MicroDebugChild
buildDebugMicro("panic", "PanicFunc")
buildDebugMicro("fatal", "FatalFunc")
buildDebugMicro("warn", "WarnFunc")
buildDebugMicro("warn_once", "WarnOnceFunc")
if with_once:
fault_once_class = \
"GenericISA::M5" + name.capitalize() + "OnceFault"
name_once = name + "_once"
class MicroDebugOnceChild(MicroDebug):
def __init__(self, message, flags=None):
super(MicroDebugOnceChild, self).__init__(
name_once, fault_once_class, message, True, flags)
microopClasses[name_once] = MicroDebugOnceChild
buildDebugMicro("panic")
buildDebugMicro("fatal")
buildDebugMicro("hack", True)
buildDebugMicro("inform", True)
buildDebugMicro("warn", True)
}};