Skip to content
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

[Xtensa] Implement Code Density Option. #119639

Merged
merged 5 commits into from
Dec 18, 2024
Merged

Conversation

andreisfr
Copy link
Contributor

The Code Density option adds 16-bit encoding for frequently used instructions.

The Code Density option adds 16-bit encoding for frequently used instructions.
@llvmbot llvmbot added the mc Machine (object) code label Dec 12, 2024
@llvmbot
Copy link
Member

llvmbot commented Dec 12, 2024

@llvm/pr-subscribers-mc

Author: Andrei Safronov (andreisfr)

Changes

The Code Density option adds 16-bit encoding for frequently used instructions.


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

14 Files Affected:

  • (modified) llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp (+10)
  • (modified) llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp (+73-8)
  • (modified) llvm/lib/Target/Xtensa/MCTargetDesc/XtensaAsmBackend.cpp (+3-1)
  • (modified) llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.cpp (+22)
  • (modified) llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.h (+2)
  • (modified) llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCCodeEmitter.cpp (+53-2)
  • (modified) llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp (+8-1)
  • (modified) llvm/lib/Target/Xtensa/XtensaISelLowering.cpp (+5-2)
  • (modified) llvm/lib/Target/Xtensa/XtensaInstrInfo.td (+101)
  • (modified) llvm/lib/Target/Xtensa/XtensaOperands.td (+14)
  • (added) llvm/test/MC/Xtensa/Options/code_density-invalid.s (+17)
  • (added) llvm/test/MC/Xtensa/Options/code_density.s (+63)
  • (modified) llvm/test/MC/Xtensa/Relocations/fixups.s (+14-9)
  • (modified) llvm/test/MC/Xtensa/Relocations/relocations.s (+10-2)
diff --git a/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp b/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp
index 83b1cfca529bf3..f1d12a51afb485 100644
--- a/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp
+++ b/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp
@@ -193,6 +193,10 @@ struct XtensaOperand : public MCParsedAsmOperand {
 
   bool isImm1_16() const { return isImm(1, 16); }
 
+  bool isImm1n_15() const { return (isImm(1, 15) || isImm(-1, -1)); }
+
+  bool isImm32n_95() const { return isImm(-32, 95); }
+
   bool isB4const() const {
     if (Kind != Immediate)
       return false;
@@ -480,6 +484,12 @@ bool XtensaAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
   case Match_InvalidImm1_16:
     return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
                  "expected immediate in range [1, 16]");
+  case Match_InvalidImm1n_15:
+    return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
+                 "expected immediate in range [-1, 15] except 0");
+  case Match_InvalidImm32n_95:
+    return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
+                 "expected immediate in range [-32, 95] except 0");
   case Match_InvalidShimm1_31:
     return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
                  "expected immediate in range [1, 31]");
diff --git a/llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp b/llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp
index 2d36b94dd40c77..8bff0f6660b52d 100644
--- a/llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp
+++ b/llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp
@@ -38,9 +38,7 @@ class XtensaDisassembler : public MCDisassembler {
   XtensaDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, bool isLE)
       : MCDisassembler(STI, Ctx), IsLittleEndian(isLE) {}
 
-  bool hasDensity() const {
-    return STI.hasFeature(Xtensa::FeatureDensity);
-  }
+  bool hasDensity() const { return STI.hasFeature(Xtensa::FeatureDensity); }
 
   DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size,
                               ArrayRef<uint8_t> Bytes, uint64_t Address,
