24.22.5. gem5 instruction definitions
This is one of the parts of gem5 that rely on semi-useless code generation inside the .isa
sublanguage.
Which is mostly Python, with some magic letters thrown in for good measure.
The class definitions get all dumped into one humongous C++ include file:
build/ARM/arch/arm/generated/exec-ns.cc.inc
That file defines the key methods of each instruction, e.g. the ARM immediate ADD instruction has its execute
method defined there:
Fault AddImm::execute( ExecContext *xc, Trace::InstRecord *traceData) const
or for example the key methods of an ARM 64-bit (X) STR with an immediate offset (STR <Wt>, [<Xn|SP>], #<simm>
):
Fault STRX64_IMM::execute(ExecContext *xc, Trace::InstRecord *traceData) const Fault STRX64_IMM::initiateAcc(ExecContext *xc, Trace::InstRecord *traceData) const Fault STRX64_IMM::completeAcc(PacketPtr pkt, ExecContext *xc, Trace::InstRecord *traceData) const { return NoFault; }
We also notice that the key argument passed to those instructions is of type ExecContext
, which is discussed further at: Section 24.22.6.3, “gem5 ExecContext
”.
The file is an include so that compilation can be split up into chunks by the autogenerated includers
build/ARM/arch/arm/generated/generic_cpu_1.cc build/ARM/arch/arm/generated/generic_cpu_2.cc ...
via the __SPLIT
macro as in:
#include "exec-g.cc.inc" #include "cpu/exec_context.hh" #include "decoder.hh" namespace ArmISAInst { #define __SPLIT 1 #include "exec-ns.cc.inc" }
This is likely done to not overload the C++ compiler? But sure enough overloads IDEs and GDB which takes forever to load the source of any frames going through it.
We should split that file into one per class for the love of God.
The autogenerated instruction class declarations can be found at:
build/ARM/arch/arm/generated/decoder-ns.hh.inc
and the autogenerated bulk of the decoder:
build/ARM/arch/arm/generated/decoder-ns.cc.inc
which also happens to contain the constructor definitions of the instruction classes, e.g. for the ADD immediate because why not:
AddImm::AddImm(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _op1, uint32_t _imm, bool _rotC)
The above files get tied in the autogenerated:
build/ARM/arch/arm/generated/decoder.hh
which contains:
#include "decoder-g.hh.inc" namespace ArmISAInst { #include "decoder-ns.hh.inc" }
Different instructions inherit form different classes, e.g. the ARM immediate ADD instruction is a DataImmOp
:
class AddImm : public DataImmOp { public: // Constructor AddImm(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _op1, uint32_t _imm, bool _rotC=true); Fault execute(ExecContext *, Trace::InstRecord *) const override; };
and STRX64_IMM is an ArmISA::MemoryImm64
:
class STRX64_IMM : public ArmISA::MemoryImm64 { public: /// Constructor. STRX64_IMM(ExtMachInst machInst, IntRegIndex _dest, IntRegIndex _base, int64_t _imm); Fault execute(ExecContext *, Trace::InstRecord *) const override; Fault initiateAcc(ExecContext *, Trace::InstRecord *) const override; Fault completeAcc(PacketPtr, ExecContext *, Trace::InstRecord *) const override; void annotateFault(ArmFault *fault) override { fault->annotate(ArmFault::SAS, 3); fault->annotate(ArmFault::SSE, false); fault->annotate(ArmFault::SRT, dest); fault->annotate(ArmFault::SF, true); fault->annotate(ArmFault::AR, false); } };
but different memory instructions can have different base classes too e.g. STXR:
class STXRX64 : public ArmISA::MemoryEx64
A summarized class hierarchy for the above is:
-
StaticInst
-
ArmISA::ArmStaticInst
-
ArmISA::PredOp
-
ArmISA::DataImmOp
-
ArmISA::AddImm
-
-
ArmISA::MightBeMicro64
-
ArmISA::Memory64
-
ArmISA::MemoryImm64
-
ArmISA::MemoryEx64
-
ArmISA::STXRX64
-
-
-
-
-
-
-
Tested in gem5 b1623cb2087873f64197e503ab8894b5e4d4c7b4.