[Assembler] Add support for symbol relocations
MODIFY
src/assembler/assembler.h
src/assembler/assembler.h
#include "matcher.h"
#include "parserutilities.h"
#include "pseudoinstruction.h"
+#include "relocation.h"
#include <set>
#include <variant>
@@ -108,6 +109,21 @@ protected:
}
}
+ void setRelocations(RelocationsVec& relocations) {
+ if (m_relocations.size() != 0) {
+ throw std::runtime_error("Directives already set");
+ }
+ m_relocations = relocations;
+ for (const auto& iter : m_relocations) {
+ const auto relocations = iter.get()->name();
+ if (m_relocationsMap.count(relocations) != 0) {
+ throw std::runtime_error("Error: relocation " + relocations.toStdString() +
+ " has already been registerred.");
+ }
+ m_relocationsMap[relocations] = iter;
+ }
+ }
+
virtual std::variant<Error, LineTokens> tokenize(const QString& line, const int sourceLine) const {
// Regex: Split on all empty strings (\s+) and characters [, \[, \], \(, \)] except for quote-delimitered
// substrings.
@@ -149,19 +165,19 @@ protected:
}
// Handle the case where a token has been joined together with another token, ie "B:nop"
- QStringList splitTokens;
+ LineTokens splitTokens;
splitTokens.reserve(tokens.size());
for (const auto& token : tokens) {
- QString buffer;
+ Token buffer;
for (const auto& ch : token) {
buffer.append(ch);
if (ch == ':') {
- splitTokens << buffer;
+ splitTokens.push_back(buffer);
buffer.clear();
}
}
if (!buffer.isEmpty()) {
- splitTokens << buffer;
+ splitTokens.push_back(buffer);
}
}
@@ -186,7 +202,7 @@ protected:
return {Error(sourceLine, QStringLiteral("Stray ':' in line"))};
}
} else {
- remainingTokens.append(token);
+ remainingTokens.push_back(token);
symbolStillAllowed = false;
}
}
@@ -211,7 +227,7 @@ protected:
return {Error(sourceLine, QStringLiteral("Stray '.' in line"))};
}
} else {
- remainingTokens.append(token);
+ remainingTokens.push_back(token);
directivesStillAllowed = false;
}
}
@@ -233,12 +249,41 @@ protected:
if (token.contains(commentDelimiter())) {
break;
} else {
- preCommentTokens.append(token);
+ preCommentTokens.push_back(token);
}
}
return {preCommentTokens};
}
+ /**
+ * @brief splitRelocationsFromLine
+ * Identify relocations in tokens; when found, add the relocation to the _following_ token. Relocations are _not_
+ * added to the set of remaining tokens.
+ */
+ virtual std::variant<Error, LineTokens> splitRelocationsFromLine(LineTokens& tokens) const {
+ if (tokens.size() == 0) {
+ return {tokens};
+ }
+
+ LineTokens remainingTokens;
+ remainingTokens.reserve(tokens.size());
+
+ Token relocationForNextToken;
+ for (auto& token : tokens) {
+ if (m_relocationsMap.count(token)) {
+ relocationForNextToken = token;
+ } else {
+ if (!relocationForNextToken.isEmpty()) {
+ token.setRelocation(relocationForNextToken);
+ relocationForNextToken.clear();
+ }
+ remainingTokens.push_back(token);
+ }
+ }
+
+ return {remainingTokens};
+ }
+
virtual QChar commentDelimiter() const = 0;
/**
@@ -248,6 +293,12 @@ protected:
DirectiveMap m_directivesMap;
EarlyDirectives m_earlyDirectives;
+ /**
+ * @brief m_relocations is the set of supported assembler relocation hints
+ */
+ RelocationsVec m_relocations;
+ RelocationsMap m_relocationsMap;
+
/**
* @brief m_sectionBasePointers maintains the base position for the segments
* annoted by the Segment enum class.
@@ -326,7 +377,14 @@ public:
return {"invalid instruction", *error};
}
- return {std::get<LineTokens>(tokens).join(' '), {}};
+ // Join tokens
+ QString joinedLine;
+ for (const auto& token : std::get<LineTokens>(tokens)) {
+ joinedLine += token + " ";
+ }
+ joinedLine.chop(1); // remove trailing ' '
+
+ return {joinedLine, {}};
}
const Matcher& getMatcher() { return *m_matcher; }
@@ -385,6 +443,7 @@ protected:
// Symbols precede directives
runOperation(symbolsAndRest, SymbolLinePair, splitSymbolsFromLine, remainingTokens, i);
+
tsl.symbols = symbolsAndRest.first;
bool uniqueSymbols = true;
@@ -403,7 +462,10 @@ protected:
runOperation(directiveAndRest, DirectiveLinePair, splitDirectivesFromLine, symbolsAndRest.second, i);
tsl.directive = directiveAndRest.first;
- tsl.tokens = directiveAndRest.second;
+ // Parse (and remove) relocation hints from the tokens.
+ runOperation(finalTokens, LineTokens, splitRelocationsFromLine, directiveAndRest.second);
+
+ tsl.tokens = finalTokens;
if (tsl.tokens.empty() && tsl.directive.isEmpty()) {
if (!tsl.symbols.empty()) {
carry.insert(tsl.symbols.begin(), tsl.symbols.end());
@@ -541,7 +603,8 @@ protected:
// Add the special __address__ symbol indicating the address of the instruction itself. Not done through
// addSymbol given that we redefine this symbol on each line.
- m_symbolMap["__address__"] = linkReqAddress(linkRequest);
+ const uint32_t linkRequestAddress = linkReqAddress(linkRequest);
+ m_symbolMap["__address__"] = linkRequestAddress;
if (m_symbolMap.count(symbol) == 0) {
if (couldBeExpression(symbol)) {
@@ -563,6 +626,17 @@ protected:
symbolValue = m_symbolMap.at(symbol);
}
+ if (!linkRequest.fieldRequest.relocation.isEmpty()) {
+ auto relocRes = m_relocationsMap.at(linkRequest.fieldRequest.relocation)
+ .get()
+ ->handle(symbolValue, linkRequestAddress);
+ if (auto* error = std::get_if<Error>(&relocRes)) {
+ errors.push_back(*error);
+ continue;
+ }
+ symbolValue = std::get<uint32_t>(relocRes);
+ }
+
QByteArray& section = program.sections.at(linkRequest.section).data;
// Decode instruction at link-request position
@@ -628,10 +702,12 @@ protected:
return m_instructionMap.at(opcode)->assemble(line);
};
- void initialize(InstrVec& instructions, PseudoInstrVec& pseudoinstructions, DirectiveVec& directives) {
+ void initialize(InstrVec& instructions, PseudoInstrVec& pseudoinstructions, DirectiveVec& directives,
+ RelocationsVec& relocations) {
setInstructions(instructions);
setPseudoInstructions(pseudoinstructions);
setDirectives(directives);
+ setRelocations(relocations);
m_matcher = std::make_unique<Matcher>(m_instructions);
}

MODIFY
src/assembler/assembler_defines.h
src/assembler/assembler_defines.h
namespace Ripes {
namespace Assembler {
-using LineTokens = QStringList;
+class Token : public QString {
+public:
+ inline Token(const QString& t) : QString(t) {}
+ inline Token(const QString& t, const QString& relocation) : QString(t), m_relocation(relocation) {}
+ inline Token() : QString() {}
+ void setRelocation(const QString& relocation) { m_relocation = relocation; }
+ bool hasRelocation() const { return !m_relocation.isEmpty(); }
+ const QString& relocation() const { return m_relocation; }
+
+private:
+ QString m_relocation;
+};
+using LineTokens = QList<Token>;
using LineTokensVec = std::vector<LineTokens>;
using Symbol = QString;
using Symbols = std::set<Symbol>;

MODIFY
src/assembler/instruction.h
src/assembler/instruction.h
@@ -35,6 +35,7 @@ struct Field;
struct FieldLinkRequest {
Field const* field = nullptr;
QString symbol = QString();
+ QString relocation = QString();
};
struct Field {
@@ -83,7 +84,7 @@ struct Opcode : public Field {
* @param name: Name of operation
* @param fields: list of OpParts corresponding to the identifying elements of the opcode.
*/
- Opcode(const QString& _name, std::vector<OpPart> _opParts) : Field(0), name(_name), opParts(_opParts) {}
+ Opcode(const Token& _name, std::vector<OpPart> _opParts) : Field(0), name(_name), opParts(_opParts) {}
std::optional<Assembler::Error> apply(const Assembler::TokenizedSrcLine&, uint32_t& instruction,
FieldLinkRequest&) const override {
@@ -98,7 +99,7 @@ struct Opcode : public Field {
return std::nullopt;
}
- const QString name;
+ const Token name;
const std::vector<OpPart> opParts;
};
@@ -129,11 +130,11 @@ struct Reg : public Field {
std::optional<Assembler::Error> decode(const uint32_t instruction, const uint32_t, const ReverseSymbolMap&,
Assembler::LineTokens& line) const override {
const unsigned regNumber = m_range.decode(instruction);
- const QString registerName = m_isa->regName(regNumber);
+ const Token registerName = m_isa->regName(regNumber);
if (registerName.isEmpty()) {
return Assembler::Error(0, "Unknown register number '" + QString::number(regNumber) + "'");
}
- line.append(registerName);
+ line.push_back(registerName);
return std::nullopt;
}
@@ -176,7 +177,7 @@ struct Imm : public Field {
std::optional<Assembler::Error> apply(const Assembler::TokenizedSrcLine& line, uint32_t& instruction,
FieldLinkRequest& linksWithSymbol) const override {
bool success;
- const QString& immToken = line.tokens[tokenIndex];
+ const Token& immToken = line.tokens[tokenIndex];
// Accept base 10, 16 and 2
uint32_t uvalue = 0;
@@ -199,6 +200,7 @@ struct Imm : public Field {
// Could not directly resolve immediate. Register it as a symbol to link to.
linksWithSymbol.field = this;
linksWithSymbol.symbol = immToken;
+ linksWithSymbol.relocation = immToken.relocation();
return {};
}
@@ -319,11 +321,11 @@ public:
Hint = Hint + " [" + regField->regsd + "]";
}
}
- if (line.tokens.length() != m_expectedTokens) {
+ if (line.tokens.size() != m_expectedTokens) {
return Assembler::Error(line.sourceLine, "Instruction " + m_opcode.name + Hint + " expects " +
QString::number(m_expectedTokens - 1) +
" arguments, but got " +
- QString::number(line.tokens.length() - 1));
+ QString::number(line.tokens.size() - 1));
}
return m_assembler(this, line);
}