@@ -99,8 +97,8 @@ static bool tryAddingSymbolicOperand(int64_t Value, bool isBranch,
                                      uint64_t InstSize, MCInst &MI,
                                      const void *Decoder) {
   const MCDisassembler *Dis = static_cast<const MCDisassembler *>(Decoder);
-  return Dis->tryAddingSymbolicOperand(MI, Value, Address, isBranch, Offset, /*OpSize=*/0,
-                                       InstSize);
+  return Dis->tryAddingSymbolicOperand(MI, Value, Address, isBranch, Offset,
+                                       /*OpSize=*/0, InstSize);
 }
 
 static DecodeStatus decodeCallOperand(MCInst &Inst, uint64_t Imm,
@@ -190,6 +188,28 @@ static DecodeStatus decodeImm1_16Operand(MCInst &Inst, uint64_t Imm,
   return MCDisassembler::Success;
 }
 
+static DecodeStatus decodeImm1n_15Operand(MCInst &Inst, uint64_t Imm,
+                                          int64_t Address,
+                                          const void *Decoder) {
+  assert(isUInt<4>(Imm) && "Invalid immediate");
+  if (!Imm)
+    Inst.addOperand(MCOperand::createImm(-1));
+  else
+    Inst.addOperand(MCOperand::createImm(Imm));
+  return MCDisassembler::Success;
+}
+
+static DecodeStatus decodeImm32n_95Operand(MCInst &Inst, uint64_t Imm,
+                                           int64_t Address,
+                                           const void *Decoder) {
+  assert(isUInt<7>(Imm) && "Invalid immediate");
+  if ((Imm & 0x60) == 0x60)
+    Inst.addOperand(MCOperand::createImm((~0x1f) | Imm));
+  else
+    Inst.addOperand(MCOperand::createImm(Imm));
+  return MCDisassembler::Success;
+}
+
 static DecodeStatus decodeShimm1_31Operand(MCInst &Inst, uint64_t Imm,
                                            int64_t Address,
                                            const void *Decoder) {
@@ -243,9 +263,37 @@ static DecodeStatus decodeMem32Operand(MCInst &Inst, uint64_t Imm,
   return MCDisassembler::Success;
 }
 
+static DecodeStatus decodeMem32nOperand(MCInst &Inst, uint64_t Imm,
+                                        int64_t Address, const void *Decoder) {
+  assert(isUInt<8>(Imm) && "Invalid immediate");
+  DecodeARRegisterClass(Inst, Imm & 0xf, Address, Decoder);
+  Inst.addOperand(MCOperand::createImm((Imm >> 2) & 0x3c));
+  return MCDisassembler::Success;
+}
+
+/// Read two bytes from the ArrayRef and return 16 bit data sorted
+/// according to the given endianness.
+static DecodeStatus readInstruction16(ArrayRef<uint8_t> Bytes, uint64_t Address,
+                                      uint64_t &Size, uint64_t &Insn,
+                                      bool IsLittleEndian) {
+  // We want to read exactly 2 Bytes of data.
+  if (Bytes.size() < 2) {
+    Size = 0;
+    return MCDisassembler::Fail;
+  }
+
+  if (!IsLittleEndian) {
+    llvm_unreachable("Big-endian mode currently is not supported!");
+  } else {
+    Insn = (Bytes[1] << 8) | Bytes[0];
+  }
+
+  return MCDisassembler::Success;
+}
+
 /// Read three bytes from the ArrayRef and return 24 bit data
 static DecodeStatus readInstruction24(ArrayRef<uint8_t> Bytes, uint64_t Address,
-                                      uint64_t &Size, uint32_t &Insn,
+                                      uint64_t &Size, uint64_t &Insn,
                                       bool IsLittleEndian) {
   // We want to read exactly 3 Bytes of data.
   if (Bytes.size() < 3) {
@@ -259,7 +307,6 @@ static DecodeStatus readInstruction24(ArrayRef<uint8_t> Bytes, uint64_t Address,
     Insn = (Bytes[2] << 16) | (Bytes[1] << 8) | (Bytes[0] << 0);
   }
 
-  Size = 3;
   return MCDisassembler::Success;
 }
 
@@ -269,13 +316,31 @@ DecodeStatus XtensaDisassembler::getInstruction(MCInst &MI, uint64_t &Size,
                                                 ArrayRef<uint8_t> Bytes,
                                                 uint64_t Address,
                                                 raw_ostream &CS) const {
-  uint32_t Insn;
+  uint64_t Insn;
   DecodeStatus Result;
 
+  // Parse 16-bit instructions
+  if (hasDensity()) {
+    Result = readInstruction16(Bytes, Address, Size, Insn, IsLittleEndian);
+    if (Result == MCDisassembler::Fail)
+      return MCDisassembler::Fail;
+    LLVM_DEBUG(dbgs() << "Trying Xtensa 16-bit instruction table :\n");
+    Result = decodeInstruction(DecoderTable16, MI, Insn, Address, this, STI);
+    if (Result != MCDisassembler::Fail) {
+      Size = 2;
+      return Result;
+    }
+  }
+
+  // Parse Core 24-bit instructions
   Result = readInstruction24(Bytes, Address, Size, Insn, IsLittleEndian);
   if (Result == MCDisassembler::Fail)
     return MCDisassembler::Fail;
   LLVM_DEBUG(dbgs() << "Trying Xtensa 24-bit instruction table :\n");
   Result = decodeInstruction(DecoderTable24, MI, Insn, Address, this, STI);
+  if (Result != MCDisassembler::Fail) {
+    Size = 3;
+    return Result;
+  }
   return Result;
 }
diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaAsmBackend.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaAsmBackend.cpp
index a296a22247a5c0..c1fb46e69e6fbe 100644
--- a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaAsmBackend.cpp
+++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaAsmBackend.cpp
@@ -88,8 +88,10 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
   case FK_Data_8:
     return Value;
   case Xtensa::fixup_xtensa_branch_6: {
+    if (!Value)
+      return 0;
     Value -= 4;
-    if (!isInt<6>(Value))
+    if (!isUInt<6>(Value))
       Ctx.reportError(Fixup.getLoc(), "fixup value out of range");
     unsigned Hi2 = (Value >> 4) & 0x3;
     unsigned Lo4 = Value & 0xf;
diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.cpp
index e04d7bd211216f..df8a0854f06f41 100644
--- a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.cpp
+++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.cpp
@@ -242,6 +242,28 @@ void XtensaInstPrinter::printImm1_16_AsmOperand(const MCInst *MI, int OpNum,
     printOperand(MI, OpNum, O);
 }
 
+void XtensaInstPrinter::printImm1n_15_AsmOperand(const MCInst *MI, int OpNum,
+                                                 raw_ostream &O) {
+  if (MI->getOperand(OpNum).isImm()) {
+    int64_t Value = MI->getOperand(OpNum).getImm();
+    assert((Value >= -1 && (Value != 0) && Value <= 15) &&
+           "Invalid argument, value must be in ranges <-1,-1> or <1,15>");
+    O << Value;
+  } else
+    printOperand(MI, OpNum, O);
+}
+
+void XtensaInstPrinter::printImm32n_95_AsmOperand(const MCInst *MI, int OpNum,
+                                                  raw_ostream &O) {
+  if (MI->getOperand(OpNum).isImm()) {
+    int64_t Value = MI->getOperand(OpNum).getImm();
+    assert((Value >= -32 && Value <= 95) &&
+           "Invalid argument, value must be in ranges <-32,95>");
+    O << Value;
+  } else
+    printOperand(MI, OpNum, O);
+}
+
 void XtensaInstPrinter::printOffset8m8_AsmOperand(const MCInst *MI, int OpNum,
                                                   raw_ostream &O) {
   if (MI->getOperand(OpNum).isImm()) {
diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.h b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.h
index f56d5d1458dc11..e5bc67869e103d 100644
--- a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.h
+++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaInstPrinter.h
@@ -58,6 +58,8 @@ class XtensaInstPrinter : public MCInstPrinter {
   void printUimm5_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O);
   void printShimm1_31_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O);
   void printImm1_16_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O);
+  void printImm1n_15_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O);
+  void printImm32n_95_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O);
   void printOffset8m8_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O);
   void printOffset8m16_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O);
   void printOffset8m32_AsmOperand(const MCInst *MI, int OpNum, raw_ostream &O);
diff --git a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCCodeEmitter.cpp b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCCodeEmitter.cpp
index 1afdbb38f9571a..51d4b8a9cc5fc5 100644
--- a/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCCodeEmitter.cpp
+++ b/llvm/lib/Target/Xtensa/MCTargetDesc/XtensaMCCodeEmitter.cpp
@@ -103,6 +103,14 @@ class XtensaMCCodeEmitter : public MCCodeEmitter {
                              SmallVectorImpl<MCFixup> &Fixups,
                              const MCSubtargetInfo &STI) const;
 
+  uint32_t getImm1n_15OpValue(const MCInst &MI, unsigned OpNo,
+                              SmallVectorImpl<MCFixup> &Fixups,
+                              const MCSubtargetInfo &STI) const;
+
+  uint32_t getImm32n_95OpValue(const MCInst &MI, unsigned OpNo,
+                               SmallVectorImpl<MCFixup> &Fixups,
+                               const MCSubtargetInfo &STI) const;
+
   uint32_t getShimm1_31OpValue(const MCInst &MI, unsigned OpNo,
                                SmallVectorImpl<MCFixup> &Fixups,
                                const MCSubtargetInfo &STI) const;
@@ -188,6 +196,11 @@ uint32_t XtensaMCCodeEmitter::getBranchTargetEncoding(
     Fixups.push_back(MCFixup::create(
         0, Expr, MCFixupKind(Xtensa::fixup_xtensa_branch_12), MI.getLoc()));
     return 0;
+  case Xtensa::BEQZ_N:
+  case Xtensa::BNEZ_N:
+    Fixups.push_back(MCFixup::create(
+        0, Expr, MCFixupKind(Xtensa::fixup_xtensa_branch_6), MI.getLoc()));
+    return 0;
   default:
     Fixups.push_back(MCFixup::create(
         0, Expr, MCFixupKind(Xtensa::fixup_xtensa_branch_8), MI.getLoc()));
@@ -255,14 +268,24 @@ XtensaMCCodeEmitter::getMemRegEncoding(const MCInst &MI, unsigned OpNo,
     break;
   case Xtensa::S32I:
   case Xtensa::L32I:
+  case Xtensa::S32I_N:
+  case Xtensa::L32I_N:
     if (Res & 0x3) {
       report_fatal_error("Unexpected operand value!");
     }
     Res >>= 2;
     break;
   }
-  
-  assert((isUInt<8>(Res)) && "Unexpected operand value!");
+
+  switch (MI.getOpcode()) {
+  case Xtensa::S32I_N:
+  case Xtensa::L32I_N:
+    assert((isUInt<4>(Res)) && "Unexpected operand value!");
+    break;
+  default:
+    assert((isUInt<8>(Res)) && "Unexpected operand value!");
+    break;
+  }
 
   uint32_t OffBits = Res << 4;
   uint32_t RegBits = getMachineOpValue(MI, MI.getOperand(OpNo), Fixups, STI);
@@ -354,6 +377,34 @@ XtensaMCCodeEmitter::getImm1_16OpValue(const MCInst &MI, unsigned OpNo,
   return (Res - 1);
 }
 
+uint32_t
+XtensaMCCodeEmitter::getImm1n_15OpValue(const MCInst &MI, unsigned OpNo,
+                                        SmallVectorImpl<MCFixup> &Fixups,
+                                        const MCSubtargetInfo &STI) const {
+  const MCOperand &MO = MI.getOperand(OpNo);
+  int32_t Res = static_cast<int32_t>(MO.getImm());
+
+  assert(((Res >= -1) && (Res <= 15) && (Res != 0)) &&
+         "Unexpected operand value!");
+
+  if (Res < 0)
+    Res = 0;
+
+  return Res;
+}
+
+uint32_t
+XtensaMCCodeEmitter::getImm32n_95OpValue(const MCInst &MI, unsigned OpNo,
+                                         SmallVectorImpl<MCFixup> &Fixups,
+                                         const MCSubtargetInfo &STI) const {
+  const MCOperand &MO = MI.getOperand(OpNo);
+  int32_t Res = static_cast<int32_t>(MO.getImm());
+
+  assert(((Res >= -32) && (Res <= 95)) && "Unexpected operand value!");
+
+  return Res;
+}
+
 uint32_t
 XtensaMCCodeEmitter::getB4constOpValue(const MCInst &MI, unsigned OpNo,
                                        SmallVectorImpl<MCFixup> &Fixups,
diff --git a/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp b/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp
index af1110487b4274..ef14095d18efbf 100644
--- a/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp
+++ b/llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp
@@ -27,10 +27,17 @@ using namespace llvm;
 namespace {
 
 class XtensaDAGToDAGISel : public SelectionDAGISel {
+  const XtensaSubtarget *Subtarget = nullptr;
+
 public:
-  XtensaDAGToDAGISel(XtensaTargetMachine &TM, CodeGenOptLevel OptLevel)
+  explicit XtensaDAGToDAGISel(XtensaTargetMachine &TM, CodeGenOptLevel OptLevel)
       : SelectionDAGISel(TM, OptLevel) {}
 
+  bool runOnMachineFunction(MachineFunction &MF) override {
+    Subtarget = &MF.getSubtarget<XtensaSubtarget>();
+    return SelectionDAGISel::runOnMachineFunction(MF);
+  }
+
   void Select(SDNode *Node) override;
 
   bool SelectInlineAsmMemoryOperand(const SDValue &Op,
diff --git a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp
index 7e43c03ee72cac..6dfda02b7622b8 100644
--- a/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp
+++ b/llvm/lib/Target/Xtensa/XtensaISelLowering.cpp
@@ -506,7 +506,8 @@ XtensaTargetLowering::LowerCall(CallLoweringInfo &CLI,
       SDValue Memcpy = DAG.getMemcpy(
           Chain, DL, Address, ArgValue, SizeNode, Flags.getNonZeroByValAlign(),
           /*isVolatile=*/false, /*AlwaysInline=*/false,
-          /*CI=*/nullptr, std::nullopt, MachinePointerInfo(), MachinePointerInfo());
+          /*CI=*/nullptr, std::nullopt, MachinePointerInfo(),
+          MachinePointerInfo());
       MemOpChains.push_back(Memcpy);
     } else {
       assert(VA.isMemLoc() && "Argument not register or memory");
@@ -1319,10 +1320,12 @@ MachineBasicBlock *XtensaTargetLowering::EmitInstrWithCustomInserter(
   case Xtensa::S8I:
   case Xtensa::S16I:
   case Xtensa::S32I:
+  case Xtensa::S32I_N:
   case Xtensa::L8UI:
   case Xtensa::L16SI:
   case Xtensa::L16UI:
-  case Xtensa::L32I: {
+  case Xtensa::L32I:
+  case Xtensa::L32I_N: {
     // Insert memory wait instruction "memw" before volatile load/store as it is
     // implemented in gcc. If memoperands is empty then assume that it aslo
     // maybe volatile load/store and insert "memw".
diff --git a/llvm/lib/Target/Xtensa/XtensaInstrInfo.td b/llvm/lib/Target/Xtensa/XtensaInstrInfo.td
index e21de0448aa5ae..699d0d6cf80445 100644
--- a/llvm/lib/Target/Xtensa/XtensaInstrInfo.td
+++ b/llvm/lib/Target/Xtensa/XtensaInstrInfo.td
@@ -577,3 +577,104 @@ let usesCustomInserter = 1 in {
                      "!select $dst, $lhs, $rhs, $t, $f, $cond",
                      [(set i32:$dst, (Xtensa_select_cc i32:$lhs, i32:$rhs, i32:$t, i32:$f, imm:$cond))]>;
 }
+
+//===----------------------------------------------------------------------===//
+// Code Density instructions
+//===----------------------------------------------------------------------===//
+
+class ArithLogic_RRRN<bits<4> oper0, string instrAsm,
+      SDPatternOperator opNode, bit isComm = 0>
+  : RRRN_Inst<oper0, (outs AR:$r), (ins AR:$s, AR:$t),
+              instrAsm#"\t$r, $s, $t",
+             [(set AR:$r, (opNode AR:$s, AR:$t))]>, Requires<[HasDensity]> {
+  let isCommutable = isComm;
+  let isReMaterializable = 0;
+}
+
+def ADD_N : ArithLogic_RRRN<0x0a, "add.n", add, 1>;
+
+def ADDI_N : RRRN_Inst<0x0B, (outs AR:$r), (ins AR:$s, imm1n_15:$imm),
+                      "addi.n\t$r, $s, $imm",
+                      [(set AR:$r, (add AR:$s, imm1n_15:$imm))]>, Requires<[HasDensity]> {
+  bits<4> imm;
+
+  let t = imm;
+}
+
+// Conditional branch instructions.
+let isBranch = 1, isTerminator = 1 in {
+  def BEQZ_N : RI6_Inst<0xC, 0x1, 0x0, (outs), (ins AR:$s, brtarget:$target),
+                       "beqz.n\t$s, $target", []>, Requires<[HasDensity]> {
+    bits<6> target;
+
+    let imm6 = target;
+  }
+
+  def BNEZ_N : RI6_Inst<0xC, 0x1, 0x1, (outs), (ins AR:$s, brtarget:$target),
+                       "bnez.n\t$s, $target", []>, Requires<[HasDensity]> {
+    bits<6> target;
+
+    let imm6 = target;
+  }
+}
+
+def ILL_N : RRRN_Inst<0x0D, (outs), (ins),
+                     "ill.n", []>, Requires<[HasDensity]> {
+  let r = 0xF;
+  let s = 0x0;
+  let t = 0x6;
+}
+
+def MOV_N : RRRN_Inst<0x0D, (outs AR:$t), (ins AR:$s),
+                     "mov.n\t$t, $s", []>, Requires<[HasDensity]> {
+  let r = 0;
+}
+
+def : InstAlias<"mov\t $t, $s", (OR AR:$t, AR:$s, AR:$s)>;
+
+def MOVI_N : RI7_Inst<0xc, 0x0, (outs AR:$s), (ins imm32n_95:$imm7),
+                     "movi.n\t$s, $imm7",
+                     [(set AR:$s, imm32n_95:$imm7)]>, Requires<[HasDensity]>;
+
+def : InstAlias<"_movi.n\t$s, $imm7", (MOVI_N AR:$s, imm32n_95:$imm7)>;
+
+def NOP_N : RRRN_Inst<0x0D, (outs), (ins),
+                     "nop.n", []>, Requires<[HasDensity]> {
+  let r = 0xF;
+  let s = 0x0;
+  let t = 0x3;
+}
+
+// Load instruction
+let mayLoad = 1, usesCustomInserter = 1 in {
+  def L32I_N : RRRN_Inst<0x8, (outs AR:$t), (ins mem32n:$addr),
+                        "l32i.n\t$t, $addr", []>, Requires<[HasDensity]> {
+    bits<8> addr;
+
+    let r{3-0} = addr{7-4};
+    let s{3-0} = addr{3-0};
+  }
+}
+
+// Store instruction
+let mayStore = 1, usesCustomInserter = 1 in {
+  def S32I_N : RRRN_Inst<0x9, (outs), (ins  AR:$t, mem32n:$addr),
+                        "s32i.n\t$t, $addr", []>, Requires<[HasDensity]> {
+    bits<8> addr;
+
+    let r{3-0} = addr{7-4};
+    let s{3-0} = addr{3-0};
+  }
+}
+
+//Return instruction
+let isReturn = 1, isTerminator = 1,
+    isBarrier = 1, Uses = [A0] in {
+  def RET_N : RRRN_Inst<0x0D, (outs), (ins),
+                       "ret.n", [(Xtensa_ret)]>,
+                       Requires<[HasDensity]> {
+    let r = 0x0F;
+    let s = 0;
+    let t = 0;
+  }
+}
diff --git a/llvm/lib/Target/Xtensa/XtensaOperands.td b/llvm/lib/Target/Xtensa/XtensaOperands.td
index f41081f9bf2f96..aa72fa0a56a6f5 100644
--- a/llvm/lib/Target/Xtensa/XtensaOperands.td
+++ b/llvm/lib/Target/Xtensa/XtensaOperands.td
@@ -72,6 +72,20 @@ def imm1_16 : Immediate<i32, [{ return Imm >= 1 && Imm <= 16; }], "Imm1_16_AsmOp
   let DecoderMethod = "decodeImm1_16Operand";
 }
 
+// imm1n_15 predicate - Immediate in the range [-1,15], except 0
+def Imm1n_15_AsmOperand: ImmAsmOperand<"Imm1n_15">;
+def imm1n_15: Immediate<i32, [{ return Imm >= -1 && Imm <= 15 && Imm != 0; }], "Imm1n_15_AsmOperand"> {
+  let EncoderMethod = "getImm1n_15OpValue";
+  let DecoderMethod = "decodeImm1n_15Operand";
+}
+
+// imm32n_95 predicate - Immediate in the range [-32,95]
+def Imm32n_95_AsmOperand: ImmAsmOperand<"Imm32n_95">;
+def imm32n_95: Immediate<i32, [{ return Imm >= -32 && Imm <= 95; }], "Imm32n_95_AsmOperand"> {
+  let EncoderMethod = "g...
[truncated]

@arsenm
Copy link
Contributor

arsenm commented Dec 12, 2024

Unrelated but can you open a PR to add the xtensa paths to .github/new-prs-labeler.yml

@@ -193,6 +193,10 @@ struct XtensaOperand : public MCParsedAsmOperand {

bool isImm1_16() const { return isImm(1, 16); }

bool isImm1n_15() const { return (isImm(1, 15) || isImm(-1, -1)); }
Copy link
Contributor

Choose a reason for hiding this comment

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

Extra parens. Not sure I understand the what || isImm(-1, -1) is doing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for review. I removed parens and added a comment.

@@ -0,0 +1,17 @@
# RUN: not llvm-mc -triple xtensa --mattr=+density %s 2>&1 | FileCheck %s

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see the point of having an "Options" subdirectory

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm sorry, you mean that we don't need to create specific directory for Xtensa architecture options(extensions) tests in "../MC/Xtensa/"? Or use other directory structure, for example for each option(extension) create correspondence directory "../MC/Xtensa/CodeDensity"?

Copy link
Contributor

@arsenm arsenm Dec 12, 2024

Choose a reason for hiding this comment

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

I mean just leave it in test/MC/Xtensa. I also wouldn't really call a subtarget feature an option

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I moved tests to MC/Xtensa from Option directory.

@@ -190,6 +188,28 @@ static DecodeStatus decodeImm1_16Operand(MCInst &Inst, uint64_t Imm,
return MCDisassembler::Success;
}

static DecodeStatus decodeImm1n_15Operand(MCInst &Inst, uint64_t Imm,
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing disassembler tests

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added disassembler test.

Comment on lines +327 to +328
LLVM_DEBUG(dbgs() << "Trying Xtensa 16-bit instruction table :\n");
Result = decodeInstruction(DecoderTable16, MI, Insn, Address, this, STI);
Copy link
Contributor

Choose a reason for hiding this comment

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

In the disassembler test, make sure to test mismatches (e.g. !hasDensity and there are encoded values using the extension)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mean try to disassemble 16-bit code with the hasDensity option disabled and check that it produces erroneous decoding?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, it should not crash

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added this verification to the disassembler test.

"expected immediate in range [-1, 15] except 0");
case Match_InvalidImm32n_95:
return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
"expected immediate in range [-32, 95] except 0");

Choose a reason for hiding this comment

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

  • isImm1n_15 checks -1..15, except 0 and the error message is reflecting this.
  • isImm32n_95, as implemented, includes 0, but the error here says that it does not.

Either the implementation, or the error message seems to be incorrect.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you very much for comments. I fixed error message.

# Out of range immediates

# imm1n_15
addi.n a2, a3, 20

Choose a reason for hiding this comment

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

Would you please add a test case for imm=0?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added test case.

# Instruction format RRRN
# CHECK-INST: addi.n a2, a3, 3
# CHECK: encoding: [0x3b,0x23]
addi.n a2, a3, 3

Choose a reason for hiding this comment

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

Would you please add a test case for imm=-1?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added test case.

}

if (!IsLittleEndian) {
llvm_unreachable("Big-endian mode currently is not supported!");
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is reachable with some triple option it shouldn't be an assert

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I changed "llvm_unreachable" in readInstruction16 to "report_fatal_error", like it is done in readInstruction24.
Hypothetically, the Xtensa architecture could have a big endian implementation, but in practice it is hard to find such hardware (at least I don't see such an example). So we don't expect such a triple variant at this time.

@andreisfr
Copy link
Contributor Author

Unrelated but can you open a PR to add the xtensa paths to .github/new-prs-labeler.yml

Thank you for advice. I created PR #120133

@gerekon
Copy link
Contributor

gerekon commented Dec 18, 2024

Link to tracking issue espressif#4

@andreisfr andreisfr merged commit c6967ef into llvm:main Dec 18, 2024
8 checks passed
jackhong12 pushed a commit to jackhong12/llvm-project that referenced this pull request Dec 18, 2024
The Code Density option adds 16-bit encoding for frequently used
instructions.

0x4a 0x23
# CHECK-DENSITY: add.n a2, a3, a4
# CHECK-CORE: [[#@LINE-2]]:1: warning: invalid instruction encoding
Copy link
Member

Choose a reason for hiding this comment

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

The test fails at HEAD. It's recommended to use [0x4a 0x23] so that llvm-mc will disassemble the bytes as a whole and not fallback (skip one byte and continue disassembling), leading to weird diagnostics.

You could utilize the recent --hex so that you can do [4a23]

For llvm-mc, s/< %s/%s/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@MaskRay, thank you very much for comment, I'm sorry, I tried to reproduce test fail using "check-llvm" but without success, may I ask you for advice about how to reproduce the problem?

Copy link
Member

Choose a reason for hiding this comment

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

You can reproduce with just ninja check-llvm-mc-disassembler-xtensa. Is the test skipped in your build?

You could change llvm/test/MC/Disassembler/Xtensa/lit.local.cfg locally to see whether Xtensa is in config.root.targets

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm sorry, that was some misunderstanding on my part. I created PR #121073, could you PTAL?

@@ -0,0 +1,64 @@
# RUN: llvm-mc -triple=xtensa -mattr=+density -disassemble < %s | FileCheck -check-prefixes=CHECK-DENSITY %s
# RUN: llvm-mc -triple=xtensa -disassemble %s &> %t
# RUN: FileCheck -check-prefixes=CHECK-CORE < %t %s
Copy link
Member

Choose a reason for hiding this comment

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

--implicit-check-not=warning: is useful to ensure that there are no additional warnings

MaskRay added a commit that referenced this pull request Dec 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:Xtensa mc Machine (object) code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants