Skip to content

[MIPS]Initial support for MIPS16 assembly. #108681

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

jdeguire
Copy link
Contributor

Provide support for basic encoding and decoding of MIPS16 instructions without requiring an external assembler. All instructions are supported, including ones with "pseudo register" operands like "addiu $rx, $pc, imm". There are features, however, that still need to be added before MIPS16 is suitable for everyday use. These include delay slot filling, MIPS16 relocations, instruction aliases for things like large immediate values and loading from or storing to labels (like "lw $rx, FOO"), and probably others.

Provide support for basic encoding and decoding of MIPS16 instructions without requiring
an external assembler. All instructions are supported, including ones with "pseudo
register" operands like "addiu $rx, $pc, imm". There are features, however, that still
need to be added before MIPS16 is suitable for everyday use. These include delay slot
filling, MIPS16 relocations, instructions aliases for things like large immediate values
and loading from or storing to labels (like "lw $rx, FOO").
@llvmbot llvmbot added the mc Machine (object) code label Sep 14, 2024
@llvmbot
Copy link
Member

llvmbot commented Sep 14, 2024

@llvm/pr-subscribers-mc

Author: Jesse D (jdeguire)

Changes

Provide support for basic encoding and decoding of MIPS16 instructions without requiring an external assembler. All instructions are supported, including ones with "pseudo register" operands like "addiu $rx, $pc, imm". There are features, however, that still need to be added before MIPS16 is suitable for everyday use. These include delay slot filling, MIPS16 relocations, instruction aliases for things like large immediate values and loading from or storing to labels (like "lw $rx, FOO"), and probably others.


Patch is 248.73 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/108681.diff

