mortbopet / Ripes

A graphical processor simulator and assembly editor for the RISC-V ISA

Home Page:https://ripes.me/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Refactor instruction definitions from macros to classes

mortbopet opened this issue · comments

Currently, defining instructions for the assembler is heavily dependent on macro usage (https://github.com/mortbopet/Ripes/blob/master/src/assembler/rvassembler_common.h#L12-L108):

In most cases, classes and templates can replace macros - i certainly think that could be the case here. I'd imagine that instead of having macros here, we'd have some hierarchy of classes that inherit from Instruction. Most likely, this would result in a class hierarchy of

Instruction -> RVInstruction -> [BTypeInstr, ITypeInstr, ...]

possibly with templates sprinkled in to denote the register width that the assembler is being initialized with.

The intention of this change is to

  1. Remove macros - they're hard to debug, cryptic, and easy to get wrong, and IDEs hate them.
  2. Classes should allow us to share a bit more code, making adding new instructions simpler
  3. ... and allow us to easily add new fields to the instructions, such as more elaborate descriptions of the instruction itself, its fields, ... - without it becoming a complete mess.

@mortbopet What are your thoughts on the following re-implementation? I used the B-Type RISC-V instruction format as an example. Function definitions are excluded for brevity.

template <typename Reg_T>
class RVInstruction : public Instruction<Reg_T> {
public:
  RVInstruction(const Opcode<Reg_T> &opcode,
                const std::vector<std::shared_ptr<Field<Reg_T>>> &fields);
};

// A B-Type RISC-V instruction
template <typename Reg_T>
class BTypeInstr : public RVInstruction<Reg_T> {
public:
  BTypeInstr(const Token &name, unsigned funct3, const ISAInfoBase *isa);
  static std::shared_ptr<Instruction<Reg_T>> defineInstr(const QString &name, unsigned funct3,
                                                         const ISAInfoBase *isa);
};

I tried to use the same parameters as the current macros to keep things similar to the current implementation. With this, you can then define an instruction as such:

// old method
instructions.push_back(BType(Token("beq"), 0b000));
// new method
instructions.push_back(BTypeInstr<Reg__T>::defineInstr("beq", 0b000, isa));

I implemented this change for B-Type instructions in a fresh branch, and it seemed to work without needing to change much. The disassembler was still able to successfully disassemble B-Type instructions.