From db3e9ed699677fe5b0f10c85e6a63185d5443115 Mon Sep 17 00:00:00 2001 From: Stelios Karagiorgis Date: Thu, 5 Oct 2023 13:10:58 +0300 Subject: [PATCH] Add MIPS32 Assembler Add mips32i_assembler Add instructions and pseudoinstructions to mips_i_ext.h Add instructions format at mipsassembler_common.h Add mipsrelocations.h to take 16MSB and 16LSB Edit instruction.h to for Signed, Unsigned and Branch instructions of MIPS32 --- src/assembler/CMakeLists.txt | 8 ++ src/assembler/instruction.h | 97 +++++--------- src/assembler/mips32i_assembler.cpp | 51 ++++++++ src/assembler/mips32i_assembler.h | 28 ++++ src/assembler/mips_i_ext.h | 184 +++++++++++++++++++++++++++ src/assembler/mipsassembler_common.h | 167 ++++++++++++++++++++++++ src/assembler/mipsrelocations.h | 70 ++++++++++ 7 files changed, 538 insertions(+), 67 deletions(-) create mode 100644 src/assembler/mips32i_assembler.cpp create mode 100644 src/assembler/mips32i_assembler.h create mode 100644 src/assembler/mips_i_ext.h create mode 100644 src/assembler/mipsassembler_common.h create mode 100644 src/assembler/mipsrelocations.h diff --git a/src/assembler/CMakeLists.txt b/src/assembler/CMakeLists.txt index 04e8eba1b..45caae9a5 100644 --- a/src/assembler/CMakeLists.txt +++ b/src/assembler/CMakeLists.txt @@ -1 +1,9 @@ create_ripes_lib(assembler LINK_TO_RIPES_LIB) + +target_sources(assembler_lib + PRIVATE + mips32i_assembler.h mips32i_assembler.cpp + mipsrelocations.h + mips_i_ext.h + mipsassembler_common.h +) diff --git a/src/assembler/instruction.h b/src/assembler/instruction.h index dc110c36d..51e5b00c9 100644 --- a/src/assembler/instruction.h +++ b/src/assembler/instruction.h @@ -89,7 +89,7 @@ struct Field { const ReverseSymbolMap &symbolMap, LineTokens &line) const = 0; - /// Return the set of bitranges which constitutes this field. + /// Return the set of bitranges which consitutes this field. virtual std::vector bitRanges() const = 0; const unsigned tokenIndex; }; @@ -217,17 +217,8 @@ struct Imm : public Field { using Reg_T_S = typename std::make_signed::type; using Reg_T_U = typename std::make_unsigned::type; - enum class Repr { Unsigned, Signed, Hex }; - static Radix reprToRadix(Repr repr) { - if (repr == Repr::Unsigned) - return Radix::Unsigned; - if (repr == Repr::Signed) - return Radix::Signed; - if (repr == Repr::Hex) - return Radix::Hex; - return Radix::Unsigned; - } - enum class SymbolType { None, Relative, Absolute }; + enum class Repr { Unsigned, UnsignedMips, Signed, SignedMips, Hex }; + enum class SymbolType { None, Relative, Absolute, MipsBranch }; /** * @brief Imm * @param _tokenIndex: Index within a list of decoded instruction tokens that @@ -248,11 +239,9 @@ struct Imm : public Field { : Field(_tokenIndex), parts(_parts), width(_width), repr(_repr), symbolType(_symbolType), symbolTransformer(_symbolTransformer) {} - int64_t getImm(const QString &immToken, bool &success, - ImmConvInfo &convInfo) const { - return repr == Repr::Signed - ? getImmediateSext32(immToken, success, &convInfo) - : getImmediate(immToken, success, &convInfo); + int64_t getImm(const QString &immToken, bool &success) const { + return repr == Repr::Signed ? getImmediateSext32(immToken, success) + : getImmediate(immToken, success); } std::optional @@ -260,8 +249,7 @@ struct Imm : public Field { FieldLinkRequest &linksWithSymbol) const override { bool success; const Token &immToken = line.tokens[this->tokenIndex]; - ImmConvInfo convInfo; - Reg_T_S value = getImm(immToken, success, convInfo); + Reg_T_S value = getImm(immToken, success); if (!success) { // Could not directly resolve immediate. Register it as a symbol to link @@ -272,8 +260,7 @@ struct Imm : public Field { return {}; } - if (auto res = checkFitsInWidth(value, line, convInfo, immToken); - res.isError()) + if (auto res = checkFitsInWidth(value, line); res.isError()) return res.error(); for (const auto &part : parts) { @@ -282,53 +269,21 @@ struct Imm : public Field { return std::nullopt; } - Result<> checkFitsInWidth(Reg_T_S value, const Location &sourceLine, - ImmConvInfo &convInfo, - QString token = QString()) const { - bool err = false; - if (repr != Repr::Signed) { - if (!isUInt(width, value)) { - err = true; - if (token.isEmpty()) - token = QString::number(static_cast(value)); - } - } else { - - // In case of a bitwize (binary or hex) radix, interpret the value as - // legal if it fits in the width of this immediate (equal to an unsigned - // immediate check). e.g. a signed immediate value of 12 bits must be able - // to accept 0xAFF. - bool isBitwize = - convInfo.radix == Radix::Hex || convInfo.radix == Radix::Binary; - if (isBitwize) { - err = !isUInt(width, value); - } - - if (!isBitwize || (isBitwize && err)) { - // A signed representation using an integer value in assembly OR a - // negative bitwize value which is represented in its full length (e.g. - // 0xFFFFFFF1). - err = !isInt(width, value); - } - - if (err) - if (token.isEmpty()) - token = QString::number(static_cast(value)); - } - - if (err) { - return Error(sourceLine, "Immediate value '" + token + - "' does not fit in " + + Result<> checkFitsInWidth(Reg_T_S value, const Location &sourceLine) const { + if (!((repr == Repr::Signed || repr == Repr::SignedMips) + ? isInt(width, value) + : (isUInt(width, value)))) { + const QString v = repr == Repr::Signed || repr == Repr::SignedMips + ? QString::number(static_cast(value)) + : QString::number(static_cast(value)); + return Error(sourceLine, "Immediate value '" + v + "' does not fit in " + QString::number(width) + " bits"); } - return Result<>::def(); } Result<> applySymbolResolution(const Location &loc, Reg_T symbolValue, Instr_T &instruction, Reg_T address) const { - ImmConvInfo convInfo; - convInfo.radix = reprToRadix(repr); Reg_T adjustedValue = symbolValue; if (symbolType == SymbolType::Relative) adjustedValue -= address; @@ -336,10 +291,15 @@ struct Imm : public Field { if (symbolTransformer) adjustedValue = symbolTransformer(adjustedValue); - if (auto res = checkFitsInWidth(adjustedValue, loc, convInfo); - res.isError()) + if (auto res = checkFitsInWidth(adjustedValue, loc); res.isError()) return res.error(); + if (repr == Repr::SignedMips) + adjustedValue = (adjustedValue - 1) / 4; + + if (repr == Repr::UnsignedMips) + adjustedValue = adjustedValue / 4; + for (const auto &part : parts) part.apply(adjustedValue, instruction); @@ -353,17 +313,20 @@ struct Imm : public Field { for (const auto &part : parts) { part.decode(reconstructed, instruction); } - if (repr == Repr::Signed) { + if (repr == Repr::Signed || repr == Repr::SignedMips) { line.push_back(QString::number(vsrtl::signextend(reconstructed, width))); - } else if (repr == Repr::Unsigned) { + } else if (repr == Repr::Unsigned || repr == Repr::UnsignedMips) { line.push_back(QString::number(reconstructed)); } else { line.push_back("0x" + QString::number(reconstructed, 16)); } if (symbolType != SymbolType::None) { - const int value = vsrtl::signextend(reconstructed, width); - const Reg_T symbolAddress = + int value = + (vsrtl::signextend(reconstructed, width) + + (repr == Repr::SignedMips ? 1 : 0)) * + ((repr == Repr::SignedMips || repr == Repr::UnsignedMips) ? 4 : 1); + Reg_T symbolAddress = value + (symbolType == SymbolType::Absolute ? 0 : address); if (symbolMap.count(symbolAddress)) { line.push_back("<" + symbolMap.at(symbolAddress).v + ">"); diff --git a/src/assembler/mips32i_assembler.cpp b/src/assembler/mips32i_assembler.cpp new file mode 100644 index 000000000..e3d815748 --- /dev/null +++ b/src/assembler/mips32i_assembler.cpp @@ -0,0 +1,51 @@ +#include "mips32i_assembler.h" +#include "gnudirectives.h" +#include "mipsrelocations.h" +#include "ripessettings.h" + +#include +#include + +#include "mips_i_ext.h" + +namespace Ripes { +namespace Assembler { + +MIPS32I_Assembler::MIPS32I_Assembler(const ISAInfo *isa) + : Assembler(isa) { + auto [instrs, pseudos] = initInstructions(isa); + + auto directives = gnuDirectives(); + auto relocations = mipsRelocations(); + initialize(instrs, pseudos, directives, relocations); + + // 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", 268500992); + }); + 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(); +} + +std::tuple +MIPS32I_Assembler::initInstructions(const ISAInfo *isa) const { + _InstrVec instructions; + _PseudoInstrVec pseudoInstructions; + + MIPS_I::enable(isa, instructions, pseudoInstructions); + return {instructions, pseudoInstructions}; +} + +} // namespace Assembler +} // namespace Ripes diff --git a/src/assembler/mips32i_assembler.h b/src/assembler/mips32i_assembler.h new file mode 100644 index 000000000..f61ccf37e --- /dev/null +++ b/src/assembler/mips32i_assembler.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include "assembler.h" +#include "mipsassembler_common.h" + +namespace Ripes { +namespace Assembler { + +class MIPS32I_Assembler : public QObject, public Assembler { + Q_OBJECT + +public: + using Reg_T = uint32_t; + MIPS32I_Assembler(const ISAInfo *isa); + +private: + std::tuple<_InstrVec, _PseudoInstrVec> + initInstructions(const ISAInfo *isa) const; + +protected: + QChar commentDelimiter() const override { return '#'; } +}; + +} // namespace Assembler +} // namespace Ripes diff --git a/src/assembler/mips_i_ext.h b/src/assembler/mips_i_ext.h new file mode 100644 index 000000000..70c556338 --- /dev/null +++ b/src/assembler/mips_i_ext.h @@ -0,0 +1,184 @@ +#pragma once + +#include +#include + +#include "assembler.h" +#include "mipsassembler_common.h" + +namespace Ripes { +namespace Assembler { + +template +struct MIPS_I { + AssemblerTypes(Reg__T); + enum class Options { + shifts64BitVariant, // appends 'w' to 32-bit shift operations, for use in + // the 64-bit MIPS ISA + LI64BitVariant // Modifies LI to be able to emit 64-bit constants + }; + + static void enable(const ISAInfoBase *isa, _InstrVec &instructions, + _PseudoInstrVec &pseudoInstructions, + const std::set &options = {}) { + + // clang-format off + + + pseudoInstructions.push_back(std::shared_ptr<_PseudoInstruction>( + new _PseudoInstruction(Token("move"), {RegTok, RegTok}, _PseudoExpandFunc(line) { + return LineTokensVec{ LineTokens{Token("addu"), line.tokens.at(1), Token("$zero"), line.tokens.at(2)}}; + }))); + + pseudoInstructions.push_back(std::shared_ptr<_PseudoInstruction>(new _PseudoInstruction( + Token("li"), {RegTok, ImmTok}, _PseudoExpandFunc(line) { + return LineTokensVec{LineTokens{Token("ori"), line.tokens.at(1), Token("$zero"), line.tokens.at(2)}}; + }))); + + pseudoInstructions.push_back(std::shared_ptr<_PseudoInstruction>(new _PseudoInstruction( + Token("lw"), {RegTok, ImmTok}, _PseudoExpandFunc(line) { + return LineTokensVec{LineTokens() << Token("lui") << Token("$at") << Token("0x1001"), + LineTokens() << Token("lw") << line.tokens.at(1) << Token(QString("%1").arg(line.tokens.at(2)), "%pcrel_lo16") << Token("$at") }; + }))); + + + pseudoInstructions.push_back(std::shared_ptr<_PseudoInstruction>(new _PseudoInstruction( + Token("la"), {RegTok, ImmTok}, _PseudoExpandFunc(line) { + return LineTokensVec{LineTokens() << Token("lui") << Token("$at") << Token("0x1001"), + LineTokens() << Token("ori") << line.tokens.at(1) << Token("$at") << Token(QString("%1").arg(line.tokens.at(2)), "%pcrel_lo16") }; + }))); + + pseudoInstructions.push_back(std::shared_ptr<_PseudoInstruction>(new _PseudoInstruction( + Token("blt"), {RegTok, RegTok, ImmTok}, _PseudoExpandFunc(line) { + return LineTokensVec{LineTokens() << Token("slt") << Token("$at") << line.tokens.at(1) << line.tokens.at(2), + LineTokens() << Token("bne") << Token("$at") << Token("$zero") << line.tokens.at(3) }; + }))); + + pseudoInstructions.push_back(std::shared_ptr<_PseudoInstruction>(new _PseudoInstruction( + Token("ble"), {RegTok, RegTok, ImmTok}, _PseudoExpandFunc(line) { + return LineTokensVec{LineTokens() << Token("slt") << Token("$at") << line.tokens.at(2) << line.tokens.at(1), + LineTokens() << Token("beq") << Token("$at") << Token("$zero") << line.tokens.at(3) }; + }))); + + pseudoInstructions.push_back(std::shared_ptr<_PseudoInstruction>(new _PseudoInstruction( + Token("bgt"), {RegTok, RegTok, ImmTok}, _PseudoExpandFunc(line) { + return LineTokensVec{LineTokens() << Token("slt") << Token("$at") << line.tokens.at(2) << line.tokens.at(1), + LineTokens() << Token("bne") << Token("$at") << Token("$zero") << line.tokens.at(3) }; + }))); + + pseudoInstructions.push_back(std::shared_ptr<_PseudoInstruction>(new _PseudoInstruction( + Token("bge"), {RegTok, RegTok, ImmTok}, _PseudoExpandFunc(line) { + return LineTokensVec{LineTokens() << Token("slt") << Token("$at") << line.tokens.at(1) << line.tokens.at(2), + LineTokens() << Token("beq") << Token("$at") << Token("$zero") << line.tokens.at(3) }; + }))); + + + + + + + instructions.push_back(std::shared_ptr<_Instruction>(new _Instruction( + _Opcode(Token("syscall"), + {OpPart(MIPSISA::Function::SYSCALL, 0, 5), OpPart(0, 6, 25), OpPart(MIPSISA::Opcode::RTYPE,26,31)}), + {}))); + + instructions.push_back(std::shared_ptr<_Instruction>(new _Instruction( + _Opcode(Token("break"), + {OpPart(MIPSISA::Function::BREAK, 0, 5), OpPart(0, 6, 25), OpPart(MIPSISA::Opcode::RTYPE,26,31)}), + {}))); + + //R-TYPE Instructions(rd, rs, rt) + instructions.push_back(RType(Token("add"), MIPSISA::Function::ADD)); + instructions.push_back(RType(Token("addu"), MIPSISA::Function::ADDU)); + instructions.push_back(RType(Token("and"), MIPSISA::Function::AND)); + instructions.push_back(RType(Token("nor"), MIPSISA::Function::NOR)); + instructions.push_back(RType(Token("or"), MIPSISA::Function::OR)); + instructions.push_back(RType(Token("sub"), MIPSISA::Function::SUB)); + instructions.push_back(RType(Token("subu"), MIPSISA::Function::SUBU)); + instructions.push_back(RType(Token("xor"), MIPSISA::Function::XOR)); + instructions.push_back(RType(Token("slt"), MIPSISA::Function::SLT)); + instructions.push_back(RType(Token("sltu"), MIPSISA::Function::SLTU)); + + + //R-TYPE Instructions(rd, rt, rs) + instructions.push_back(RTypeRtRs(Token("sllv"), MIPSISA::Function::SLLV)); + instructions.push_back(RTypeRtRs(Token("srav"), MIPSISA::Function::SRAV)); + instructions.push_back(RTypeRtRs(Token("srlv"), MIPSISA::Function::SRLV)); + + //R-TYPE Mult,Div (rs, rt) + instructions.push_back(RMultDivType(Token("div"), MIPSISA::Function::DIV)); + instructions.push_back(RMultDivType(Token("divu"), MIPSISA::Function::DIVU)); + instructions.push_back(RMultDivType(Token("mult"), MIPSISA::Function::MULT)); + instructions.push_back(RMultDivType(Token("multu"), MIPSISA::Function::MULTU)); + + //R-TYPE Jalr (rd, rs) + instructions.push_back(JALRType(Token("jalr"), MIPSISA::Function::JALR)); + + + //R-TYPE Rs (rs) + instructions.push_back(RRSType(Token("jr"), MIPSISA::Function::JR)); + instructions.push_back(RRSType(Token("mthi"), MIPSISA::Function::MTHI)); + instructions.push_back(RRSType(Token("mtlo"), MIPSISA::Function::MTLO)); + + //R-TYPE Rd (rd) + instructions.push_back(RRDType(Token("mfhi"), MIPSISA::Function::MFHI)); + instructions.push_back(RRDType(Token("mflo"), MIPSISA::Function::MFLO)); + + + + //R-TYPE Shift Instructions + instructions.push_back(RShiftType(Token("sll"), MIPSISA::Function::SLL)); + instructions.push_back(RShiftType(Token("sra"), MIPSISA::Function::SRA)); + instructions.push_back(RShiftType(Token("srl"), MIPSISA::Function::SRL)); + + + + //I-TYPE Instructions + instructions.push_back(IType(Token("addi"), MIPSISA::Opcode::ADDI)); + instructions.push_back(IType(Token("addiu"), MIPSISA::Opcode::ADDIU)); + instructions.push_back(IType(Token("andi"), MIPSISA::Opcode::ANDI)); + instructions.push_back(IType(Token("ori"), MIPSISA::Opcode::ORI)); + instructions.push_back(IType(Token("slti"), MIPSISA::Opcode::SLTI)); + instructions.push_back(IType(Token("sltiu"), MIPSISA::Opcode::SLTIU)); + instructions.push_back(IType(Token("xori"), MIPSISA::Opcode::XORI)); + + + //I-TYPE Lui + instructions.push_back(ILuiType(Token("lui"), MIPSISA::Opcode::LUI)); + + //I-TYPE Branch Instructions + instructions.push_back(IBranchType(Token("beq"), MIPSISA::Opcode::BEQ)); + instructions.push_back(IBranchType(Token("bne"), MIPSISA::Opcode::BNE)); + + //I-TYPE Branch with Zero Instructions + instructions.push_back(IBranchZeroType(Token("bgez"), MIPSISA::Opcode::BGEZ, 0b00001)); + instructions.push_back(IBranchZeroType(Token("bgtz"), MIPSISA::Opcode::BGTZ, 0b00000)); + instructions.push_back(IBranchZeroType(Token("blez"), MIPSISA::Opcode::BLEZ, 0b00000)); + instructions.push_back(IBranchZeroType(Token("bltz"), MIPSISA::Opcode::BLTZ, 0b00000)); + + //I-TYPE Load Store + instructions.push_back(ILoadStoreType(Token("lb"), MIPSISA::Opcode::LB)); + instructions.push_back(ILoadStoreType(Token("lbu"), MIPSISA::Opcode::LBU)); + instructions.push_back(ILoadStoreType(Token("lh"), MIPSISA::Opcode::LH)); + instructions.push_back(ILoadStoreType(Token("lhu"), MIPSISA::Opcode::LHU)); + instructions.push_back(ILoadStoreType(Token("lw"), MIPSISA::Opcode::LW)); + instructions.push_back(ILoadStoreType(Token("lwc1"), MIPSISA::Opcode::LWC1)); + instructions.push_back(ILoadStoreType(Token("sb"), MIPSISA::Opcode::SB)); + instructions.push_back(ILoadStoreType(Token("sh"), MIPSISA::Opcode::SH)); + instructions.push_back(ILoadStoreType(Token("sw"), MIPSISA::Opcode::SW)); + instructions.push_back(ILoadStoreType(Token("swc1"), MIPSISA::Opcode::SWC1)); + + + //J-Type + instructions.push_back(JType(Token("j"), MIPSISA::Opcode::J)); + instructions.push_back(JType(Token("jal"), MIPSISA::Opcode::JAL)); + + + + } + + +}; + +} // namespace Assembler +} // namespace Ripes diff --git a/src/assembler/mipsassembler_common.h b/src/assembler/mipsassembler_common.h new file mode 100644 index 000000000..77bd12cb5 --- /dev/null +++ b/src/assembler/mipsassembler_common.h @@ -0,0 +1,167 @@ +#pragma once + +#include "../isa/mipsisainfo_common.h" +#include "assembler.h" + +namespace Ripes { +namespace Assembler { + +// The following macros assumes that ASSEMBLER_TYPES(..., ...) has been defined +// for the given assembler. + +/// R TYPE +#define RType(name, funct) \ + std::shared_ptr<_Instruction>(new _Instruction( \ + _Opcode(name, {OpPart(funct, 0, 5), OpPart(0b000000 /*shampt*/, 6, 10), \ + OpPart(MIPSISA::Opcode::RTYPE /*opcode*/, 26, 31)}), \ + {std::make_shared<_Reg>(isa, 1, 11, 15, "rd"), \ + std::make_shared<_Reg>(isa, 2, 21, 25, "rs"), \ + std::make_shared<_Reg>(isa, 3, 16, 20, "rt")})) + +#define RTypeRtRs(name, funct) \ + std::shared_ptr<_Instruction>(new _Instruction( \ + _Opcode(name, {OpPart(funct, 0, 5), OpPart(0b000000 /*shampt*/, 6, 10), \ + OpPart(MIPSISA::Opcode::RTYPE /*opcode*/, 26, 31)}), \ + {std::make_shared<_Reg>(isa, 1, 11, 15, "rd"), \ + std::make_shared<_Reg>(isa, 2, 16, 20, "rt"), \ + std::make_shared<_Reg>(isa, 3, 21, 25, "rs")})) + +#define JALRType(name, funct) \ + std::shared_ptr<_Instruction>(new _Instruction( \ + _Opcode(name, {OpPart(funct, 0, 5), OpPart(0b000000 /*shampt*/, 6, 10), \ + OpPart(MIPSISA::Opcode::RTYPE /*opcode*/, 26, 31), \ + OpPart(0b00000 /*rt*/, 16, 20)}), \ + {std::make_shared<_Reg>(isa, 1, 11, 15, "rd"), \ + std::make_shared<_Reg>(isa, 2, 21, 25, "rs")})) + +#define RMultDivType(name, funct) \ + std::shared_ptr<_Instruction>(new _Instruction( \ + _Opcode(name, {OpPart(funct, 0, 5), OpPart(0b000000 /*shampt*/, 6, 10), \ + OpPart(0b00000 /*rd*/, 11, 15), \ + OpPart(MIPSISA::Opcode::RTYPE /*opcode*/, 26, 31)}), \ + {std::make_shared<_Reg>(isa, 1, 21, 25, "rs"), \ + std::make_shared<_Reg>(isa, 2, 16, 20, "rt")})) + +#define RRSType(name, funct) \ + std::shared_ptr<_Instruction>(new _Instruction( \ + _Opcode(name, {OpPart(funct, 0, 5), OpPart(0b000000 /*shampt*/, 6, 10), \ + OpPart(MIPSISA::Opcode::RTYPE /*opcode*/, 26, 31), \ + OpPart(0b00000 /*rt*/, 16, 20), \ + OpPart(0b00000 /*rd*/, 11, 15)}), \ + {std::make_shared<_Reg>(isa, 1, 21, 25, "rs")})) + +#define RRDType(name, funct) \ + std::shared_ptr<_Instruction>(new _Instruction( \ + _Opcode(name, {OpPart(funct, 0, 5), OpPart(0b000000 /*shampt*/, 6, 10), \ + OpPart(MIPSISA::Opcode::RTYPE /*opcode*/, 26, 31), \ + OpPart(0b00000 /*rt*/, 16, 20), \ + OpPart(0b00000 /*rs*/, 21, 25)}), \ + {std::make_shared<_Reg>(isa, 1, 11, 15, "rd")})) + +#define RShiftType(name, funct) \ + std::shared_ptr<_Instruction>(new _Instruction( \ + _Opcode(name, {OpPart(funct, 0, 5), OpPart(0b00000 /*rs*/, 21, 25), \ + OpPart(MIPSISA::Opcode::RTYPE /*opcode*/, 26, 31)}), \ + {std::make_shared<_Reg>(isa, 1, 11, 15, "rd"), \ + std::make_shared<_Reg>(isa, 2, 16, 20, "rt"), \ + std::make_shared<_Imm>(3, 5, _Imm::Repr::Signed, \ + std::vector{ImmPart(0, 6, 10)})})) + +/// I TYPE +#define IType(name, opcode) \ + std::shared_ptr<_Instruction>(new _Instruction( \ + _Opcode(name, {OpPart(opcode, 26, 31)}), \ + {std::make_shared<_Reg>(isa, 1, 16, 20, "rt"), \ + std::make_shared<_Reg>(isa, 2, 21, 25, "rs"), \ + std::make_shared<_Imm>(3, 16, _Imm::Repr::Signed, \ + std::vector{ImmPart(0, 0, 15)})})) + +#define IBranchType(name, opcode) \ + std::shared_ptr<_Instruction>( \ + new _Instruction(_Opcode(name, {OpPart(opcode, 26, 31)}), \ + {std::make_shared<_Reg>(isa, 1, 21, 25, "rs"), \ + std::make_shared<_Reg>(isa, 2, 16, 20, "rt"), \ + std::make_shared<_Imm>(3, 16, _Imm::Repr::SignedMips, \ + std::vector{ImmPart(0, 0, 15)}, \ + _Imm::SymbolType::Relative)})) + +#define IBranchZeroType(name, opcode, rt) \ + std::shared_ptr<_Instruction>(new _Instruction( \ + _Opcode(name, {OpPart(opcode, 26, 31), OpPart(rt, 16, 20)}), \ + {std::make_shared<_Reg>(isa, 1, 21, 25, "rs"), \ + std::make_shared<_Imm>(2, 16, _Imm::Repr::SignedMips, \ + std::vector{ImmPart(0, 0, 15)}, \ + _Imm::SymbolType::Relative)})) + +#define ILoadStoreType(name, opcode) \ + std::shared_ptr<_Instruction>(new _Instruction( \ + _Opcode(name, {OpPart(opcode, 26, 31)}), \ + {std::make_shared<_Reg>(isa, 1, 16, 20, "rt"), \ + std::make_shared<_Imm>(2, 16, _Imm::Repr::Signed, \ + std::vector{ImmPart(0, 0, 15)}), \ + std::make_shared<_Reg>(isa, 3, 21, 25, "rs")})) + +#define ILuiType(name, opcode) \ + std::shared_ptr<_Instruction>(new _Instruction( \ + _Opcode(name, {OpPart(opcode, 26, 31), OpPart(0b00000, 21, 25)}), \ + {std::make_shared<_Reg>(isa, 1, 16, 20, "rt"), \ + std::make_shared<_Imm>(2, 16, _Imm::Repr::Signed, \ + std::vector{ImmPart(0, 0, 15)})})) + +/// J TYPE +#define JType(name, opcode) \ + std::shared_ptr<_Instruction>(new _Instruction( \ + _Opcode(name, {OpPart(opcode, 26, 31)}), \ + {std::make_shared<_Imm>(1, 26, _Imm::Repr::UnsignedMips, \ + std::vector{ImmPart(0, 0, 25)}, \ + _Imm::SymbolType::Absolute)})) + +#define RegTok _PseudoInstruction::reg() +#define ImmTok _PseudoInstruction::imm() +#define Create_PseudoInstruction +#define _PseudoExpandFuncSyms(line, symbols) \ + [=](const _PseudoInstruction &, const TokenizedSrcLine &line, \ + const SymbolMap &symbols) + +#define _PseudoExpandFunc(line) \ + [](const _PseudoInstruction &, const TokenizedSrcLine &line, \ + const SymbolMap &) + +#define PseudoLoad(name) \ + std::shared_ptr<_PseudoInstruction>(new _PseudoInstruction( \ + name, {RegTok, ImmTok}, _PseudoExpandFunc(line) { \ + LineTokensVec v; \ + v.push_back(LineTokens() << Token("lui") << Token("x1") \ + << Token(line.tokens.at(2), "%pcrel_hi")); \ + v.push_back(LineTokens() \ + << name << line.tokens.at(1) \ + << Token(QString("(%1 + 4)").arg(line.tokens.at(2)), \ + "%pcrel_lo") \ + << line.tokens.at(1)); \ + return v; \ + })) + +// The sw is a pseudo-op if a symbol is given as the immediate token. Thus, if +// we detect that a number has been provided, then abort the pseudo-op handling. +#define PseudoStore(name) \ + std::shared_ptr<_PseudoInstruction>(new _PseudoInstruction( \ + name, {RegTok, ImmTok, RegTok}, _PseudoExpandFunc(line) { \ + bool canConvert; \ + getImmediate(line.tokens.at(2), canConvert); \ + if (canConvert) { \ + return Result>( \ + Error(0, "Unused; will fallback to non-pseudo op sw")); \ + } \ + LineTokensVec v; \ + v.push_back(LineTokens() << Token("auipc") << line.tokens.at(3) \ + << Token(line.tokens.at(2), "%pcrel_hi")); \ + v.push_back(LineTokens() \ + << name << line.tokens.at(1) \ + << Token(QString("(%1 + 4)").arg(line.tokens.at(2)), \ + "%pcrel_lo") \ + << line.tokens.at(3)); \ + return Result>(v); \ + })) + +} // namespace Assembler +} // namespace Ripes diff --git a/src/assembler/mipsrelocations.h b/src/assembler/mipsrelocations.h new file mode 100644 index 000000000..701ea24e8 --- /dev/null +++ b/src/assembler/mipsrelocations.h @@ -0,0 +1,70 @@ +#pragma once + +#include "QDebug" +#include "VSRTL/interface/vsrtl_binutils.h" +#include "relocation.h" + +namespace Ripes { +namespace Assembler { + +// pcrel_lo/hi are restricted to 32-bit absolute addresses, so keep computations +// in this base. +inline uint32_t pcrel_hi16(const uint32_t val, const uint32_t reloc_addr) { + return ((val - (reloc_addr % 0xFFFFF000) + 0x800) >> 16); +} + +template +Relocation mips_pcrel_hi() { + return Relocation("%pcrel_hi16", + [](const Reg_T val, const Reg_T reloc_addr) + -> HandleRelocationRes { + const uint32_t _hi16 = pcrel_hi16(val, reloc_addr); + return {_hi16}; + }); +} + +template +Relocation mips_pcrel_lo() { + return Relocation( + "%pcrel_lo16", + [](const Reg_T val, + const Reg_T reloc_addr) -> HandleRelocationRes { + using Reg_T_S = typename std::make_signed::type; + const uint32_t _hi16 = pcrel_hi16(val, reloc_addr); + const uint32_t lo16 = val & 0x0000FFF; + return {static_cast( + static_cast(vsrtl::signextend(lo16, 16)))}; + }); +} + +template +Relocation mips_hi() { + return Relocation( + "%hi", + [](const Reg_T val, const Reg_T /*reloc_addr*/) + -> HandleRelocationRes { return {val >> 16 & 0xFFFFFF}; }); +} + +template +Relocation mips_lo() { + return Relocation( + "%lo", + [](const Reg_T val, const Reg_T /*reloc_addr*/) + -> HandleRelocationRes { return {val & 0xFFF}; }); +} + +template +RelocationsVec mipsRelocations() { + RelocationsVec relocations; + + relocations.push_back( + std::make_shared>(mips_pcrel_hi())); + relocations.push_back( + std::make_shared>(mips_pcrel_lo())); + relocations.push_back(std::make_shared>(mips_hi())); + relocations.push_back(std::make_shared>(mips_lo())); + + return relocations; +} +} // namespace Assembler +} // namespace Ripes