68 Files Affected:

  • (modified) llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp (+439-8)
  • (modified) llvm/lib/Target/Mips/Disassembler/MipsDisassembler.cpp (+380-23)
  • (modified) llvm/lib/Target/Mips/MCTargetDesc/MipsAsmBackend.cpp (+1)
  • (modified) llvm/lib/Target/Mips/MCTargetDesc/MipsInstPrinter.cpp (+27-27)
  • (modified) llvm/lib/Target/Mips/MCTargetDesc/MipsInstPrinter.h (+7-1)
  • (modified) llvm/lib/Target/Mips/MCTargetDesc/MipsMCCodeEmitter.cpp (+217-1)
  • (modified) llvm/lib/Target/Mips/MCTargetDesc/MipsMCCodeEmitter.h (+43-1)
  • (modified) llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp (+17-1)
  • (modified) llvm/lib/Target/Mips/MicroMips32r6InstrInfo.td (+3-2)
  • (modified) llvm/lib/Target/Mips/MicroMipsInstrInfo.td (-5)
  • (modified) llvm/lib/Target/Mips/Mips16ISelDAGToDAG.cpp (+5-1)
  • (modified) llvm/lib/Target/Mips/Mips16InstrFormats.td (+40-38)
  • (modified) llvm/lib/Target/Mips/Mips16InstrInfo.cpp (+5-4)
  • (modified) llvm/lib/Target/Mips/Mips16InstrInfo.td (+889-292)
  • (modified) llvm/lib/Target/Mips/MipsAsmPrinter.cpp (+33-1)
  • (modified) llvm/lib/Target/Mips/MipsAsmPrinter.h (+4)
  • (modified) llvm/lib/Target/Mips/MipsBranchExpansion.cpp (+1-1)
  • (modified) llvm/lib/Target/Mips/MipsConstantIslandPass.cpp (+6-1)
  • (modified) llvm/lib/Target/Mips/MipsDelaySlotFiller.cpp (+2-1)
  • (modified) llvm/lib/Target/Mips/MipsInstrInfo.cpp (+12-9)
  • (modified) llvm/lib/Target/Mips/MipsInstrInfo.h (-1)
  • (modified) llvm/lib/Target/Mips/MipsInstrInfo.td (+80-23)
  • (modified) llvm/lib/Target/Mips/MipsRegisterInfo.cpp (+2)
  • (modified) llvm/lib/Target/Mips/MipsRegisterInfo.h (+2)
  • (modified) llvm/lib/Target/Mips/MipsRegisterInfo.td (+25-5)
  • (modified) llvm/lib/Target/Mips/MipsScheduleGeneric.td (+34-23)
  • (modified) llvm/lib/Target/Mips/MipsTargetMachine.cpp (+3)
  • (modified) llvm/test/CodeGen/Mips/addi.ll (+7-5)
  • (modified) llvm/test/CodeGen/Mips/adjust-callstack-sp.ll (+1-1)
  • (modified) llvm/test/CodeGen/Mips/align16.ll (+7-5)
  • (modified) llvm/test/CodeGen/Mips/alloca16.ll (+1-1)
  • (modified) llvm/test/CodeGen/Mips/beqzc.ll (+1-1)
  • (modified) llvm/test/CodeGen/Mips/beqzc1.ll (+1-1)
  • (modified) llvm/test/CodeGen/Mips/brsize3.ll (+6-17)
  • (modified) llvm/test/CodeGen/Mips/brsize3a.ll (+1-1)
  • (modified) llvm/test/CodeGen/Mips/const4a.ll (+1-1)
  • (modified) llvm/test/CodeGen/Mips/const6.ll (+3-3)
  • (modified) llvm/test/CodeGen/Mips/const6a.ll (+5-5)
  • (modified) llvm/test/CodeGen/Mips/dins.ll (+11-15)
  • (modified) llvm/test/CodeGen/Mips/div.ll (+2-2)
  • (modified) llvm/test/CodeGen/Mips/div_rem.ll (+3-3)
  • (modified) llvm/test/CodeGen/Mips/divu.ll (+2-2)
  • (modified) llvm/test/CodeGen/Mips/divu_remu.ll (+3-3)
  • (modified) llvm/test/CodeGen/Mips/ex2.ll (+1-1)
  • (modified) llvm/test/CodeGen/Mips/helloworld.ll (+6-4)
  • (added) llvm/test/CodeGen/Mips/init-global-base-reg-16.ll (+37)
  • (modified) llvm/test/CodeGen/Mips/lcb2.ll (+10-21)
  • (modified) llvm/test/CodeGen/Mips/lcb3c.ll (+3-3)
  • (modified) llvm/test/CodeGen/Mips/lcb4a.ll (+2-2)
  • (modified) llvm/test/CodeGen/Mips/lcb5.ll (+8-8)
  • (modified) llvm/test/CodeGen/Mips/rem.ll (+2-2)
  • (modified) llvm/test/CodeGen/Mips/remu.ll (+2-2)
  • (modified) llvm/test/CodeGen/Mips/sel1c.ll (+1-1)
  • (modified) llvm/test/CodeGen/Mips/sel2c.ll (+1-1)
  • (modified) llvm/test/CodeGen/Mips/seleqk.ll (+8-8)
  • (modified) llvm/test/CodeGen/Mips/selgek.ll (+8-8)
  • (modified) llvm/test/CodeGen/Mips/selltk.ll (+8-8)
  • (modified) llvm/test/CodeGen/Mips/selnek.ll (+8-8)
  • (modified) llvm/test/CodeGen/Mips/setultk.ll (+3-3)
  • (modified) llvm/test/CodeGen/Mips/simplebr.ll (+1-1)
  • (modified) llvm/test/CodeGen/Mips/sr1.ll (+9-13)
  • (modified) llvm/test/CodeGen/Mips/tailcall/tailcall.ll (+13-13)
  • (modified) llvm/test/CodeGen/Mips/trap1.ll (+1-1)
  • (modified) llvm/test/CodeGen/Mips/whitespace.ll (+2-2)
  • (added) llvm/test/MC/Disassembler/Mips/mips16/valid.txt (+167)
  • (modified) llvm/test/MC/Mips/micromips-invalid.s (+2-2)
  • (modified) llvm/test/MC/Mips/mips16/invalid.s (+208-5)
  • (modified) llvm/test/MC/Mips/mips16/valid.s (+170-2)