MODIFY
src/assembler/parserutilities.h
src/assembler/parserutilities.h
#include <QStringList>
#include <variant>
+#include "assembler_defines.h"
#include "assemblererror.h"
namespace Ripes {
@@ -45,8 +46,8 @@ inline bool matchedParens(std::vector<QChar>& parensStack, QChar end) {
return (toMatch == '[' && end == ']') || (toMatch == '(' && end == ')');
}
-inline std::variant<Error, QStringList> joinParentheses(QStringList& tokens) {
- QStringList outtokens;
+inline std::variant<Error, LineTokens> joinParentheses(QStringList& tokens) {
+ LineTokens outtokens;
std::vector<QChar> parensStack;
QString tokenBuffer;

ADD
src/assembler/relocation.h
+#pragma once
+
+#include <QByteArray>
+#include <QString>
+#include <functional>
+#include <memory>
+#include <variant>
+
+#include "assembler_defines.h"
+#include "assemblererror.h"
+
+namespace Ripes {
+namespace Assembler {
+
+class AssemblerBase;
+
+using HandleRelocationRes = std::variant<Error, uint32_t>;
+
+class Relocation {
+public:
+ using RelocationHandler = std::function<HandleRelocationRes(const uint32_t symbol_addr, const uint32_t reloc_pos)>;
+ Relocation(const QString& relocation, const RelocationHandler& handler)
+ : m_relocation(relocation), m_handler(handler) {}
+
+ HandleRelocationRes handle(const uint32_t symbol_addr, const uint32_t reloc_pos) {
+ return m_handler(symbol_addr, reloc_pos);
+ }
+ const QString& name() const { return m_relocation; }
+
+private:
+ const QString m_relocation;
+ RelocationHandler m_handler;
+};
+
+using RelocationsMap = std::map<QString, std::shared_ptr<Relocation>>;
+using RelocationsVec = std::vector<std::shared_ptr<Relocation>>;
+
+} // namespace Assembler
+} // namespace Ripes

[Assembler] Add support for symbol relocations #75