Rework 'li' pseudo-op expansion based on LLVM implementation
For more info, see:
MODIFY
src/assembler/rv32i_assembler.cpp
src/assembler/rv32i_assembler.cpp
@@ -105,42 +105,6 @@ void RV32I_Assembler::enableExtI(const ISAInfoBase* isa, InstrVec& instructions,
return LineTokensVec{LineTokens() << Token("jal") << Token("x1") << line.tokens.at(1)};
})));
- pseudoInstructions.push_back(std::shared_ptr<PseudoInstruction>(new PseudoInstruction(
- Token("li"), {RegTok, ImmTok},
- _PseudoExpandFuncSyms(line, symbols) {
- LineTokensVec v;
- // Determine whether an ADDI or LUI instruction is sufficient, or if both LUI and ADDI is needed, by
- // analysing the immediate size
- bool canConvert;
- int immediate = getImmediate(line.tokens.at(2), canConvert);
-
- if(!canConvert) {
- // Check if the immediate has been made available in the symbol set at this point...
- auto it = symbols.find(line.tokens.at(2));
- if(it != symbols.end()){
- immediate = it->second;
- } else {
- return PseudoExpandRes{Error(line.sourceLine, QString("Invalid immediate '%1'").arg(line.tokens.at(2)))};
- }
- }
-
- // Generate offset required for discerning between positive and negative immediates
- if (isInt<12>(immediate)) {
- // immediate can be represented by 12 bits, ADDI is sufficient
- v.push_back(LineTokens() << Token("addi") << line.tokens.at(1) << Token("x0") << QString::number(immediate));
- } else {
- const int lower12Signed = signextend<int32_t, 12>(immediate & 0xFFF);
- int signOffset = lower12Signed < 0 ? 1 : 0;
- v.push_back(LineTokens() << Token("lui") << line.tokens.at(1)
- << QString::number((static_cast<uint32_t>(immediate) >> 12) + signOffset));
- if ((immediate & 0xFFF) != 0) {
- v.push_back(LineTokens()
- << Token("addi") << line.tokens.at(1) << line.tokens.at(1) << QString::number(lower12Signed));
- }
- }
- return PseudoExpandRes{v};
- })));
-
pseudoInstructions.push_back(std::shared_ptr<PseudoInstruction>(
new PseudoInstruction(Token(Token("nop")), {}, [](const PseudoInstruction&, const TokenizedSrcLine& , const SymbolMap&) {
return LineTokensVec{LineTokens() << Token("addi") << Token("x0") << Token("x0") << Token("0")};
@@ -232,6 +196,72 @@ void RV32I_Assembler::enableExtI(const ISAInfoBase* isa, InstrVec& instructions,
})));
// clang-format on
+ pseudoInstructions.push_back(std::shared_ptr<PseudoInstruction>(new PseudoInstruction(
+ Token("li"), {RegTok, ImmTok}, _PseudoExpandFuncSyms(line, symbols) {
+ LineTokensVec res;
+ // Get an integer representation of the immediate, which might have been a symbol.
+ bool canConvert;
+ bool liveDstReg = false;
+ int64_t immediate = getImmediate(line.tokens.at(2), canConvert);
+
+ if (!canConvert) {
+ // Check if the immediate has been made available in the symbol set at this point...
+ auto it = symbols.find(line.tokens.at(2));
+ if (it != symbols.end()) {
+ immediate = it->second;
+ } else {
+ return PseudoExpandRes{
+ Error(line.sourceLine, QString("Invalid immediate '%1'").arg(line.tokens.at(2)))};
+ }
+ }
+
+ /* The load-immediate pseudo instructions follows the LLVM implementation, as seen here.
+ * https://llvm.org/docs/doxygen/RISCVMatInt_8cpp_source.html
+ * For more insight, please refer to there, since their comments have been left out of this source
+ * code.
+ */
+ std::function<PseudoExpandRes(int64_t, bool)> genInstrSeq = [&](int64_t val, bool isRV64) {
+ // LLVM has preconversion of all immediates into a sign-extended 64-bit version, and only checks
+ // int32. getImmediate, as used above, does not sign-extend unsigned immediates. Therefore we must
+ // go into the terminal case when @p val is either representable as uint32 or int32.
+ if (isUInt<32>(val) || isInt<32>(val)) {
+ int64_t Hi20 = ((val + 0x800) >> 12) & 0xFFFFF;
+ int64_t Lo12 = signextend<int64_t, 12>(val);
+ if (Hi20) {
+ res.push_back(LineTokens() << Token("lui") << line.tokens.at(1) << QString::number(Hi20));
+ liveDstReg = true;
+ }
+ if (Lo12 || Hi20 == 0) {
+ QString addiOpc = isRV64 && Hi20 != 0 ? "addiw" : "addi";
+ res.push_back(LineTokens()
+ << Token(addiOpc) << line.tokens.at(1)
+ << (liveDstReg ? line.tokens.at(1) : Token("x0")) << QString::number(Lo12));
+ liveDstReg = true;
+ }
+ return PseudoExpandRes{res};
+ }
+ if (!isRV64) {
+ return PseudoExpandRes{Error(
+ line.sourceLine, QString("Invalid immediate '%1'; can't emit >32-bit imm for non-RV64 target")
+ .arg(line.tokens.at(2)))};
+ }
+ int64_t Lo12 = signextend<int64_t, 12>(val);
+ int64_t Hi52 = ((uint64_t)val + 0x800ull) >> 12;
+ int ShiftAmount = 12 + firstSetBitIdx(Hi52);
+ Hi52 = signextend<int64_t>(Hi52 >> (ShiftAmount - 12), 64 - ShiftAmount);
+ genInstrSeq(Hi52, isRV64);
+ res.push_back(LineTokens() << Token("slli") << line.tokens.at(1) << line.tokens.at(1)
+ << QString::number(ShiftAmount));
+ if (Lo12) {
+ res.push_back(LineTokens()
+ << Token("addi") << line.tokens.at(1) << line.tokens.at(1) << QString::number(Lo12));
+ }
+ return PseudoExpandRes{res};
+ };
+ auto instrSeq = genInstrSeq(immediate, options.count(Options::LI64BitVariant));
+ return instrSeq;
+ })));
+
// Assembler functors
instructions.push_back(std::shared_ptr<Instruction>(

MODIFY
src/assembler/rv32i_assembler.h
src/assembler/rv32i_assembler.h
@@ -12,7 +12,8 @@ class RV32I_Assembler : public QObject, public Assembler {
Q_OBJECT
public:
enum class Options {
- shifts64BitVariant // appends 'w' to 32-bit shift operations, for use in the 64-bit RISC-V ISA
+ shifts64BitVariant, // appends 'w' to 32-bit shift operations, for use in the 64-bit RISC-V ISA
+ LI64BitVariant // Modifies LI to be able to emit 64-bit constants
};
RV32I_Assembler(const ISAInfo<ISA::RV32I>* isa);

MODIFY
src/assembler/rv64i_assembler.cpp
src/assembler/rv64i_assembler.cpp
@@ -50,7 +50,9 @@ std::tuple<InstrVec, PseudoInstrVec> RV64I_Assembler::initInstructions(const ISA
}
void RV64I_Assembler::enableExtI(const ISAInfoBase* isa, InstrVec& instructions, PseudoInstrVec& pseudoInstructions) {
- RV32I_Assembler::enableExtI(isa, instructions, pseudoInstructions, {RV32I_Assembler::Options::shifts64BitVariant});
+ RV32I_Assembler::enableExtI(
+ isa, instructions, pseudoInstructions,
+ {RV32I_Assembler::Options::shifts64BitVariant, RV32I_Assembler::Options::LI64BitVariant});
instructions.push_back(IType32(Token("addiw"), 0b000));

Rework 'li' pseudo-op expansion based on LLVM implementation

For more info, see: https://llvm.org/docs/doxygen/RISCVMatInt_8cpp_source.html