Detect & warn of multiple declarations of labels in ASM code
MODIFY
src/asmhighlighter.cpp
src/asmhighlighter.cpp
#include <QList>
#include <QRegularExpressionMatchIterator>
+#include <QTextDocument>
#include "defines.h"
@@ -50,6 +51,7 @@ QString FieldType::validateField(const QString& field) const {
}
AsmHighlighter::AsmHighlighter(QTextDocument* parent) : QSyntaxHighlighter(parent) {
+ connect(this, &AsmHighlighter::rehighlightInvalidBlock, this, &AsmHighlighter::rehighlightBlock);
createSyntaxRules();
errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
errorFormat.setUnderlineColor(Qt::red);
@@ -109,6 +111,7 @@ AsmHighlighter::AsmHighlighter(QTextDocument* parent) : QSyntaxHighlighter(paren
<< "\\bret\\b"
<< "\\bcall\\b"
<< "\\btail\\b"
+ /*
<< "\\bfence\\b"
<< "\\brdinstret\\b"
<< "\\brdcycle\\b"
@@ -120,6 +123,7 @@ AsmHighlighter::AsmHighlighter(QTextDocument* parent) : QSyntaxHighlighter(paren
<< "\\bcsrwi\\b"
<< "\\bcsrsi\\b"
<< "\\bcsrci\\b"
+ */
<< "\\bauipc\\b"
<< "\\baddi\\b"
<< "\\baddi\\b"
@@ -168,10 +172,14 @@ AsmHighlighter::AsmHighlighter(QTextDocument* parent) : QSyntaxHighlighter(paren
}
void AsmHighlighter::highlightBlock(const QString& text) {
- if (checkSyntax(text) != QString()) {
+ QString tooltip = checkSyntax(text);
+ int row = currentBlock().firstLineNumber();
+ if (tooltip != QString()) {
setFormat(0, text.length(), errorFormat);
+ emit setTooltip(row, tooltip);
} else {
- foreach (const HighlightingRule& rule, m_highlightingRules) {
+ emit setTooltip(row, QString());
+ for (const HighlightingRule& rule : m_highlightingRules) {
QRegularExpressionMatchIterator matchIterator = rule.pattern.globalMatch(text);
while (matchIterator.hasNext()) {
QRegularExpressionMatch match = matchIterator.next();
@@ -254,8 +262,10 @@ void AsmHighlighter::createSyntaxRules() {
<< "bne"
<< "blt"
<< "bge"
+ << "bgt"
<< "bltu"
- << "bgeu";
+ << "bgeu"
+ << "bleu";
for (const auto& name : names) {
rule.instr = name;
rule.fields = 4;
@@ -263,6 +273,23 @@ void AsmHighlighter::createSyntaxRules() {
m_syntaxRules.insert(name, rule);
}
+ // Branch pseudo-instructions
+ types.clear();
+ names.clear();
+ types << FieldType(Type::Register) << FieldType(Type::Offset);
+ names << "begz"
+ << "bnez"
+ << "blez"
+ << "bgez"
+ << "bltz"
+ << "bgtz";
+ for (const auto& name : names) {
+ rule.instr = name;
+ rule.fields = 3;
+ rule.inputs = types;
+ m_syntaxRules.insert(name, rule);
+ }
+
// I type instructions
types.clear();
names.clear();
@@ -341,9 +368,55 @@ QString AsmHighlighter::checkSyntax(const QString& input) {
auto fields = input.split(splitter);
fields.removeAll("");
- // Check for labels
- if (fields.size() > 0 && fields[0].at(fields[0].length() - 1) == ':') {
- fields.removeFirst();
+ // Throw away case information
+ std::transform(fields.begin(), fields.end(), fields.begin(), [](const QString& s) { return s.toLower(); });
+
+ int pos = currentBlock().firstLineNumber();
+
+ if (fields.size() == 1) {
+ // check for labels
+ QString string = fields[0];
+ if (string[string.length() - 1] == ':') {
+ // Label detected - check if already defined, else add to label definitions
+ string = string.remove(':');
+ // Update map entries at given block
+ if (m_labelPosMap.contains(string) && m_labelPosMap[string] != pos) {
+ // duplicate label found
+ if (pos < m_labelPosMap[string]) {
+ // Label is redefined before previous use of label
+ int prevPos = m_labelPosMap[string];
+ m_posLabelMap.remove(m_labelPosMap[string]);
+ m_labelPosMap[string] = pos;
+ m_posLabelMap[pos] = string;
+ return QString();
+ }
+ return QString("Multiple definitions of label %1").arg(string);
+ } else {
+ // no duplicates, update label at given pos
+ m_labelPosMap.remove(m_posLabelMap[pos]);
+ m_labelPosMap[string] = pos;
+ m_posLabelMap[pos] = string;
+ }
+ return QString();
+ } else {
+ // remove label at given position
+ if (m_posLabelMap.contains(pos)) {
+ /*
+ m_labelPosMap.remove(m_posLabelMap[pos]);
+ m_posLabelMap.remove(pos);
+*/
+ }
+
+ // Check for assembler directives
+ if (string[0] == '.') {
+ string = string.remove('.');
+ if (ASMDirectives.contains(string)) {
+ fields.removeFirst(); // valid assembler directive detected
+ } else {
+ return QString("Unknown assembler directive");
+ }
+ }
+ }
}
// Validate remaining fields
@@ -373,3 +446,17 @@ QString AsmHighlighter::checkSyntax(const QString& input) {
}
return QString();
}
+
+void AsmHighlighter::clearAndRehighlight() {
+ m_labelPosMap.clear();
+ m_posLabelMap.clear();
+ rehighlight();
+}
+
+void AsmHighlighter::invalidateLabels(const QTextCursor& cursor) {
+ if (m_posLabelMap.contains(cursor.block().firstLineNumber())) {
+ // a current label exists at the cursor position - if current action was a newline event, we need to reset
+ // resets label mapping and rehighlights required lines
+ clearAndRehighlight();
+ }
+}

MODIFY
src/asmhighlighter.h
src/asmhighlighter.h
@@ -34,6 +34,8 @@ public:
QString checkSyntax(const QString& line);
signals:
+ void setTooltip(int, QString);
+ void rehighlightInvalidBlock(const QTextBlock&);
private:
struct HighlightingRule {
@@ -61,8 +63,12 @@ private:
QTextCharFormat errorFormat;
QMap<int, QString> m_errors;
+ QMap<QString, int> m_labelPosMap;
+ QMap<int, QString> m_posLabelMap;
public slots:
+ void invalidateLabels(const QTextCursor&);
+ void clearAndRehighlight();
};
#endif // ASMHIGHLIGHTER_H

MODIFY
src/codeeditor.cpp
src/codeeditor.cpp
#include <QLinearGradient>
#include <QMenu>
#include <QPainter>
+#include <QSyntaxHighlighter>
#include <QTextBlock>
#include <QToolTip>
#include <QWheelEvent>
@@ -45,6 +46,13 @@ CodeEditor::CodeEditor(QWidget* parent) : QPlainTextEdit(parent) {
setMouseTracking(true);
setWordWrapMode(QTextOption::NoWrap);
+
+ // connect tooltip changes from asm highlighter
+ connect(m_highlighter, &AsmHighlighter::setTooltip, this, &CodeEditor::updateTooltip);
+
+ // The highlighting is reset upon line count changes, to detect label invalidation
+ connect(this->document(), &QTextDocument::cursorPositionChanged, m_highlighter, &AsmHighlighter::invalidateLabels);
+ connect(this->document(), &QTextDocument::blockCountChanged, m_highlighter, &AsmHighlighter::clearAndRehighlight);
}
int CodeEditor::lineNumberAreaWidth() {
@@ -90,14 +98,20 @@ bool CodeEditor::eventFilter(QObject* /*observed*/, QEvent* event) {
return false;
}
+void CodeEditor::updateTooltip(int line, QString tip) {
+ // Connects to AsmHighlighter::setTooltip
+ m_tooltipForLine[line] = tip;
+}
+
bool CodeEditor::event(QEvent* event) {
// Override event handler for receiving tool tips
if (event->type() == QEvent::ToolTip) {
+ // Tooltips are updated through slot handler updateTooltip
QHelpEvent* helpEvent = static_cast<QHelpEvent*>(event);
QTextCursor textAtCursor = cursorForPosition(helpEvent->pos());
- QString syntaxCheck = m_highlighter->checkSyntax(textAtCursor.block().text().trimmed());
- if (!syntaxCheck.isEmpty()) {
- QToolTip::showText(helpEvent->globalPos(), syntaxCheck);
+ int row = textAtCursor.block().firstLineNumber();
+ if (m_tooltipForLine.contains(row) && m_tooltipForLine[row] != QString()) {
+ QToolTip::showText(helpEvent->globalPos(), m_tooltipForLine[row]);
} else {
QToolTip::hideText();
event->ignore();

MODIFY
src/codeeditor.h
src/codeeditor.h
@@ -35,6 +35,7 @@ private slots:
void updateSidebarWidth(int newBlockCount);
void highlightCurrentLine();
void updateSidebar(const QRect&, int);
+ void updateTooltip(int line, QString tip);
private:
AsmHighlighter* m_highlighter;
@@ -43,6 +44,7 @@ private:
int m_sidebarWidth;
std::set<int> m_breakpoints;
+ QMap<int, QString> m_tooltipForLine;
QFont m_font = font();

MODIFY
src/defines.h
src/defines.h
@@ -77,6 +77,8 @@ enum instrType {
INVALID = 0b0
};
+const static QStringList ASMDirectives = QStringList() << "text";
+
enum class cacheLevel { L1, L2, L3 };
enum class cacheType { DM, SA, FA };

MODIFY
src/main.cpp
src/main.cpp
#include <QApplication>
+#include <QFileInfo>
+#include <QMessageBox>
#include <iostream>
#include "mainwindow.h"
@@ -16,7 +18,17 @@ int main(int argc, char** argv) {
if (argc == 2) {
// Load file specified in command-line argument
+ QString ext = QFileInfo(argv[1]).suffix();
+ if (ext == QString("bin")) {
m.loadBinaryFile(argv[1]);
+ } else if (ext == "asm" || ext == "s") {
+ m.loadAssemblyFile(argv[1]);
+ } else {
+ QMessageBox msg;
+ msg.setText(
+ QString("Unknown extension for input file.\ngot: \"%1\" - expected:\n- bin\n- asm\n- s").arg(ext));
+ msg.exec();
+ }
}
return app.exec();

MODIFY
src/mainwindow.cpp
src/mainwindow.cpp
@@ -28,7 +28,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), m_ui(new Ui::Main
// m_ui->tabbar->addFancyTab(QIcon(QPixmap(":/icons/server.svg")), "Cache");
// m_ui->tabbar->addFancyTab(QIcon(QPixmap(":/icons/graph.svg")), "Results");
connect(m_ui->tabbar, &FancyTabBar::activeIndexChanged, m_ui->stackedWidget, &QStackedWidget::setCurrentIndex);
- m_ui->tabbar->setActiveIndex(1);
+ m_ui->tabbar->setActiveIndex(0);
// Setup splitter such that consoles are always as small as possible
auto splitterSize = m_ui->splitter->size();

Detect & warn of multiple declarations of labels in ASM code