diff --git a/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp b/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp
index 8ab435c6c6fd18..2a443747ec4228 100644
--- a/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp
+++ b/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp
@@ -206,6 +206,7 @@ class MipsAsmParser : public MCTargetAsmParser {
   ParseStatus parseJumpTarget(OperandVector &Operands);
   ParseStatus parseInvNum(OperandVector &Operands);
   ParseStatus parseRegisterList(OperandVector &Operands);
+  ParseStatus parseSaveRestoreOperands(OperandVector &Operands);
 
   bool searchSymbolAlias(OperandVector &Operands);
 
@@ -429,6 +430,8 @@ class MipsAsmParser : public MCTargetAsmParser {
 
   int matchMSA128CtrlRegisterName(StringRef Name);
 
+  int matchPCRegisterName(StringRef Name);
+
   unsigned getReg(int RC, int RegNo);
 
   /// Returns the internal register number for the current AT. Also checks if
@@ -582,7 +585,11 @@ class MipsAsmParser : public MCTargetAsmParser {
   }
 
   bool isJalrRelocAvailable(const MCExpr *JalExpr) {
-    if (!EmitJalrReloc)
+    // FIXME: A similar check was disabled for MIPS16 in
+    // MipsAsmPrinter::emitInstruction(), so disable it here
+    // as well for now to be safe. If this is updated, also
+    // update that FIXME in MipsAsmPrinter.
+    if (!EmitJalrReloc || inMips16Mode())
       return false;
     MCValue Res;
     if (!JalExpr->evaluateAsRelocatable(Res, nullptr, nullptr))
@@ -825,9 +832,14 @@ class MipsOperand : public MCParsedAsmOperand {
     RegKind_COP3 = 512,   /// COP3
     RegKind_COP0 = 1024,  /// COP0
     /// Potentially any (e.g. $1)
+    // This purposefully does not include RegKind_PC because it is not a real
+    // register and has no number.
     RegKind_Numeric = RegKind_GPR | RegKind_FGR | RegKind_FCC | RegKind_MSA128 |
                       RegKind_MSACtrl | RegKind_COP2 | RegKind_ACC |
-                      RegKind_CCR | RegKind_HWRegs | RegKind_COP3 | RegKind_COP0
+                      RegKind_CCR | RegKind_HWRegs | RegKind_COP3 |
+                      RegKind_COP0,
+    RegKind_PC = 2048 /// Program counter (not a real register; used in
+                      /// some MIPS16 instructions).
   };
 
 private:
@@ -837,6 +849,7 @@ class MipsOperand : public MCParsedAsmOperand {
     k_RegisterIndex, /// A register index in one or more RegKind.
     k_Token,         /// A simple token
     k_RegList,       /// A physical register list
+    k_SaveRestore,   /// Operands used in MIPS16 save/restore instructions
   } Kind;
 
 public:
@@ -853,6 +866,7 @@ class MipsOperand : public MCParsedAsmOperand {
     case k_Immediate:
     case k_RegisterIndex:
     case k_Token:
+    case k_SaveRestore:
       break;
     }
   }
@@ -886,12 +900,19 @@ class MipsOperand : public MCParsedAsmOperand {
     SmallVector<unsigned, 10> *List;
   };
 
+  struct SaveRestoreOp {
+    unsigned SavedRegsMask;
+    unsigned StaticRegsMask;
+    unsigned Framesize;
+  };
+
   union {
     struct Token Tok;
     struct RegIdxOp RegIdx;
     struct ImmOp Imm;
     struct MemOp Mem;
     struct RegListOp RegList;
+    struct SaveRestoreOp SaveRestore;
   };
 
   SMLoc StartLoc, EndLoc;
@@ -931,6 +952,14 @@ class MipsOperand : public MCParsedAsmOperand {
     return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
   }
 
+  /// Coerce the register to GPR32 and return the real register for the current
+  /// target.
+  unsigned getCPU16Reg() const {
+    assert(isRegIdx() && (RegIdx.Kind & RegKind_GPR) && "Invalid access!");
+    unsigned ClassID = Mips::GPR32RegClassID;
+    return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index);
+  }
+
   /// Coerce the register to GPR64 and return the real register for the current
   /// target.
   unsigned getGPR64Reg() const {
@@ -1115,6 +1144,31 @@ class MipsOperand : public MCParsedAsmOperand {
     Inst.addOperand(MCOperand::createReg(getGPRMM16Reg()));
   }
 
+  void addCPU16AsmRegOperands(MCInst &Inst, unsigned N) const {
+    assert(N == 1 && "Invalid number of operands!");
+    Inst.addOperand(MCOperand::createReg(getCPU16Reg()));
+  }
+
+  void addCPU16AsmRegPlusSPOperands(MCInst &Inst, unsigned N) const {
+    assert(N == 1 && "Invalid number of operands!");
+    Inst.addOperand(MCOperand::createReg(getCPU16Reg()));
+  }
+
+  void addPCPseudoRegOperands(MCInst &Inst, unsigned N) const {
+    assert(N == 1 && "Invalid number of operands!");
+    Inst.addOperand(MCOperand::createReg(Mips::PC));
+  }
+
+  void addSPPseudoRegOperands(MCInst &Inst, unsigned N) const {
+    assert(N == 1 && "Invalid number of operands!");
+    Inst.addOperand(MCOperand::createReg(Mips::SP));
+  }
+
+  void addRAPseudoRegOperands(MCInst &Inst, unsigned N) const {
+    assert(N == 1 && "Invalid number of operands!");
+    Inst.addOperand(MCOperand::createReg(Mips::RA));
+  }
+
   /// Render the operand to an MCInst as a GPR64
   /// Asserts if the wrong number of operands are requested, or the operand
   /// is not a k_RegisterIndex compatible with RegKind_GPR
@@ -1282,6 +1336,20 @@ class MipsOperand : public MCParsedAsmOperand {
     addExpr(Inst, Expr);
   }
 
+  void addMips16MemOperands(MCInst &Inst, unsigned N) const {
+    assert(N == 2 && "Invalid number of operands!");
+
+    MipsOperand *BaseOp = getMemBase();
+
+    if (BaseOp->isPCPseudoReg())
+      Inst.addOperand(MCOperand::createReg(Mips::PC));
+    else
+      Inst.addOperand(MCOperand::createReg(BaseOp->getCPU16Reg()));
+
+    const MCExpr *Expr = getMemOff();
+    addExpr(Inst, Expr);
+  }
+
   void addRegListOperands(MCInst &Inst, unsigned N) const {
     assert(N == 1 && "Invalid number of operands!");
 
@@ -1289,6 +1357,30 @@ class MipsOperand : public MCParsedAsmOperand {
       Inst.addOperand(MCOperand::createReg(RegNo));
   }
 
+  void addSaveRestoreOperands(MCInst &Inst, unsigned N) const {
+    assert(N == 1 && "Invalid number of operands!");
+
+    int RC =
+        (AsmParser.isGP64bit() ? Mips::GPR64RegClassID : Mips::GPR32RegClassID);
+    auto &RegClass = AsmParser.getContext().getRegisterInfo()->getRegClass(RC);
+
+    auto Mask = getSaveRestoreSavedRegs();
+    while (Mask) {
+      unsigned RegNo = RegClass.getRegister(llvm::countr_zero(Mask));
+      Inst.addOperand(MCOperand::createReg(RegNo));
+      Mask &= (Mask - 1);
+    }
+
+    Inst.addOperand(MCOperand::createImm(getSaveRestoreFramesize()));
+
+    Mask = getSaveRestoreStaticRegs();
+    while (Mask) {
+      unsigned RegNo = RegClass.getRegister(llvm::countr_zero(Mask));
+      Inst.addOperand(MCOperand::createReg(RegNo));
+      Mask &= (Mask - 1);
+    }
+  }
+
   bool isReg() const override {
     // As a special case until we sort out the definition of div/divu, accept
     // $0/$zero here so that MCK_ZERO works correctly.
@@ -1397,16 +1489,75 @@ class MipsOperand : public MCParsedAsmOperand {
       && (getMemBase()->getGPR32Reg() == Mips::GP);
   }
 
+  template <unsigned ShiftAmount = 0> bool isMemWithCPU16Base() const {
+    return isMem() && getMemBase()->isCPU16AsmReg() && isConstantMemOff() &&
+           isShiftedUInt<5, ShiftAmount>(getConstantMemOff());
+  }
+
+  bool isMemWithCPU16PCBase() const {
+    return isMem() && getMemBase()->isPCPseudoReg() && isConstantMemOff() &&
+           isShiftedUInt<8, 2>(getConstantMemOff());
+  }
+
+  bool isMemWithCPU16SPBase() const {
+    return isMem() && getMemBase()->isSPPseudoReg() && isConstantMemOff() &&
+           isShiftedUInt<8, 2>(getConstantMemOff());
+  }
+
+  bool isMemWithExtCPU16Base() const {
+    if (!isMem())
+      return false;
+    if (!getMemBase()->isCPU16AsmReg())
+      return false;
+    if (isa<MCTargetExpr>(getMemOff()) ||
+        (isConstantMemOff() && isInt<16>(getConstantMemOff())))
+      return true;
+    MCValue Res;
+    bool IsReloc = getMemOff()->evaluateAsRelocatable(Res, nullptr, nullptr);
+    return IsReloc && isInt<16>(Res.getConstant());
+  }
+
+  bool isMemWithExtCPU16PCBase() const {
+    if (!isMem())
+      return false;
+    if (!getMemBase()->isPCPseudoReg())
+      return false;
+    if (isa<MCTargetExpr>(getMemOff()) ||
+        (isConstantMemOff() && isInt<16>(getConstantMemOff())))
+      return true;
+    MCValue Res;
+    bool IsReloc = getMemOff()->evaluateAsRelocatable(Res, nullptr, nullptr);
+    return IsReloc && isInt<16>(Res.getConstant());
+  }
+
+  bool isMemWithExtCPU16SPBase() const {
+    if (!isMem())
+      return false;
+    if (!getMemBase()->isSPPseudoReg())
+      return false;
+    if (isa<MCTargetExpr>(getMemOff()) ||
+        (isConstantMemOff() && isInt<16>(getConstantMemOff())))
+      return true;
+    MCValue Res;
+    bool IsReloc = getMemOff()->evaluateAsRelocatable(Res, nullptr, nullptr);
+    return IsReloc && isInt<16>(Res.getConstant());
+  }
+
   template <unsigned Bits, unsigned ShiftLeftAmount>
   bool isScaledUImm() const {
     return isConstantImm() &&
            isShiftedUInt<Bits, ShiftLeftAmount>(getConstantImm());
   }
 
+  template <unsigned Bits, unsigned ShiftLeftAmount>
+  bool isConstantScaledSImm() const {
+    return isConstantImm() &&
+           isShiftedInt<Bits, ShiftLeftAmount>(getConstantImm());
+  }
+
   template <unsigned Bits, unsigned ShiftLeftAmount>
   bool isScaledSImm() const {
-    if (isConstantImm() &&
-        isShiftedInt<Bits, ShiftLeftAmount>(getConstantImm()))
+    if (isConstantScaledSImm<Bits, ShiftLeftAmount>())
       return true;
     // Operand can also be a symbol or symbol plus
     // offset in case of relocations.
@@ -1442,6 +1593,23 @@ class MipsOperand : public MCParsedAsmOperand {
     return true;
   }
 
+  bool isSaveRestore16() const {
+    if (!isSaveRestore())
+      return false;
+
+    if (SaveRestore.Framesize == 0 || SaveRestore.Framesize > 128)
+      return false;
+
+    if (SaveRestore.StaticRegsMask != 0)
+      return false;
+
+    // The 16-bit instruction can save only regs $16, $17, and $31.
+    if (SaveRestore.SavedRegsMask & ~0x80030000)
+      return false;
+
+    return true;
+  }
+
   bool isInvNum() const { return Kind == k_Immediate; }
 
   bool isLSAImm() const {
@@ -1453,6 +1621,8 @@ class MipsOperand : public MCParsedAsmOperand {
 
   bool isRegList() const { return Kind == k_RegList; }
 
+  bool isSaveRestore() const { return Kind == k_SaveRestore; }
+
   StringRef getToken() const {
     assert(Kind == k_Token && "Invalid access!");
     return StringRef(Tok.Data, Tok.Length);
@@ -1500,6 +1670,21 @@ class MipsOperand : public MCParsedAsmOperand {
     return *(RegList.List);
   }
 
+  unsigned getSaveRestoreSavedRegs() const {
+    assert((Kind == k_SaveRestore) && "Invalid access!");
+    return SaveRestore.SavedRegsMask;
+  }
+
+  unsigned getSaveRestoreStaticRegs() const {
+    assert((Kind == k_SaveRestore) && "Invalid access!");
+    return SaveRestore.StaticRegsMask;
+  }
+
+  unsigned getSaveRestoreFramesize() const {
+    assert((Kind == k_SaveRestore) && "Invalid access!");
+    return SaveRestore.Framesize;
+  }
+
   static std::unique_ptr<MipsOperand> CreateToken(StringRef Str, SMLoc S,
                                                   MipsAsmParser &Parser) {
     auto Op = std::make_unique<MipsOperand>(k_Token, Parser);
@@ -1607,13 +1792,32 @@ class MipsOperand : public MCParsedAsmOperand {
     return Op;
   }
 
- bool isGPRZeroAsmReg() const {
+  static std::unique_ptr<MipsOperand>
+  CreateSaveRestore(unsigned SavedRegsMask, unsigned StaticRegsMask,
+                    unsigned Framesize, SMLoc StartLoc, SMLoc EndLoc,
+                    MipsAsmParser &Parser) {
+    auto Op = std::make_unique<MipsOperand>(k_SaveRestore, Parser);
+    Op->SaveRestore.SavedRegsMask = SavedRegsMask;
+    Op->SaveRestore.StaticRegsMask = StaticRegsMask;
+    Op->SaveRestore.Framesize = Framesize;
+    Op->StartLoc = StartLoc;
+    Op->EndLoc = EndLoc;
+    return Op;
+  }
+
+  static std::unique_ptr<MipsOperand> createPCReg(const MCRegisterInfo *RegInfo,
+                                                  SMLoc S, SMLoc E,
+                                                  MipsAsmParser &Parser) {
+    return CreateReg(0, "pc", RegKind_PC, RegInfo, S, E, Parser);
+  }
+
+  bool isGPRZeroAsmReg() const {
     return isRegIdx() && RegIdx.Kind & RegKind_GPR && RegIdx.Index == 0;
   }
 
- bool isGPRNonZeroAsmReg() const {
-   return isRegIdx() && RegIdx.Kind & RegKind_GPR && RegIdx.Index > 0 &&
-          RegIdx.Index <= 31;
+  bool isGPRNonZeroAsmReg() const {
+    return isRegIdx() && RegIdx.Kind & RegKind_GPR && RegIdx.Index > 0 &&
+           RegIdx.Index <= 31;
   }
 
   bool isGPRAsmReg() const {
@@ -1655,6 +1859,41 @@ class MipsOperand : public MCParsedAsmOperand {
       (RegIdx.Index >= 5 && RegIdx.Index <= 7));
   }
 
+  bool isCPU16AsmReg() const {
+    if (!(isRegIdx() && RegIdx.Kind))
+      return false;
+    return ((RegIdx.Index >= 2 && RegIdx.Index <= 7) || RegIdx.Index == 16 ||
+            RegIdx.Index == 17);
+  }
+
+  bool isCPU16AsmRegPlusSP() const {
+    if (!(isRegIdx() && RegIdx.Kind))
+      return false;
+    return ((RegIdx.Index >= 2 && RegIdx.Index <= 7) || RegIdx.Index == 16 ||
+            RegIdx.Index == 17 || RegIdx.Index == 29);
+  }
+
+  // These next three are used in MIPS16 mode to find instructions that perform
+  // operations involving the program counter, SP, or RA. They are "pseudo
+  // registers" because they determine the opcode instead of being encoded as
+  // an operand. In this way, they are part of the instruction mnemonic.
+  bool isPCPseudoReg() const {
+    // "pc" is not a real register and has no number
+    return isRegIdx() && RegIdx.Kind == RegKind_PC;
+  }
+
+  bool isSPPseudoReg() const {
+    // Purposefully do not allow numeric registers; that is, require "$sp"
+    // instead of "$29"
+    return isRegIdx() && RegIdx.Kind == RegKind_GPR && RegIdx.Index == 29;
+  }
+
+  bool isRAPseudoReg() const {
+    // Purposefully do not allow numeric registers; that is, require "$ra"
+    // instead of "$31"
+    return isRegIdx() && RegIdx.Kind == RegKind_GPR && RegIdx.Index == 31;
+  }
+
   bool isFGRAsmReg() const {
     // AFGR64 is $0-$15 but we handle this in getAFGR64()
     return isRegIdx() && RegIdx.Kind & RegKind_FGR && RegIdx.Index <= 31;
@@ -1735,6 +1974,13 @@ class MipsOperand : public MCParsedAsmOperand {
         OS << Reg << " ";
       OS <<  ">";
       break;
+    case k_SaveRestore:
+      OS << "SaveRestore<CallerSaved 0x";
+      OS.write_hex(SaveRestore.SavedRegsMask);
+      OS << ", Framesize " << SaveRestore.Framesize << ", CalleeSaved 0x";
+      OS.write_hex(SaveRestore.StaticRegsMask);
+      OS << ">";
+      break;
     }
   }
 
@@ -1771,6 +2017,11 @@ static bool hasShortDelaySlot(MCInst &Inst) {
     case Mips::JALRS16_MM:
     case Mips::BGEZALS_MM:
     case Mips::BLTZALS_MM:
+    case Mips::Jal16:
+    case Mips::Jalx16:
+    case Mips::JrRa16:
+    case Mips::JrRx16:
+    case Mips::JalrRaRx16:
       return true;
     case Mips::J_MM:
       return !Inst.getOperand(0).isReg();
@@ -1988,6 +2239,44 @@ bool MipsAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
       if (offsetToAlignment(Offset.getImm(), Align(2)))
         return Error(IDLoc, "branch to misaligned address");
       break;
+    // MIPS16 extended branches/jumps.
+    // The 16-bit variants do not need handling here because their predicate
+    // methods require a scaled 8-bit constant immediate. The instruction
+    // matcher will fall back to these extended variants as needed.
+    case Mips::BimmX16:
+    case Mips::BteqzX16:
+    case Mips::BtnezX16:
+      assert(MCID.getNumOperands() == 1 && "unexpected number of operands");
+      Offset = Inst.getOperand(0);
+      if (!Offset.isImm())
+        break; // We'll deal with this situation later on when applying fixups.
+      if (!isInt<17>(Offset.getImm()))
+        return Error(IDLoc, "branch target out of range");
+      if (offsetToAlignment(Offset.getImm(), Align(2)))
+        return Error(IDLoc, "branch to misaligned address");
+      break;
+    case Mips::BeqzRxImmX16:
+    case Mips::BnezRxImmX16:
+      assert(MCID.getNumOperands() == 2 && "unexpected number of operands");
+      Offset = Inst.getOperand(1);
+      if (!Offset.isImm())
+        break; // We'll deal with this situation later on when applying fixups.
+      if (!isInt<17>(Offset.getImm()))
+        return Error(IDLoc, "branch target out of range");
+      if (offsetToAlignment(Offset.getImm(), Align(2)))
+        return Error(IDLoc, "branch to misaligned address");
+      break;
+    case Mips::Jal16:
+    case Mips::Jalx16:
+      assert(MCID.getNumOperands() == 1 && "unexpected number of operands");
+      Offset = Inst.getOperand(0);
+      if (!Offset.isImm())
+        break; // We'll deal with this situation later on when applying fixups.
+      if (!isUInt<28>(Offset.getImm()))
+        return Error(IDLoc, "branch target out of range");
+      if (offsetToAlignment(Offset.getImm(), Align(4)))
+        return Error(IDLoc, "branch to misaligned address");
+      break;
     }
   }
 
@@ -2385,6 +2674,7 @@ bool MipsAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
     TOut.setUsesMicroMips();
     TOut.updateABIInfo(*this);
   }
+  // FIXME: Do something similar for MIPS16?
 
   // If this instruction has a delay slot and .set reorder is active,
   // emit a NOP after it.
@@ -6057,6 +6347,9 @@ bool MipsAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
   case Match_UImm3_0:
     return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
                  "expected 3-bit unsigned immediate");
+  case Match_UImm3_1:
+    return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
+                 "expected immediate in range 1 ... 8");
   case Match_UImm4_0:
     return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
                  "expected 4-bit unsigned immediate");
@@ -6110,6 +6403,9 @@ bool MipsAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
   case Match_UImm8_0:
     return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
                  "expected 8-bit unsigned immediate");
+  case Match_SImm8_Lsl3:
+    return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
+                 "expected both 11-bit signed immediate and multiple of 8");
   case Match_UImm10_0:
     return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
                  "expected 10-bit unsigned immediate");
@@ -6124,6 +6420,9 @@ bool MipsAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
   case Match_UImm16_AltRelaxed:
     return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
                  "expected 16-bit unsigned immediate");
+  case Match_SImm15:
+    return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
+                 "expected 15-bit signed immediate");
   case Match_SImm16:
   case Match_SImm16_Relaxed:
     return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
@@ -6384,6 +6683,15 @@ int MipsAsmParser::matchMSA128CtrlRegisterName(StringRef Name) {
   return CC;
 }
 
+int MipsAsmParser::matchPCRegisterName(StringRef Name) {
+  // Used only in MIPS16; check in case someone was using "pc" as a symbol name
+  // in other MIPS modes.
+  if (inMips16Mode() && Name == "pc")
+    return 0;
+
+  return -1;
+}
+
 bool MipsAsmParser::canUseATReg() {
   return AssemblerOptions.back()->getATRegIndex() != 0;
 }
@@ -6740,6 +7048,13 @@ ParseStatus MipsAsmParser::matchAnyRegisterNameWithoutDollar(
     return ParseStatus::Success;
   }
 
+  Index = matchPCRegisterName(Identifier);
+  if (Index != -1) {
+    Operands.push_back(MipsOperand::createPCReg(getContext().getRegisterInfo(),
+                                                S, getLexer().getLoc(), *this));
+    return ParseStatus::Success;
+  }
+
   return ParseStatus::NoMatch;
 }
 
