[Assembler] AssemblerBase owns instructions and pseudoinstructions
DELETE
src/assembler/assembler.cpp
-#include "assembler.h"
-
-#include <algorithm>
-
-#include <QRegExp>
-#include <QRegularExpression>
-#include <QString>
-
-namespace {
-/**
- * A macro for running an assembler pass with error handling
- */
-#define runPass(resName, resType, passFunction, ...) \
- auto passFunction##_res = passFunction(__VA_ARGS__); \
- try { \
- auto& errors = std::get<Errors>(passFunction##_res); \
- result.errors.insert(result.errors.end(), errors.begin(), errors.end()); \
- return result; \
- } catch (const std::bad_variant_access&) { \
- } \
- auto resName = std::get<resType>(passFunction##_res);
-
-/**
- * A macro for running an ISA-specific assembler instruction with error handling
- */
-#define runOperation(resName, resType, operationFunction, ...) \
- auto operationFunction##_res = operationFunction(__VA_ARGS__); \
- try { \
- errors.push_back(std::get<Error>(operationFunction##_res)); \
- continue; \
- } catch (const std::bad_variant_access&) { \
- } \
- auto resName = std::get<resType>(operationFunction##_res);
-
-} // namespace
-
-namespace Ripes {
-namespace AssemblerTmp {
-
-Result AssemblerBase::assemble(const QString& program) const {
- const auto programLines = program.split(QRegExp("[\r\n]"));
- Result result;
-
- runPass(tokenizedLines, Program, pass0, programLines);
-
- // Pseudo SourceLine expansion
- runPass(unused, Program, pass1, tokenizedLines);
-
- // Symbol mapping
- Symbols symbols;
- runPass(unused2, NoPassResult, pass2, tokenizedLines, symbols);
-
- // Machine code translation
- // runPass(unused3, QByteArray, pass3, tokenizedLines, symbols);
-}
-
-std::variant<Errors, Program> AssemblerBase::pass0(const QStringList& program) const {
- Errors errors;
- Program tokenizedLines;
- tokenizedLines.reserve(program.size());
- for (unsigned i = 0; i < program.size(); i++) {
- const auto& line = program.at(i);
- if (line.isEmpty())
- continue;
- runOperation(tokenizedLine, LineTokens, tokenize, program[i]);
- tokenizedLines.push_back({tokenizedLine, i, {}});
- }
- if (errors.size() != 0) {
- return {errors};
- } else {
- return {tokenizedLines};
- }
-}
-
-std::variant<Errors, Program> AssemblerBase::pass1(const Program& tokenizedLines) const {
- Errors errors;
- Program expandedLines;
- expandedLines.reserve(tokenizedLines.size());
-
- for (unsigned i = 0; i < tokenizedLines.size(); i++) {
- const auto& tokenizedLine = tokenizedLines.at(i);
- runOperation(expandedOps, std::optional<std::vector<LineTokens>>, expandPseudoOp, tokenizedLine);
- if (expandedOps) {
- for (const auto& expandedOp : expandedOps.value()) {
- /** @note: Original source line is kept for all resulting lines after pseudo-op expantion */
- expandedLines.push_back({expandedOp, tokenizedLine.source_line, {}});
- }
- } else {
- expandedLines.push_back(tokenizedLine);
- }
- }
-
- if (errors.size() != 0) {
- return {errors};
- } else {
- return {expandedLines};
- }
-}
-
-std::variant<Errors, NoPassResult> AssemblerBase::pass2(std::vector<SourceLine>& tokenizedLines,
- Symbols& symbols) const {
- Errors errors;
- for (auto& line : tokenizedLines) {
- runOperation(splitLine, SymbolLinePair, splitSymbolFromLine, line);
- const auto& [symbol, rest] = splitLine;
- if (!symbol.isEmpty()) {
- if (symbols.count(symbol)) {
- errors.push_back({line.source_line, "Symbol '" + symbol + "' is already defined."});
- }
- line.symbol = symbol;
- }
- }
-
- if (errors.size() != 0) {
- return {errors};
- } else {
- return {NoPassResult()};
- }
-}
-
-std::variant<Errors, QByteArray> AssemblerBase::pass3(const Program& tokenizedLines, const SymbolMap& symbolMap) const {
- QByteArray program;
- Errors errors;
- for (const auto& line : tokenizedLines) {
- runOperation(machineCode, QByteArray, assembleInstruction, line, symbolMap);
- program.append(machineCode);
- }
- if (errors.size() != 0) {
- return {errors};
- } else {
- return {program};
- }
-}
-
-std::variant<Error, LineTokens> AssemblerBase::tokenize(const QString& line) const {
- const static auto splitter = QRegularExpression(
- R"(\t|\((?=x(?:[1-2]\d|3[0-1]|\d)|t[0-6]|a[0-7]|s(?:1[0-1]|\d)|[sgt]p|zero)|(?:x(?:[1-2]\d|3[0-1]|\d)|t[0-6]|a[0-7]|s(?:1[0-1]|\d)|[sgt]p|zero)\K\))");
-
- return line.split(splitter);
-}
-
-std::variant<Error, std::pair<QString, LineTokens>> AssemblerBase::splitSymbolFromLine(const SourceLine& line) const {
- if (line.tokens.size() == 0) {
- return std::pair<QString, LineTokens>(QString(), line.tokens);
- }
-
- const bool hasSymbol =
- std::any_of(line.tokens.begin(), line.tokens.end(), [](const auto& s) { return s.contains(':'); });
-
- const auto& firstToken = line.tokens.at(0);
-
- if (hasSymbol && !firstToken.contains(':')) {
- return Error(line.source_line, "Line must start with symbol");
- }
- if (firstToken.count(':') > 1) {
- return Error(line.source_line, "Multiple instances of ':' in label");
- }
-
- // Valid symbol - parse the symbol and remaining tokens
- auto remainingTokens = line.tokens;
- remainingTokens.removeFirst();
- auto symbol = firstToken;
- symbol.remove(':');
- return std::pair<QString, LineTokens>(symbol, remainingTokens);
-}
-
-} // namespace AssemblerTmp
-} // namespace Ripes

MODIFY
src/assembler/assembler.h
src/assembler/assembler.h
#pragma once
+#include <QRegularExpression>
+
#include "assembler_defines.h"
#include "isainfo.h"
+#include "matcher.h"
#include <set>
#include <variant>
namespace Ripes {
namespace AssemblerTmp {
+/**
+ * A macro for running an assembler pass with error handling
+ */
+#define runPass(resName, resType, passFunction, ...) \
+ auto passFunction##_res = passFunction(__VA_ARGS__); \
+ try { \
+ auto& errors = std::get<Errors>(passFunction##_res); \
+ result.errors.insert(result.errors.end(), errors.begin(), errors.end()); \
+ return result; \
+ } catch (const std::bad_variant_access&) { \
+ } \
+ auto resName = std::get<resType>(passFunction##_res);
+
+/**
+ * A macro for running an ISA-specific assembler instruction with error handling
+ */
+#define runOperation(resName, resType, operationFunction, ...) \
+ auto operationFunction##_res = operationFunction(__VA_ARGS__); \
+ try { \
+ errors.push_back(std::get<Error>(operationFunction##_res)); \
+ continue; \
+ } catch (const std::bad_variant_access&) { \
+ } \
+ auto resName = std::get<resType>(operationFunction##_res);
+
+template <typename ISA>
class AssemblerBase {
+ using Instr = Instruction<ISA>;
+ using InstrVec = std::vector<std::shared_ptr<Instr>>;
+ using PseudoInstr = PseudoInstruction<ISA>;
+ using PseudoInstrVec = std::vector<std::shared_ptr<PseudoInstr>>;
+
public:
- Result assemble(const QString& program) const;
+ Result assemble(const QString& program) const {
+ const auto programLines = program.split(QRegExp("[\r\n]"));
+ Result result;
+
+ runPass(tokenizedLines, Program, pass0, programLines);
+
+ // Pseudo SourceLine expansion
+ runPass(unused, Program, pass1, tokenizedLines);
+
+ // Symbol mapping
+ Symbols symbols;
+ runPass(unused2, NoPassResult, pass2, tokenizedLines, symbols);
+
+ // Machine code translation
+ // runPass(unused3, QByteArray, pass3, tokenizedLines, symbols);
+ }
+ QString disassemble(const QByteArray& program) const;
private:
/**
* @brief pass0
* Line tokenization and source line recording
*/
- std::variant<Errors, Program> pass0(const QStringList& program) const;
+ std::variant<Errors, Program> pass0(const QStringList& program) const {
+ Errors errors;
+ Program tokenizedLines;
+ tokenizedLines.reserve(program.size());
+ for (unsigned i = 0; i < program.size(); i++) {
+ const auto& line = program.at(i);
+ if (line.isEmpty())
+ continue;
+ runOperation(tokenizedLine, LineTokens, tokenize, program[i]);
+ tokenizedLines.push_back({tokenizedLine, i, {}});
+ }
+ if (errors.size() != 0) {
+ return {errors};
+ } else {
+ return {tokenizedLines};
+ }
+ }
/**
* @brief pass1
* Pseudo-op expansion. If @return errors is empty, pass succeeded.
*/
- std::variant<Errors, Program> pass1(const Program& tokenizedLines) const;
+ std::variant<Errors, Program> pass1(const Program& tokenizedLines) const {
+ Errors errors;
+ Program expandedLines;
+ expandedLines.reserve(tokenizedLines.size());
+
+ for (unsigned i = 0; i < tokenizedLines.size(); i++) {
+ const auto& tokenizedLine = tokenizedLines.at(i);
+ runOperation(expandedOps, std::optional<std::vector<LineTokens>>, expandPseudoOp, tokenizedLine);
+ if (expandedOps) {
+ for (const auto& expandedOp : expandedOps.value()) {
+ /** @note: Original source line is kept for all resulting lines after pseudo-op expantion */
+ expandedLines.push_back({expandedOp, tokenizedLine.source_line, {}});
+ }
+ } else {
+ expandedLines.push_back(tokenizedLine);
+ }
+ }
+
+ if (errors.size() != 0) {
+ return {errors};
+ } else {
+ return {expandedLines};
+ }
+ }
/**
* @brief pass2
* Symbol recording. If @return errors is empty, pass succeeded.
*/
- std::variant<Errors, NoPassResult> pass2(Program& tokenizedLines, Symbols& symbols) const;
+ std::variant<Errors, NoPassResult> pass2(Program& tokenizedLines, Symbols& symbols) const {
+ Errors errors;
+ for (auto& line : tokenizedLines) {
+ runOperation(splitLine, SymbolLinePair, splitSymbolFromLine, line);
+ const auto& [symbol, rest] = splitLine;
+ if (!symbol.isEmpty()) {
+ if (symbols.count(symbol)) {
+ errors.push_back({line.source_line, "Symbol '" + symbol + "' is already defined."});
+ }
+ line.symbol = symbol;
+ }
+ }
+
+ if (errors.size() != 0) {
+ return {errors};
+ } else {
+ return {NoPassResult()};
+ }
+ }
+
/**
* @brief pass3
* Machine code translation. If @return errors is empty, pass succeeded.
*/
- std::variant<Errors, QByteArray> pass3(const Program& tokenizedLines, const SymbolMap& symbolMap) const;
+ std::variant<Errors, QByteArray> pass3(const Program& tokenizedLines, const SymbolMap& symbolMap) const {
+ QByteArray program;
+ Errors errors;
+ for (const auto& line : tokenizedLines) {
+ runOperation(machineCode, QByteArray, assembleInstruction, line, symbolMap);
+ program.append(machineCode);
+ }
+ if (errors.size() != 0) {
+ return {errors};
+ } else {
+ return {program};
+ }
+ }
protected:
+ AssemblerBase<ISA>(std::pair<InstrVec, PseudoInstrVec> instructions)
+ : m_instructions(instructions.first), m_pseudoInstructions(instructions.second), m_matcher(m_instructions) {}
+
virtual std::variant<Error, std::optional<std::vector<LineTokens>>>
expandPseudoOp(const SourceLine& line) const = 0;
- virtual std::variant<Error, LineTokens> tokenize(const QString& line) const;
+ virtual std::variant<Error, LineTokens> tokenize(const QString& line) const {
+ const static auto splitter = QRegularExpression(
+ R"(\t|\((?=x(?:[1-2]\d|3[0-1]|\d)|t[0-6]|a[0-7]|s(?:1[0-1]|\d)|[sgt]p|zero)|(?:x(?:[1-2]\d|3[0-1]|\d)|t[0-6]|a[0-7]|s(?:1[0-1]|\d)|[sgt]p|zero)\K\))");
+
+ return line.split(splitter);
+ }
virtual std::variant<Error, QByteArray> assembleInstruction(const SourceLine& instruction,
const SymbolMap& symbols) const;
/**
* @brief splitSymbol
* @returns a pair consisting of a symbol and the the input @p line tokens where the symbol has been removed.
*/
- virtual std::variant<Error, SymbolLinePair> splitSymbolFromLine(const SourceLine& line) const = 0;
+ virtual std::variant<Error, SymbolLinePair> splitSymbolFromLine(const SourceLine& line) const {
+ if (line.tokens.size() == 0) {
+ return std::pair<QString, LineTokens>(QString(), line.tokens);
+ }
+
+ const bool hasSymbol =
+ std::any_of(line.tokens.begin(), line.tokens.end(), [](const auto& s) { return s.contains(':'); });
+
+ const auto& firstToken = line.tokens.at(0);
+
+ if (hasSymbol && !firstToken.contains(':')) {
+ return Error(line.source_line, "Line must start with symbol");
+ }
+ if (firstToken.count(':') > 1) {
+ return Error(line.source_line, "Multiple instances of ':' in label");
+ }
+
+ // Valid symbol - parse the symbol and remaining tokens
+ auto remainingTokens = line.tokens;
+ remainingTokens.removeFirst();
+ auto symbol = firstToken;
+ symbol.remove(':');
+ return std::pair<QString, LineTokens>(symbol, remainingTokens);
+ }
+
+ const InstrVec& m_instructions;
+ const PseudoInstrVec& m_pseudoInstructions;
+ const Matcher<ISA> m_matcher;
};
} // namespace AssemblerTmp

MODIFY
src/assembler/instruction.h
src/assembler/instruction.h
@@ -16,6 +16,7 @@ namespace AssemblerTmp {
namespace {
using AssembleRes = std::variant<AssemblerTmp::Error, uint32_t>;
+using PseudoExpandRes = std::variant<AssemblerTmp::Error, std::vector<AssemblerTmp::LineTokens>>;
using DisassembleRes = std::variant<AssemblerTmp::Error, AssemblerTmp::LineTokens>;
} // namespace
@@ -217,8 +218,7 @@ public:
return m_disassembler(instruction, address, symbolMap);
}
- const Opcode& getOpcode() const { return m_opcode; }
- const QString& name() const { return m_opcode.name; }
+ const QString& name() const { return m_opcode; }
private:
std::function<AssembleRes(const AssemblerTmp::SourceLine&)> m_assembler;
@@ -229,6 +229,34 @@ private:
const std::vector<std::shared_ptr<Field>> m_fields;
};
+template <typename ISA>
+class PseudoInstruction {
+public:
+ PseudoInstruction(
+ const QString& opcode, const std::vector<std::shared_ptr<Field>>& fields,
+ const std::function<PseudoExpandRes(const PseudoInstruction&, const AssemblerTmp::SourceLine&)>& expander)
+ : m_opcode(opcode), m_expectedTokens(1 /*opcode*/ + fields.size()), m_fields(fields), m_expander(expander) {}
+
+ PseudoExpandRes expand(const AssemblerTmp::SourceLine& line) {
+ if (line.tokens.length() != m_expectedTokens) {
+ return AssemblerTmp::Error(
+ line.source_line, "Instruction '" + m_opcode + "' expects " + QString::number(m_expectedTokens - 1) +
+ " tokens, but got " + QString::number(line.tokens.length() - 1));
+ }
+
+ return m_expander(line);
+ }
+
+ const QString& name() const { return m_opcode; }
+
+private:
+ std::function<PseudoExpandRes(const PseudoInstruction& /*this*/, const AssemblerTmp::SourceLine&)> m_expander;
+
+ const QString m_opcode;
+ const int m_expectedTokens;
+ const std::vector<std::shared_ptr<Field>> m_fields;
+};
+
} // namespace AssemblerTmp
} // namespace Ripes

MODIFY
src/assembler/rv32i_assembler.cpp
src/assembler/rv32i_assembler.cpp
namespace Ripes {
namespace AssemblerTmp {
-namespace {
-inline QByteArray uint32ToByteArr(uint32_t in) {
- const char bytes[] = {static_cast<char>(in & 0xff), static_cast<char>((in >> 8) & 0xff),
- static_cast<char>((in >> 16) & 0xff), static_cast<char>((in >> 24) & 0xff)};
- return QByteArray::fromRawData(bytes, 4);
-}
-
-namespace instrType {
-enum {
- LUI = 0b0110111,
- JAL = 0b1101111,
- JALR = 0b1100111,
- BRANCH = 0b1100011,
- LOAD = 0b0000011,
- STORE = 0b0100011,
- OP_IMM = 0b0010011,
- OP = 0b0110011,
- ECALL = 0b1110011,
- AUIPC = 0b0010111,
- INVALID = 0b0
-};
-}
-
-const static QMap<QString, uint32_t> ABInames{
- {"zero", 0}, {"ra", 1}, {"sp", 2}, {"gp", 3}, {"tp", 4}, {"t0", 5}, {"t1", 6}, {"t2", 7},
- {"s0", 8}, {"a0", 10}, {"a1", 11}, {"a2", 12}, {"a3", 13}, {"a4", 14}, {"a5", 15}, {"a6", 16},
- {"a7", 17}, {"s1", 9}, {"s2", 18}, {"s3", 19}, {"s4", 20}, {"s5", 21}, {"s6", 22}, {"s7", 23},
- {"s8", 24}, {"s9", 25}, {"s10", 26}, {"s11", 27}, {"t3", 28}, {"t4", 29}, {"t5", 30}, {"t6", 31}};
-
-uint32_t getRegisterNumber(const QString& reg, bool& success) {
- // Converts a textual representation of a register to its numeric value
- QString regRes = reg;
- success = true;
- if (reg[0] == 'x') {
- regRes.remove('x');
- return regRes.toInt(&success, 10);
- } else if (ABInames.contains(reg)) {
- return ABInames[reg];
- }
- success = false;
- return 0;
-}
-
-/** @brief
- * Wraps a functor with the expectancy of a line containing @p N tokens.
- */
-template <int N, typename T_funct, typename T_ret, typename... Args>
-T_funct expectNTokens(const T_funct& functor) {
- return [=](const AssemblerTmp::SourceLine& line, Args... others) {
- if (line.tokens.size() != N) {
- return std::variant<AssemblerTmp::Error, T_ret>({AssemblerTmp::Error(
- line.source_line, "Expected " + QString::number(N) + " tokens but got " + line.tokens.size())});
- } else {
- return functor(line, others...);
- }
- };
-}
-
-std::variant<AssemblerTmp::Error, std::vector<uint32_t>> getRegisters(const AssemblerTmp::SourceLine& line,
- const std::vector<int>& indicies) {
- bool success;
- std::vector<uint32_t> regs(indicies.size());
- for (const int& index : indicies) {
- const auto& regToken = line.tokens[index];
- const uint32_t reg = getRegisterNumber(regToken, success);
- if (!success) {
- return {AssemblerTmp::Error(line.source_line, "Unknown register '" + regToken + "'")};
- }
- regs.push_back(reg);
- }
- return regs;
-}
-
-template <uint32_t funct3, uint32_t funct7>
-std::variant<AssemblerTmp::Error, QByteArray> assembleOpInstruction(const AssemblerTmp::SourceLine& line) {
- auto regsVariant = getRegisters(line, {1, 2, 3});
- try {
- return std::get<AssemblerTmp::Error>(regsVariant);
- } catch (std::bad_variant_access&) {
- }
- auto& regs = std::get<std::vector<uint32_t>>(regsVariant);
- return uint32ToByteArr(instrType::OP | funct3 << 12 | funct7 << 25 | regs[0] << 7 | regs[1] << 15 | regs[2] << 20);
-}
-
-} // namespace
-
-RV32I_Assembler::RV32I_Assembler(const std::set<Extensions>& extensions) {
- enableExtI();
+RV32I_Assembler::RV32I_Assembler(const std::set<Extensions>& extensions)
+ : AssemblerBase<ISAInfo<ISA::RV32IM>>(initInstructions(extensions)) {}
+
+std::pair<RV32I_Assembler::RVInstrVec, RV32I_Assembler::RVPseudoInstrVec>
+RV32I_Assembler::initInstructions(const std::set<Extensions>& extensions) const {
+ RVInstrVec instructions;
+ RVPseudoInstrVec pseudoInstructions;
+
+ enableExtI(instructions, pseudoInstructions);
for (const auto& extension : extensions) {
switch (extension) {
case Extensions::M:
- enableExtM();
+ enableExtM(instructions, pseudoInstructions);
break;
case Extensions::F:
- enableExtF();
+ enableExtF(instructions, pseudoInstructions);
break;
default:
assert(false && "Unhandled ISA extension");
}
}
+ return {instructions, pseudoInstructions};
}
std::variant<AssemblerTmp::Error, std::optional<std::vector<AssemblerTmp::LineTokens>>>
@@ -145,38 +67,147 @@ void RV32I_Assembler::addAssemblerFunctor(const QString& opcode, AssemblerFuncto
}
m_assemblers[opcode] = functor;
}
-
-void RV32I_Assembler::enableExtI() {
+#define BType(name, funct3) \
+ std::shared_ptr<Instruction<ISAInfo<ISA::RV32IM>>>(new Instruction<ISAInfo<ISA::RV32IM>>( \
+ Opcode(name, {OpPart(0b1100011, 0, 6), OpPart(funct3, 12, 14)}), \
+ {std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(1, 15, 19), \
+ std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(2, 20, 24), \
+ std::make_shared<Imm>( \
+ 3, 13, Imm::Repr::Signed, \
+ std::vector{ImmPart(12, 31, 31), ImmPart(11, 7, 7), ImmPart(5, 25, 30), ImmPart(1, 8, 11)}, \
+ Imm::SymbolType::Relative)}))
+
+#define IType(name, funct3) \
+ std::shared_ptr<Instruction<ISAInfo<ISA::RV32IM>>>(new Instruction<ISAInfo<ISA::RV32IM>>( \
+ Opcode(name, {OpPart(0b0010011, 0, 6), OpPart(funct3, 12, 14)}), \
+ {std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(1, 7, 11), \
+ std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(2, 15, 19), \
+ std::make_shared<Imm>(3, 12, Imm::Repr::Signed, std::vector{ImmPart(0, 20, 31)})}))
+
+#define LoadType(name, funct3) \
+ std::shared_ptr<Instruction<ISAInfo<ISA::RV32IM>>>(new Instruction<ISAInfo<ISA::RV32IM>>( \
+ Opcode(name, {OpPart(0b0000011, 0, 6), OpPart(funct3, 12, 14)}), \
+ {std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(1, 7, 11), \
+ std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(3, 15, 19), \
+ std::make_shared<Imm>(2, 12, Imm::Repr::Signed, std::vector{ImmPart(0, 20, 31)})}))
+
+#define IShiftType(name, funct3, funct7) \
+ std::shared_ptr<Instruction<ISAInfo<ISA::RV32IM>>>(new Instruction<ISAInfo<ISA::RV32IM>>( \
+ Opcode(name, {OpPart(0b0010011, 0, 6), OpPart(funct3, 12, 14), OpPart(funct7, 25, 31)}), \
+ {std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(1, 7, 11), \
+ std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(2, 15, 19), \
+ std::make_shared<Imm>(3, 5, Imm::Repr::Unsigned, std::vector{ImmPart(0, 20, 24)})}))
+
+#define RType(name, funct3, funct7) \
+ std::shared_ptr<Instruction<ISAInfo<ISA::RV32IM>>>(new Instruction<ISAInfo<ISA::RV32IM>>( \
+ Opcode(name, {OpPart(0b0110011, 0, 6), OpPart(funct3, 12, 14), OpPart(funct7, 25, 31)}), \
+ {std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(1, 7, 11), \
+ std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(2, 15, 19), \
+ std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(3, 20, 24)}))
+
+#define SType(name, funct3) \
+ std::shared_ptr<Instruction<ISAInfo<ISA::RV32IM>>>(new Instruction<ISAInfo<ISA::RV32IM>>( \
+ Opcode(name, {OpPart(0b0100011, 0, 6), OpPart(funct3, 12, 14)}), \
+ {std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(3, 15, 19), \
+ std::make_shared<Imm>(2, 12, Imm::Repr::Signed, std::vector{ImmPart(5, 25, 31), ImmPart(0, 7, 11)}), \
+ std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(1, 20, 24)}))
+
+#define UType(name, opcode) \
+ std::shared_ptr<Instruction<ISAInfo<ISA::RV32IM>>>(new Instruction<ISAInfo<ISA::RV32IM>>( \
+ Opcode(name, {OpPart(opcode, 0, 6)}), \
+ {std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(1, 7, 11), \
+ std::make_shared<Imm>(2, 32, Imm::Repr::Hex, std::vector{ImmPart(12, 12, 31)})}))
+
+#define JType(name, opcode) \
+ std::shared_ptr<Instruction<ISAInfo<ISA::RV32IM>>>(new Instruction<ISAInfo<ISA::RV32IM>>( \
+ Opcode(name, {OpPart(opcode, 0, 6)}), \
+ {std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(1, 7, 11), \
+ std::make_shared<Imm>( \
+ 2, 21, Imm::Repr::Hex, \
+ std::vector{ImmPart(20, 31, 31), ImmPart(12, 12, 19), ImmPart(11, 20, 20), ImmPart(1, 21, 30)}, \
+ Imm::SymbolType::Relative)}))
+
+#define JALRType(name) \
+ std::shared_ptr<Instruction<ISAInfo<ISA::RV32IM>>>(new Instruction<ISAInfo<ISA::RV32IM>>( \
+ Opcode(name, {OpPart(0b1100111, 0, 6), OpPart(0b000, 12, 14)}), \
+ {std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(1, 7, 11), \
+ std::make_shared<Reg<ISAInfo<ISA::RV32IM>>>(2, 15, 19), \
+ std::make_shared<Imm>(3, 12, Imm::Repr::Signed, std::vector{ImmPart(0, 20, 31)})}))
+
+void RV32I_Assembler::enableExtI(RVInstrVec& instructions, RVPseudoInstrVec& pseudoInstructions) const {
// Pseudo-op functors
- addPseudoOpExpanderFunctor("nop", expectNTokens<1, PseudoOpFunctor, PseudoOpFunctorRetT>([](const SourceLine&) {
+ pseudoInstructions.push_back(std::shared_ptr<RVPseudoInstr>(
+ new RVPseudoInstr("nop", {}, [](const RVPseudoInstr& _this, const AssemblerTmp::SourceLine& line) {
return std::vector<AssemblerTmp::LineTokens>{QString("addi x0 x0 0").split(' ')};
- }));
+ })));
- // Assembler functors
-}
+ pseudoInstructions.push_back(std::shared_ptr<RVPseudoInstr>(
+ new RVPseudoInstr("mv", {}, [](const RVPseudoInstr& _this, const AssemblerTmp::SourceLine& line) {
+ return std::vector<AssemblerTmp::LineTokens>{QString("addi x0 x0 0").split(' ')};
+ })));
-void RV32I_Assembler::enableExtM() {
- // Pseudo-op functors
+ /* m_expander = [this](const PseudoInstruction& _this, const AssemblerTmp::SourceLine& line) {
+ uint32_t instruction = 0;
+ m_opcode.apply(line, instruction);
+ for (const auto& field : m_fields)
+ field->apply(line, instruction);
+ return instruction;
+ };*/
// Assembler functors
+ instructions.push_back(UType("lui", 0b0110111));
+ instructions.push_back(UType("auipc", 0b0010111));
+
+ instructions.push_back(JType("jal", 0b1101111));
+
+ instructions.push_back(JALRType("jalr"));
+
+ instructions.push_back(LoadType("lb", 0b000));
+ instructions.push_back(LoadType("lh", 0b001));
+ instructions.push_back(LoadType("lw", 0b010));
+ instructions.push_back(LoadType("lbu", 0b100));
+ instructions.push_back(LoadType("lhu", 0b101));
+
+ instructions.push_back(SType("sb", 0b000));
+ instructions.push_back(SType("sh", 0b001));
+ instructions.push_back(SType("sw", 0b010));
+
+ instructions.push_back(IType("addi", 0b000));
+ instructions.push_back(IType("slti", 0b010));
+ instructions.push_back(IType("sltiu", 0b011));
+ instructions.push_back(IType("xori", 0b100));
+ instructions.push_back(IType("ori", 0b110));
+ instructions.push_back(IType("andi", 0b111));
+
+ instructions.push_back(IShiftType("slli", 0b001, 0b0000000));
+ instructions.push_back(IShiftType("srli", 0b101, 0b0000000));
+ instructions.push_back(IShiftType("srai", 0b101, 0b0100000));
+
+ instructions.push_back(RType("add", 0b000, 0b0000000));
+ instructions.push_back(RType("sub", 0b000, 0b0100000));
+ instructions.push_back(RType("sll", 0b001, 0b0000000));
+ instructions.push_back(RType("slt", 0b010, 0b0000000));
+ instructions.push_back(RType("sltu", 0b011, 0b0000000));
+ instructions.push_back(RType("xor", 0b100, 0b0000000));
+ instructions.push_back(RType("srl", 0b101, 0b0000000));
+ instructions.push_back(RType("sra", 0b101, 0b0100000));
+ instructions.push_back(RType("or", 0b110, 0b0000000));
+ instructions.push_back(RType("and", 0b111, 0b0000000));
+
+ instructions.push_back(BType("beq", 0b000));
+ instructions.push_back(BType("bne", 0b001));
+ instructions.push_back(BType("blt", 0b100));
+ instructions.push_back(BType("bge", 0b101));
+ instructions.push_back(BType("bltu", 0b110));
+ instructions.push_back(BType("bgeu", 0b111));
+}
+
+void RV32I_Assembler::enableExtM(RVInstrVec& instructions, RVPseudoInstrVec& pseudoInstructions) const {
+ // Pseudo-op functors
- addAssemblerFunctor("mul", expectNTokens<3, AssemblerFunctor, AssemblerFunctorRetT, const AssemblerTmp::SymbolMap&>(
- [](const SourceLine& line, const AssemblerTmp::SymbolMap&) {
- return assembleOpInstruction<0b000, 0b0000001>(line);
- }));
-
- addAssemblerFunctor("mulh",
- expectNTokens<3, AssemblerFunctor, AssemblerFunctorRetT, const AssemblerTmp::SymbolMap&>(
- [](const SourceLine& line, const AssemblerTmp::SymbolMap&) {
- return assembleOpInstruction<0b001, 0b0000001>(line);
- }));
- addAssemblerFunctor("mulhsu",
- expectNTokens<3, AssemblerFunctor, AssemblerFunctorRetT, const AssemblerTmp::SymbolMap&>(
- [](const SourceLine& line, const AssemblerTmp::SymbolMap&) {
- return assembleOpInstruction<0b011, 0b0000001>(line);
- }));
-}
-void RV32I_Assembler::enableExtF() {
+ // Assembler functors
+}
+void RV32I_Assembler::enableExtF(RVInstrVec& instructions, RVPseudoInstrVec& pseudoInstructions) const {
// Pseudo-op functors
// Assembler functors

MODIFY
src/assembler/rv32i_assembler.h
src/assembler/rv32i_assembler.h
namespace Ripes {
namespace AssemblerTmp {
-class RV32I_Assembler : public AssemblerBase {
+class RV32I_Assembler : public AssemblerBase<ISAInfo<ISA::RV32IM>> {
+private:
+ using RVInstr = Instruction<ISAInfo<ISA::RV32IM>>;
+ using RVInstrVec = std::vector<std::shared_ptr<RVInstr>>;
+ using RVPseudoInstr = PseudoInstruction<ISAInfo<ISA::RV32IM>>;
+ using RVPseudoInstrVec = std::vector<std::shared_ptr<RVPseudoInstr>>;
+
public:
enum class Extensions { M, F };
RV32I_Assembler(const std::set<Extensions>& extensions);
@@ -37,14 +43,16 @@ public:
std::map<QString, AssemblerFunctor> m_assemblers;
void addAssemblerFunctor(const QString& opcode, AssemblerFunctor functor);
+ std::pair<RVInstrVec, RVPseudoInstrVec> initInstructions(const std::set<Extensions>& extensions) const;
+
/**
* Extension enablers
* Calling an extension enabler will register the appropriate assemblers and pseudo-op expander functors with
* the assembler.
*/
- void enableExtI();
- void enableExtM();
- void enableExtF();
+ void enableExtI(RVInstrVec& instructions, RVPseudoInstrVec& pseudoInstructions) const;
+ void enableExtM(RVInstrVec& instructions, RVPseudoInstrVec& pseudoInstructions) const;
+ void enableExtF(RVInstrVec& instructions, RVPseudoInstrVec& pseudoInstructions) const;
};
} // namespace AssemblerTmp

[Assembler] AssemblerBase owns instructions and pseudoinstructions