[Assembler] Initial commit for assembler directives
MODIFY
src/assembler/assembler.h
src/assembler/assembler.h
#include <QRegularExpression>
#include "assembler_defines.h"
+#include "directive.h"
#include "isainfo.h"
#include "lexerutilities.h"
#include "matcher.h"
@@ -41,6 +42,8 @@ namespace AssemblerTmp {
template <typename ISA>
class AssemblerBase {
static_assert(std::is_base_of<ISAInfoBase, ISA>::value, "Provided ISA type must derive from ISAInfoBase");
+
+public:
using Instr = Instruction<ISA>;
using InstrMap = std::map<QString, std::shared_ptr<Instr>>;
using InstrVec = std::vector<std::shared_ptr<Instr>>;
@@ -48,6 +51,7 @@ class AssemblerBase {
using PseudoInstrMap = std::map<QString, std::shared_ptr<PseudoInstr>>;
using PseudoInstrVec = std::vector<std::shared_ptr<PseudoInstr>>;
+private:
struct LinkRequest {
unsigned sourceLine; // Source location of code which resulted in the link request
uint32_t offset; // Offset of instruction which needs link resolution
@@ -95,7 +99,7 @@ public:
DisassembleResult res;
while (i < program.size()) {
const uint32_t instructionWord = *reinterpret_cast<const uint32_t*>(program.data() + i);
- auto match = m_matcher.matchInstruction(instructionWord);
+ auto match = m_matcher->matchInstruction(instructionWord);
try {
auto& error = std::get<Error>(match);
// Unknown instruction
@@ -117,7 +121,7 @@ public:
return res;
}
- const Matcher<ISA>& getMatcher() { return m_matcher; }
+ const Matcher<ISA>& getMatcher() { return *m_matcher; }
private:
/**
@@ -288,16 +292,36 @@ private:
}
protected:
- AssemblerBase<ISA>(std::pair<InstrVec, PseudoInstrVec> instructions)
- : m_instructions(instructions.first), m_pseudoInstructions(instructions.second), m_matcher(m_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() +
+ AssemblerBase<ISA>() {}
+
+ void initialize(InstrVec& instructions, PseudoInstrVec& pseudoinstructions, DirectiveVec& directives) {
+ setInstructions(instructions);
+ setPseudoInstructions(pseudoinstructions);
+ setDirectives(directives);
+ m_matcher = std::make_unique<Matcher<ISA>>(m_instructions);
+ }
+
+ void setDirectives(DirectiveVec& directives) {
+ if (directives.size() != 0) {
+ throw std::runtime_error("Directives already set");
+ }
+ m_directives = directives;
+ for (const auto& iter : m_directives) {
+ const auto directive = iter.get()->name();
+ if (m_directivesMap.count(directive) != 0) {
+ throw std::runtime_error("Error: directive " + directive.toStdString() +
" has already been registerred.");
}
- m_instructionMap[instr_name] = iter;
+ m_directivesMap[directive] = iter;
}
+ }
+
+ void setPseudoInstructions(PseudoInstrVec& pseudoInstructions) {
+ if (m_pseudoInstructions.size() != 0) {
+ throw std::runtime_error("Pseudoinstructions already set");
+ }
+ m_pseudoInstructions = pseudoInstructions;
+
for (const auto& iter : m_pseudoInstructions) {
const auto instr_name = iter.get()->name();
if (m_pseudoInstructionMap.count(instr_name) != 0) {
@@ -308,6 +332,21 @@ protected:
}
}
+ void setInstructions(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;
+ }
+ }
+
virtual PseudoExpandRes expandPseudoOp(const TokenizedSrcLine& line) const {
if (line.tokens.empty()) {
return std::optional<std::vector<AssemblerTmp::LineTokens>>{};
@@ -338,9 +377,17 @@ protected:
return m_instructionMap.at(opcode)->assemble(line);
};
- virtual AssembleDirectiveRes assembleDirective(const TokenizedSrcLine& line) const {
- // @todo: add assembler directive assembling
+ virtual HandleDirectiveRes assembleDirective(const TokenizedSrcLine& line) const {
+ if (line.tokens.empty()) {
+ return {Error(line.sourceLine, "Empty source lines should be impossible at this point")};
+ }
+
+ const auto& directive = line.tokens.at(0);
+ if (m_directivesMap.count(directive) == 0) {
+ // Not a directive
return std::optional<QByteArray>();
+ }
+ return m_directivesMap.at(directive)->handle(line);
};
/**
* @brief splitSymbolsFromLine
@@ -425,16 +472,22 @@ protected:
* @brief m_instructions is the set of instructions which can be matched from an instruction string as well as be
* disassembled from a program.
*/
- const InstrVec& m_instructions;
+ 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.
*/
- const PseudoInstrVec& m_pseudoInstructions;
+ PseudoInstrVec m_pseudoInstructions;
PseudoInstrMap m_pseudoInstructionMap;
+ /**
+ * @brief m_assemblerDirectives is the set of supported assembler directives.
+ */
+ DirectiveVec m_directives;
+ DirectiveMap m_directivesMap;
+
/**
* @brief m_segmentPointers maintains the current end-of-segment pointers for the segments annotated by the program
* (ie. .text, .data, ...).
@@ -445,7 +498,7 @@ protected:
*/
QString m_currentSegment;
- const Matcher<ISA> m_matcher;
+ std::unique_ptr<Matcher<ISA>> m_matcher;
};
} // namespace AssemblerTmp

MODIFY
src/assembler/assembler_defines.h
src/assembler/assembler_defines.h
@@ -15,12 +15,11 @@ namespace AssemblerTmp {
using LineTokens = QStringList;
using Symbol = QString;
using Symbols = std::set<Symbol>;
-using Directive = QString;
-using Directives = std::set<Directive>;
+using Directives = std::set<QString>;
using DirectivesLinePair = std::pair<Directives, LineTokens>;
using SymbolMap = std::map<Symbol, uint32_t>;
using ReverseSymbolMap = std::map<uint32_t, Symbol>;
-using AssembleDirectiveRes = std::variant<Error, std::optional<QByteArray>>;
+using HandleDirectiveRes = std::variant<Error, std::optional<QByteArray>>;
struct TokenizedSrcLine {
Symbols symbols;
LineTokens tokens;

ADD
src/assembler/directive.h
+#pragma once
+
+#include <QByteArray>
+#include <QString>
+#include <functional>
+#include <memory>
+#include <variant>
+
+#include "assembler_defines.h"
+#include "assemblererror.h"
+
+namespace Ripes {
+namespace AssemblerTmp {
+
+class Directive {
+public:
+ using DirectiveHandler = std::function<HandleDirectiveRes(const Directive&, const TokenizedSrcLine&)>;
+ Directive(const QString& directive, const DirectiveHandler& handler) : m_directive(directive), m_handler(handler) {}
+
+ HandleDirectiveRes handle(const TokenizedSrcLine& line) { return m_handler(*this, line); }
+ const QString& name() const { return m_directive; }
+
+private:
+ const QString m_directive;
+ DirectiveHandler m_handler;
+};
+
+using DirectiveMap = std::map<QString, std::shared_ptr<Directive>>;
+using DirectiveVec = std::vector<std::shared_ptr<Directive>>;
+
+} // namespace AssemblerTmp
+} // namespace Ripes

MODIFY
src/assembler/rv32i_assembler.cpp
src/assembler/rv32i_assembler.cpp
namespace Ripes {
namespace AssemblerTmp {
-RV32I_Assembler::RV32I_Assembler(const std::set<Extensions>& extensions)
- : AssemblerBase<ISAInfo<ISA::RV32IM>>(initInstructions(extensions)) {
+RV32I_Assembler::RV32I_Assembler(const std::set<Extensions>& extensions) : AssemblerBase<ISAInfo<ISA::RV32IM>>() {
+ auto [instrs, pseudos, directives] = initInstructions(extensions);
+ initialize(instrs, pseudos, directives);
+
// Initialize segment pointers
m_segmentPointers[instrSegment()] = 0x0;
m_segmentPointers[dataSegment()] = 0x10000000;
}
-std::pair<RV32I_Assembler::RVInstrVec, RV32I_Assembler::RVPseudoInstrVec>
+std::tuple<RV32I_Assembler::RVInstrVec, RV32I_Assembler::RVPseudoInstrVec, DirectiveVec>
RV32I_Assembler::initInstructions(const std::set<Extensions>& extensions) const {
RVInstrVec instructions;
RVPseudoInstrVec pseudoInstructions;
@@ -31,7 +33,7 @@ RV32I_Assembler::initInstructions(const std::set<Extensions>& extensions) const
assert(false && "Unhandled ISA extension");
}
}
- return {instructions, pseudoInstructions};
+ return {instructions, pseudoInstructions, {}};
}
#define BType(name, funct3) \

MODIFY
src/assembler/rv32i_assembler.h
src/assembler/rv32i_assembler.h
@@ -19,7 +19,8 @@ public:
RV32I_Assembler(const std::set<Extensions>& extensions);
public:
- std::pair<RVInstrVec, RVPseudoInstrVec> initInstructions(const std::set<Extensions>& extensions) const;
+ std::tuple<RVInstrVec, RVPseudoInstrVec, DirectiveVec>
+ initInstructions(const std::set<Extensions>& extensions) const;
/**
* Extension enablers

[Assembler] Initial commit for assembler directives