@@ -6927,6 +7242,122 @@ ParseStatus MipsAsmParser::parseRegisterList(OperandVector &Operands) {
   return ParseStatus::Success;
 }
 
+ParseStatus MipsAsmParser::parseSaveRestoreOperands(OperandVector &Operands) {
+  MCAsmParser &Parser = getParser();
+  SMLoc OperandsStart = Parser.getTok().getLoc();
+  uint32_t SavedRegMask = 0;
+  uint32_t StaticRegMask = 0;
+  uint32_t *RegMaskPtr = &SavedRegMask;
+  unsigned PrevRegIndex = 0;
+  unsigned Framesize = 0;
+  bool FramesizeSet = false;
+  bool IsRange = false;
+  const llvm::MCRegisterInfo *RegInfo = getContext().getRegisterInfo();
+
+  // MIPS16 Save and Restore instructions are weird and the MIPS16 docs do
+  // ...
[truncated]

@brad0 brad0 requested a review from wzssyqa September 14, 2024 05:29
@brad0
Copy link
Contributor

brad0 commented Sep 14, 2024

cc @yingopq

@wzssyqa
Copy link
Contributor

wzssyqa commented Sep 23, 2024

It's a huge patch. I think that I will need some time to review it.

@jdeguire
Copy link
Contributor Author

Yeah...sorry about that. I wasn't sure how to break this up since a lot of it needs to be here for the MIPS16 instructions to get parsed. Take all the time you need and thanks!

Oh! On another note, a long time ago on Bugzilla I submitted bug #50595 which is what lead me down the long path of MIPS16. This PR should address that bug report and maybe also #48386. Is there something I need to do to link this PR to those bugs or vice versa?

@jdeguire
Copy link
Contributor Author

Ping!

Any luck getting through this big patch? I probably should have followed up sooner, but I got caught up in other things. Thanks!

@jdeguire
Copy link
Contributor Author

jdeguire commented Dec 9, 2024

PIng

@jdeguire
Copy link
Contributor Author

jdeguire commented Jan 8, 2025

Happy New Year ping!

@jdeguire
Copy link
Contributor Author

Ping!

@jdeguire
Copy link
Contributor Author

jdeguire commented Feb 5, 2025

Pong!

@@ -0,0 +1,167 @@
# REQUIRES: mips-registered-target
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

llvm/test/MC/Disassembler/Mips/lit.local.cfg requires mips so you can drop this

Copy link
Member

@MaskRay MaskRay Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider utils/update_mc_test_checks.py and use [] (atomic blocks)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "REQUIRES" line has been removed and I surrounded the machine code values in brackets. That utility script does not exist in the version of LLVM this PR is based on. Should I try to rebase this PR to the latest one or leave this be?

@@ -1,10 +1,213 @@
# RUN: not llvm-mc -triple=mips -mcpu=mips32r2 -mattr=+mips16 < %s 2> %t
# REQUIRES: mips-registered-target
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unneeded thanks to llvm/test/MC/Mips/lit.local.cfg

Copy link
Contributor Author

@jdeguire jdeguire Feb 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to know, thanks! I removed the "REQUIRES" line and pushed a "fixup" commit like the docs said.

@jdeguire
Copy link
Contributor Author

jdeguire commented May 4, 2025

Ping!

I'm guessing this isn't going to merge cleanly at this point. I don't have commit access, so I don't know if I should try to update this or leave it be until it's accepted.

@MaskRay
Copy link
Member

MaskRay commented May 4, 2025

The MIPS port lacks adequate maintenance and has accumulated significant technical debt. Few people seem to care about it today.

I maintain MC and binary utilities, occasionally contributing to or reviewing MIPS changes.
To be clear, I have little interest in MIPS, but I’m concerned about its impact on specific components like the Clang driver, ld.lld, MC, and compiler-rt.
https://maskray.me/blog/2023-09-04-toolchain-notes-on-mips

Regarding this PR: MIPS16 and microMIPS are already partially supported (likely with many incorrect things), and since this PR modifies codegen, the title "[MIPS] Initial support for MIPS16 assembly" may be misleading (note: we omit periods in subject lines).

I doubt any regular contributor uses MIPS16 or microMIPS, so finding someone to thoroughly review these functional changes could be challenging.
I can provide a rubber stamp if needed.

The llvm-project receives over 100 commits daily. After a PR is approved, it’s best to rebase and retrigger CI if the PR has been stale for a few days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:MIPS mc Machine (object) code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants