Skip to content

Commit

Permalink
Move instruction set from Assembler to ISAInfoBase (#323)
Browse files Browse the repository at this point in the history
* Move instruction set vector from Assembler to ISAInfo

* Fix clang-format

* Add back runtime errors to assembler initializations

* Change ISA-specific assemblers into a single template class

This change should complete #303.

* Throw runtime error in `constructAssemblerDynamic()`

Also added doc comment.
  • Loading branch information
raccog authored Dec 6, 2023
1 parent 42ac2d4 commit f6130db
Show file tree
Hide file tree
Showing 30 changed files with 347 additions and 329 deletions.
22 changes: 22 additions & 0 deletions src/assembler/assembler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "assembler.h"

namespace Ripes {
namespace Assembler {

std::shared_ptr<AssemblerBase>
constructAssemblerDynamic(const std::shared_ptr<const ISAInfoBase> &isa) {
if (auto rv32isa =
std::dynamic_pointer_cast<const ISAInfo<ISA::RV32I>>(isa)) {
return std::make_shared<ISA_Assembler<ISA::RV32I>>(rv32isa);
} else if (auto rv64isa =
std::dynamic_pointer_cast<const ISAInfo<ISA::RV64I>>(isa)) {
return std::make_shared<ISA_Assembler<ISA::RV64I>>(rv64isa);
}

throw std::runtime_error(
std::string("Cannot dynamically construct assembler for isa: ") +
isa->name().toStdString());
}

} // namespace Assembler
} // namespace Ripes
150 changes: 98 additions & 52 deletions src/assembler/assembler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@

#include <QRegularExpression>

#include "assembler_defines.h"
#include "isa/instruction.h"
#include "isa/isa_defines.h"
#include "isa/isa_types.h"
#include "isa/pseudoinstruction.h"
#include "matcher.h"

#include <cstdint>
#include <numeric>
#include <set>
#include <variant>

#include "STLExtras.h"
#include "assembler_defines.h"
#include "assemblerbase.h"
#include "gnudirectives.h"
#include "isa/instruction.h"
#include "isa/isa_defines.h"
#include "isa/isa_types.h"
#include "isa/isainfo.h"
#include "isa/pseudoinstruction.h"
#include "matcher.h"
#include "ripessettings.h"

namespace Ripes {
namespace Assembler {
Expand Down Expand Up @@ -65,7 +66,7 @@ class Assembler : public AssemblerBase {
"Register type must be integer");

public:
explicit Assembler(const ISAInfoBase *isa) : m_isa(isa) {}
explicit Assembler(std::shared_ptr<const ISAInfoBase> isa) : m_isa(isa) {}

AssembleResult
assemble(const QStringList &programLines, const SymbolMap *symbols = nullptr,
Expand Down Expand Up @@ -177,12 +178,19 @@ class Assembler : public AssemblerBase {
return opcodes;
}

void setRelocations(const RelocationsVec &relocations) {
if (m_relocations.size() != 0) {
const InstrVec &getInstructionSet() const override {
return m_isa->instructions();
}

const PseudoInstrVec &getPseudoInstructionSet() const override {
return m_isa->pseudoInstructions();
}

void setRelocations() {
if (m_relocationsMap.size() != 0) {
throw std::runtime_error("Directives already set");
}
m_relocations = relocations;
for (const auto &iter : m_relocations) {
for (const auto &iter : m_isa->relocations()) {
const auto relocation = iter.get()->name();
if (m_relocationsMap.count(relocation) != 0) {
throw std::runtime_error("Error: relocation " +
Expand Down Expand Up @@ -541,13 +549,11 @@ class Assembler : public AssemblerBase {
return assembledWith->assemble(line);
}

void setPseudoInstructions(const PseudoInstrVec &pseudoInstructions) {
if (m_pseudoInstructions.size() != 0) {
void setPseudoInstructions() {
if (m_pseudoInstructionMap.size() != 0) {
throw std::runtime_error("Pseudoinstructions already set");
}
m_pseudoInstructions = pseudoInstructions;

for (const auto &iter : m_pseudoInstructions) {
for (const auto &iter : m_isa->pseudoInstructions()) {
const auto instr_name = iter.get()->name();
if (m_pseudoInstructionMap.count(instr_name) != 0) {
throw std::runtime_error("Error: pseudo-instruction with opcode '" +
Expand All @@ -558,6 +564,21 @@ class Assembler : public AssemblerBase {
}
}

void setInstructions() {
if (m_instructionMap.size() != 0) {
throw std::runtime_error("Instructions already set");
}
for (const auto &iter : m_isa->instructions()) {
const auto instr_name = iter.get()->name();
if (m_instructionMap.count(instr_name) != 0) {
throw std::runtime_error("Error: instruction with opcode '" +
instr_name.toStdString() +
"' has already been registerred.");
}
m_instructionMap[instr_name] = iter;
}
}

/**
* @brief splitRelocationsFromLine
* Identify relocations in tokens; when found, add the relocation to the
Expand Down Expand Up @@ -589,60 +610,85 @@ class Assembler : public AssemblerBase {
return {remainingTokens};
}

void initialize(const InstrVec &instructions,
const PseudoInstrVec &pseudoinstructions,
const DirectiveVec &directives,
const RelocationsVec &relocations) {
setInstructions(instructions);
setPseudoInstructions(pseudoinstructions);
void initialize(const DirectiveVec &directives) {
setInstructions();
setPseudoInstructions();
setDirectives(directives);
setRelocations(relocations);
m_matcher = std::make_unique<Matcher>(m_instructions);
}

void setInstructions(const InstrVec &instructions) {
if (m_instructions.size() != 0) {
throw std::runtime_error("Instructions already set");
}
m_instructions = instructions;
for (const auto &iter : m_instructions) {
const auto instr_name = iter.get()->name();
if (m_instructionMap.count(instr_name) != 0) {
throw std::runtime_error("Error: instruction with opcode '" +
instr_name.toStdString() +
"' has already been registerred.");
}
m_instructionMap[instr_name] = iter;
}
setRelocations();
m_matcher = std::make_unique<Matcher>(m_isa->instructions());
}

/**
* @brief m_instructions is the set of instructions which can be matched from
* an instruction string as well as be disassembled from a program.
* @brief m_instructionMap contains the set of instructions which can be
* matched from an instruction string as well as be disassembled from a
* program.
*/
InstrVec m_instructions;
InstrMap m_instructionMap;

/**
* @brief m_pseudoInstructions is the set of instructions which can be matched
* from an instruction string but cannot be disassembled from a program.
* Typically, pseudoinstructions will expand to one or more non-pseudo
* instructions.
* @brief m_pseudoInstructionMap contains the set of instructions which can be
* matched from an instruction string but cannot be disassembled from a
* program. Typically, pseudoinstructions will expand to one or more
* non-pseudo instructions.
*/
PseudoInstrVec m_pseudoInstructions;
PseudoInstrMap m_pseudoInstructionMap;

/**
* @brief m_relocations is the set of supported assembler relocation hints
* @brief m_relocationMap contains the set of supported assembler relocation
* hints
*/
RelocationsVec m_relocations;
RelocationsMap m_relocationsMap;

std::unique_ptr<Matcher> m_matcher;

const ISAInfoBase *m_isa;
std::shared_ptr<const ISAInfoBase> m_isa;
};

/// An Assembler and QObject (workaround because QObject cannot be directly
/// subclassed by ISA_Assembler)
class QAssembler : public QObject, public Assembler {
Q_OBJECT
public:
QAssembler(std::shared_ptr<const ISAInfoBase> isaInfo) : Assembler(isaInfo) {
// Initialize segment pointers and monitor settings changes to segment
// pointers
connect(RipesSettings::getObserver(RIPES_SETTING_ASSEMBLER_TEXTSTART),
&SettingObserver::modified, this, [this](const QVariant &value) {
setSegmentBase(".text", value.toULongLong());
});
RipesSettings::getObserver(RIPES_SETTING_ASSEMBLER_TEXTSTART)->trigger();
connect(RipesSettings::getObserver(RIPES_SETTING_ASSEMBLER_DATASTART),
&SettingObserver::modified, this, [this](const QVariant &value) {
setSegmentBase(".data", value.toULongLong());
});
RipesSettings::getObserver(RIPES_SETTING_ASSEMBLER_DATASTART)->trigger();
connect(RipesSettings::getObserver(RIPES_SETTING_ASSEMBLER_BSSSTART),
&SettingObserver::modified, this, [this](const QVariant &value) {
setSegmentBase(".bss", value.toULongLong());
});
RipesSettings::getObserver(RIPES_SETTING_ASSEMBLER_BSSSTART)->trigger();
}
};

/// An ISA-specific assembler
template <ISA isa>
struct ISA_Assembler : public QAssembler {
ISA_Assembler(std::shared_ptr<const ISAInfo<isa>> isaInfo)
: QAssembler(isaInfo) {
initialize(gnuDirectives());
}

ISA getISA() const override { return isa; }

protected:
QChar commentDelimiter() const override { return '#'; }
};

/// Returns an assembler for isa
/// Throws a runtime error if the isa does not have a matching assembler
std::shared_ptr<AssemblerBase>
constructAssemblerDynamic(const std::shared_ptr<const ISAInfoBase> &isa);

} // namespace Assembler

} // namespace Ripes
12 changes: 12 additions & 0 deletions src/assembler/assemblerbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include "assembler_defines.h"
#include "directive.h"
#include "expreval.h"
#include "isa/instruction.h"
#include "isa/isainfo.h"
#include "isa/pseudoinstruction.h"
#include "isa/symbolmap.h"

namespace Ripes {
Expand All @@ -32,6 +35,9 @@ class AssemblerBase {
/// Sets the base pointer of seg to the provided 'base' value.
void setSegmentBase(Section seg, AInt base);

/// Returns the ISA that this assembler is used for.
virtual ISA getISA() const = 0;

/// Assembles an input program (represented as a list of strings). Optionally,
/// a set of predefined symbols may be provided to the assemble call. If
/// programLines does not represent the source program directly (possibly due
Expand All @@ -57,6 +63,12 @@ class AssemblerBase {
/// assembler.
virtual std::set<QString> getOpcodes() const = 0;

/// Returns the map of instructions supported by this assembler.
virtual const InstrVec &getInstructionSet() const = 0;

/// Returns the map of pseudo-instructions supported by this assembler.
virtual const PseudoInstrVec &getPseudoInstructionSet() const = 0;

/// Resolves an expression through either the built-in symbol map, or through
/// the expression evaluator.
ExprEvalRes evalExpr(const Location &location, const QString &expr) const;
Expand Down
62 changes: 0 additions & 62 deletions src/assembler/rv32i_assembler.cpp

This file was deleted.

27 changes: 0 additions & 27 deletions src/assembler/rv32i_assembler.h

This file was deleted.

Loading

0 comments on commit f6130db

Please sign in to comment.