[llvm] [Xtensa] Implement Code Density Option. (PR #119639)

Andrei Safronov via llvm-commits llvm-commits at lists.llvm.org
Mon Dec 16 11:33:15 PST 2024


https://github.com/andreisfr updated https://github.com/llvm/llvm-project/pull/119639

>From b9a6a95909b50275cffab3dbb61b9429d4713048 Mon Sep 17 00:00:00 2001
From: Andrei Safronov <safronov at espressif.com>
Date: Thu, 12 Dec 2024 03:25:35 +0300
Subject: [PATCH 1/5] [Xtensa] Implement Code Density Option.

The Code Density option adds 16-bit encoding for frequently used instructions.
---
 .../Xtensa/AsmParser/XtensaAsmParser.cpp      |  10 ++
 .../Disassembler/XtensaDisassembler.cpp       |  81 ++++++++++++--
 .../Xtensa/MCTargetDesc/XtensaAsmBackend.cpp  |   4 +-
 .../Xtensa/MCTargetDesc/XtensaInstPrinter.cpp |  22 ++++
 .../Xtensa/MCTargetDesc/XtensaInstPrinter.h   |   2 +
 .../MCTargetDesc/XtensaMCCodeEmitter.cpp      |  55 +++++++++-
 llvm/lib/Target/Xtensa/XtensaISelDAGToDAG.cpp |   9 +-
 llvm/lib/Target/Xtensa/XtensaISelLowering.cpp |   7 +-
 llvm/lib/Target/Xtensa/XtensaInstrInfo.td     | 101 ++++++++++++++++++
 llvm/lib/Target/Xtensa/XtensaOperands.td      |  14 +++
 .../MC/Xtensa/Options/code_density-invalid.s  |  17 +++
 llvm/test/MC/Xtensa/Options/code_density.s    |  63 +++++++++++
 llvm/test/MC/Xtensa/Relocations/fixups.s      |  23 ++--
 llvm/test/MC/Xtensa/Relocations/relocations.s |  12 ++-
 14 files changed, 395 insertions(+), 25 deletions(-)
 create mode 100644 llvm/test/MC/Xtensa/Options/code_density-invalid.s
 create mode 100644 llvm/test/MC/Xtensa/Options/code_density.s

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 = "getImm32n_95OpValue";
+  let DecoderMethod = "decodeImm32n_95Operand";
+}
+
 // shimm1_31 predicate - Immediate in the range [1,31]
 def Shimm1_31_AsmOperand : ImmAsmOperand<"Shimm1_31">;
 def shimm1_31 : Immediate<i32, [{ return Imm >= 1 && Imm <= 31; }], "Shimm1_31_AsmOperand"> {
diff --git a/llvm/test/MC/Xtensa/Options/code_density-invalid.s b/llvm/test/MC/Xtensa/Options/code_density-invalid.s
new file mode 100644
index 00000000000000..8d0c472e3f1da0
--- /dev/null
+++ b/llvm/test/MC/Xtensa/Options/code_density-invalid.s
@@ -0,0 +1,17 @@
+# RUN: not llvm-mc -triple xtensa --mattr=+density %s 2>&1 | FileCheck %s
+
+LBL0:
+
+# Out of range immediates
+
+# imm1n_15
+addi.n a2, a3, 20
+# CHECK: :[[#@LINE-1]]:16: error: expected immediate in range [-1, 15] except 0
+
+# imm32n_95
+movi.n a2, 100
+# CHECK: :[[#@LINE-1]]:12: error: expected immediate in range [-32, 95] except 0
+
+# Offset4m32
+l32i.n a2, a3, 100
+# CHECK: :[[#@LINE-1]]:16: error: expected immediate in range [0, 60], first 2 bits should be zero
diff --git a/llvm/test/MC/Xtensa/Options/code_density.s b/llvm/test/MC/Xtensa/Options/code_density.s
new file mode 100644
index 00000000000000..429b3d6a03daf8
--- /dev/null
+++ b/llvm/test/MC/Xtensa/Options/code_density.s
@@ -0,0 +1,63 @@
+# RUN: llvm-mc %s -triple=xtensa -show-encoding --mattr=+density \
+# RUN:     | FileCheck -check-prefixes=CHECK,CHECK-INST %s
+
+.align	4
+LBL0:
+
+# Instruction format RRRN
+# CHECK-INST: add.n a2, a3, a4
+# CHECK: encoding: [0x4a,0x23]
+add.n a2, a3, a4
+
+# Instruction format RRRN
+# CHECK-INST: addi.n a2, a3, 3
+# CHECK: encoding: [0x3b,0x23]
+addi.n a2, a3, 3
+
+# Instruction format RI6
+# CHECK-INST:  beqz.n  a3, LBL1
+# CHECK: encoding: [0x8c'A',0x03'A']
+beqz.n  a3, LBL1
+
+# Instruction format RI6
+# CHECK-INST:  bnez.n  a3, LBL1
+# CHECK: encoding: [0xcc'A',0x03'A']
+bnez.n  a3, LBL1
+
+# Instruction format RRRN
+# CHECK-INST: ill.n
+# CHECK: encoding: [0x6d,0xf0]
+ill.n
+
+# Instruction format RRRN
+# CHECK-INST: l32i.n a2, a3, 12
+# CHECK: encoding: [0x28,0x33]
+l32i.n a2, a3, 12
+
+# Instruction format RRRN
+# CHECK-INST: mov.n a2, a3
+# CHECK: encoding: [0x2d,0x03]
+mov.n a2, a3
+
+# Instruction format RI7
+# CHECK-INST: movi.n a2, -32
+# CHECK: encoding: [0x6c,0x02]
+movi.n a2, -32
+
+# Instruction format RRRN
+# CHECK-INST: nop.n
+# CHECK: encoding: [0x3d,0xf0]
+nop.n
+
+# Instruction format RRRN
+# CHECK-INST: ret.n
+# CHECK: encoding: [0x0d,0xf0]
+ret.n
+
+# Instruction format RRRN
+# CHECK-INST: s32i.n a2, a3, 12
+# CHECK: encoding: [0x29,0x33]
+s32i.n a2, a3, 12
+
+.align	4
+LBL1:
diff --git a/llvm/test/MC/Xtensa/Relocations/fixups.s b/llvm/test/MC/Xtensa/Relocations/fixups.s
index cd76f2a23322d8..0a3a9eeef1159c 100644
--- a/llvm/test/MC/Xtensa/Relocations/fixups.s
+++ b/llvm/test/MC/Xtensa/Relocations/fixups.s
@@ -1,7 +1,7 @@
-# RUN: llvm-mc -triple xtensa < %s -show-encoding \
+# RUN: llvm-mc -triple xtensa --mattr=+density < %s -show-encoding \
 # RUN:     | FileCheck -check-prefix=CHECK-FIXUP %s
-# RUN: llvm-mc -filetype=obj -triple xtensa < %s \
-# RUN:     | llvm-objdump -d - | FileCheck -check-prefix=CHECK-INSTR %s
+# RUN: llvm-mc -filetype=obj -triple xtensa --mattr=+density < %s \
+# RUN:     | llvm-objdump --mattr=+density -d - | FileCheck -check-prefix=CHECK-INSTR %s
 
 
 # Checks that fixups that can be resolved within the same object file are
@@ -11,9 +11,13 @@ LBL0:
 
 .fill 12
 
+beqz.n a2, LBL1
+# CHECK-FIXUP: fixup A - offset: 0, value: LBL1, kind: fixup_xtensa_branch_6
+# CHECK-INSTR: beqz.n a2, . +29
+
 beq a0, a1, LBL0
 # CHECK-FIXUP: fixup A - offset: 0, value: LBL0, kind: fixup_xtensa_branch_8
-# CHECK-INSTR: beq a0, a1, . -12
+# CHECK-INSTR: beq a0, a1, . -14
 
 beq a0, a1, LBL1
 # CHECK-FIXUP: fixup A - offset: 0, value: LBL1, kind: fixup_xtensa_branch_8
@@ -21,7 +25,7 @@ beq a0, a1, LBL1
 
 beqz a2, LBL0
 # CHECK-FIXUP: fixup A - offset: 0, value: LBL0, kind: fixup_xtensa_branch_12
-# CHECK-INSTR: beqz a2, . -18
+# CHECK-INSTR: beqz a2, . -20
 
 beqz a2, LBL1
 # CHECK-FIXUP: fixup A - offset: 0, value: LBL1, kind: fixup_xtensa_branch_12
@@ -33,22 +37,23 @@ call0 LBL0
 
 call0 LBL2
 # CHECK-FIXUP: fixup A - offset: 0, value: LBL2, kind: fixup_xtensa_call_18
-# CHECK-INSTR: call0 . +2056
+# CHECK-INSTR: call0 . +2068
 
 j LBL0
 # CHECK-FIXUP: fixup A - offset: 0, value: LBL0, kind: fixup_xtensa_jump_18
-# CHECK-INSTR: j . -30
+# CHECK-INSTR: j . -32
 
 j LBL2
 # CHECK-FIXUP: fixup A - offset: 0, value: LBL2, kind: fixup_xtensa_jump_18
-# CHECK-INSTR: j . +2047
+# CHECK-INSTR: j . +2061
 
 l32r a1, LBL0
 # CHECK-FIXUP: fixup A - offset: 0, value: LBL0, kind: fixup_xtensa_l32r_16
-# CHECK-INSTR: l32r a1, . -36
+# CHECK-INSTR: l32r a1, . -38
 
 LBL1:
 
 .fill 2041
 
+.align 4
 LBL2:
diff --git a/llvm/test/MC/Xtensa/Relocations/relocations.s b/llvm/test/MC/Xtensa/Relocations/relocations.s
index 19c2e16352509d..339f6cb44bfcfd 100644
--- a/llvm/test/MC/Xtensa/Relocations/relocations.s
+++ b/llvm/test/MC/Xtensa/Relocations/relocations.s
@@ -1,6 +1,6 @@
-# RUN: llvm-mc -triple xtensa < %s -show-encoding \
+# RUN: llvm-mc -triple xtensa --mattr=+density < %s -show-encoding \
 # RUN:     | FileCheck -check-prefix=INSTR -check-prefix=FIXUP %s
-# RUN: llvm-mc -filetype=obj -triple xtensa < %s \
+# RUN: llvm-mc -filetype=obj -triple xtensa --mattr=+density < %s \
 # RUN:     | llvm-readobj -r - | FileCheck -check-prefix=RELOC %s
 
 # Check prefixes:
@@ -76,6 +76,14 @@ beqz a8, func
 # INST:  beqz    a8, func
 # FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_12
 
+beqz.n a8, func
+# INST:  beqz.n    a8, func
+# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_6
+
+bnez.n a8, func
+# INST:  bnez.n    a8, func
+# FIXUP: fixup A - offset: 0, value: func, kind: fixup_xtensa_branch_6
+
 bge a14, a2, func
 # RELOC: R_XTENSA_SLOT0_OP
 # INST:  bge     a14, a2, func

>From 9dc207f402f50408f203cd205776ff43056ad405 Mon Sep 17 00:00:00 2001
From: Andrei Safronov <safronov at espressif.com>
Date: Fri, 13 Dec 2024 01:31:14 +0300
Subject: [PATCH 2/5] [Xtensa] Minor fix.

---
 llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp b/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp
index f1d12a51afb485..d2c4537d18dffa 100644
--- a/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp
+++ b/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp
@@ -193,7 +193,8 @@ struct XtensaOperand : public MCParsedAsmOperand {
 
   bool isImm1_16() const { return isImm(1, 16); }
 
-  bool isImm1n_15() const { return (isImm(1, 15) || isImm(-1, -1)); }
+  // Check that value is either equals (-1) or from [1,15] range.
+  bool isImm1n_15() const { return isImm(1, 15) || isImm(-1, -1); }
 
   bool isImm32n_95() const { return isImm(-32, 95); }
 

>From 32270ea222f0a7d7dbf7bb99800635576440003a Mon Sep 17 00:00:00 2001
From: Andrei Safronov <safronov at espressif.com>
Date: Fri, 13 Dec 2024 16:59:16 +0300
Subject: [PATCH 3/5] [Xtensa] Fix asm parser and add disassembler test.

---
 .../Xtensa/AsmParser/XtensaAsmParser.cpp      |  2 +-
 .../MC/Disassembler/Xtensa/code_density.txt   | 64 +++++++++++++++++++
 .../{Options => }/code_density-invalid.s      |  6 +-
 .../MC/Xtensa/{Options => }/code_density.s    |  5 ++
 4 files changed, 75 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/MC/Disassembler/Xtensa/code_density.txt
 rename llvm/test/MC/Xtensa/{Options => }/code_density-invalid.s (79%)
 rename llvm/test/MC/Xtensa/{Options => }/code_density.s (92%)

diff --git a/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp b/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp
index d2c4537d18dffa..731f9535ca251f 100644
--- a/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp
+++ b/llvm/lib/Target/Xtensa/AsmParser/XtensaAsmParser.cpp
@@ -490,7 +490,7 @@ bool XtensaAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
                  "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");
+                 "expected immediate in range [-32, 95]");
   case Match_InvalidShimm1_31:
     return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo),
                  "expected immediate in range [1, 31]");
diff --git a/llvm/test/MC/Disassembler/Xtensa/code_density.txt b/llvm/test/MC/Disassembler/Xtensa/code_density.txt
new file mode 100644
index 00000000000000..b2c91bcfbaefec
--- /dev/null
+++ b/llvm/test/MC/Disassembler/Xtensa/code_density.txt
@@ -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
+
+#------------------------------------------------------------------------------
+# Verify that binary code is correctly disassembled with
+# code density option enabled. Also verify that dissasembling without
+# density option generates warnings.
+#------------------------------------------------------------------------------
+
+0x4a 0x23
+# CHECK-DENSITY: add.n  a2, a3, a4
+# CHECK-CORE: [[#@LINE-2]]:1: warning: invalid instruction encoding
+# CHECK-CORE: [[#@LINE-3]]:6: warning: invalid instruction encoding
+
+0x3b 0x23
+# CHECK-DENSITY: addi.n a2, a3, 3
+# CHECK-CORE: [[#@LINE-2]]:1: warning: invalid instruction encoding
+# CHECK-CORE: [[#@LINE-3]]:6: warning: invalid instruction encoding
+
+0x9c 0x03
+# CHECK-DENSITY: beqz.n a3, . +20
+# CHECK-CORE: [[#@LINE-2]]:1: warning: invalid instruction encoding
+# CHECK-CORE: [[#@LINE-3]]:6: warning: invalid instruction encoding
+
+0xcc 0xe3
+# CHECK-DENSITY: bnez.n a3, . +18
+# CHECK-CORE: [[#@LINE-2]]:1: warning: invalid instruction encoding
+# CHECK-CORE: [[#@LINE-3]]:6: warning: invalid instruction encoding
+
+0x6d 0xf0
+# CHECK-DENSITY: ill.n
+# CHECK-CORE: [[#@LINE-2]]:1: warning: invalid instruction encoding
+# CHECK-CORE: [[#@LINE-3]]:6: warning: invalid instruction encoding
+
+0x28 0x33
+# CHECK-DENSITY: l32i.n a2, a3, 12
+# CHECK-CORE: [[#@LINE-2]]:1: warning: invalid instruction encoding
+# CHECK-CORE: [[#@LINE-3]]:6: warning: invalid instruction encoding
+
+0x2d 0x03
+# CHECK-DENSITY: mov.n  a2, a3
+# CHECK-CORE: [[#@LINE-2]]:1: warning: invalid instruction encoding
+# CHECK-CORE: [[#@LINE-3]]:6: warning: invalid instruction encoding
+
+0x0d 0xf0
+# CHECK-DENSITY: ret.n
+# CHECK-CORE: [[#@LINE-2]]:1: warning: invalid instruction encoding
+# CHECK-CORE: [[#@LINE-3]]:6: warning: invalid instruction encoding
+
+0x29 0x33
+# CHECK-DENSITY: s32i.n a2, a3, 12
+# CHECK-CORE: [[#@LINE-2]]:1: warning: invalid instruction encoding
+# CHECK-CORE: [[#@LINE-3]]:6: warning: invalid instruction encoding
+
+0x6c 0x02
+# CHECK-DENSITY: movi.n a2, -32
+# CHECK-CORE: [[#@LINE-2]]:1: warning: invalid instruction encoding
+# CHECK-CORE: [[#@LINE-3]]:6: warning: invalid instruction encoding
+
+0x3d 0xf0
+# CHECK-DENSITY: nop.n
+# CHECK-CORE: [[#@LINE-2]]:1: warning: invalid instruction encoding
+# CHECK-CORE: [[#@LINE-3]]:6: warning: invalid instruction encoding
diff --git a/llvm/test/MC/Xtensa/Options/code_density-invalid.s b/llvm/test/MC/Xtensa/code_density-invalid.s
similarity index 79%
rename from llvm/test/MC/Xtensa/Options/code_density-invalid.s
rename to llvm/test/MC/Xtensa/code_density-invalid.s
index 8d0c472e3f1da0..b5068cb8d57ab2 100644
--- a/llvm/test/MC/Xtensa/Options/code_density-invalid.s
+++ b/llvm/test/MC/Xtensa/code_density-invalid.s
@@ -8,9 +8,13 @@ LBL0:
 addi.n a2, a3, 20
 # CHECK: :[[#@LINE-1]]:16: error: expected immediate in range [-1, 15] except 0
 
+# imm1n_15
+addi.n a2, a3, 0
+# CHECK: :[[#@LINE-1]]:16: error: expected immediate in range [-1, 15] except 0
+
 # imm32n_95
 movi.n a2, 100
-# CHECK: :[[#@LINE-1]]:12: error: expected immediate in range [-32, 95] except 0
+# CHECK: :[[#@LINE-1]]:12: error: expected immediate in range [-32, 95]
 
 # Offset4m32
 l32i.n a2, a3, 100
diff --git a/llvm/test/MC/Xtensa/Options/code_density.s b/llvm/test/MC/Xtensa/code_density.s
similarity index 92%
rename from llvm/test/MC/Xtensa/Options/code_density.s
rename to llvm/test/MC/Xtensa/code_density.s
index 429b3d6a03daf8..fe9f7e91774487 100644
--- a/llvm/test/MC/Xtensa/Options/code_density.s
+++ b/llvm/test/MC/Xtensa/code_density.s
@@ -14,6 +14,11 @@ add.n a2, a3, a4
 # CHECK: encoding: [0x3b,0x23]
 addi.n a2, a3, 3
 
+# Instruction format RRRN
+# CHECK-INST: addi.n a2, a3, -1
+# CHECK: encoding: [0x0b,0x23]
+addi.n a2, a3, -1
+
 # Instruction format RI6
 # CHECK-INST:  beqz.n  a3, LBL1
 # CHECK: encoding: [0x8c'A',0x03'A']

>From 343fdac2995c785e9776ba192cd445efe5fb7596 Mon Sep 17 00:00:00 2001
From: Andrei Safronov <safronov at espressif.com>
Date: Fri, 13 Dec 2024 18:18:44 +0300
Subject: [PATCH 4/5] [Xtensa] Fix disassembler test.

---
 llvm/test/MC/Disassembler/Xtensa/lit.local.cfg | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 llvm/test/MC/Disassembler/Xtensa/lit.local.cfg

diff --git a/llvm/test/MC/Disassembler/Xtensa/lit.local.cfg b/llvm/test/MC/Disassembler/Xtensa/lit.local.cfg
new file mode 100644
index 00000000000000..e81bfa773f36a8
--- /dev/null
+++ b/llvm/test/MC/Disassembler/Xtensa/lit.local.cfg
@@ -0,0 +1,2 @@
+if not "Xtensa" in config.root.targets:
+    config.unsupported = True

>From c8c44285f06cddd3752e65a36b6b6ca07306d795 Mon Sep 17 00:00:00 2001
From: Andrei Safronov <safronov at espressif.com>
Date: Mon, 16 Dec 2024 22:18:41 +0300
Subject: [PATCH 5/5] [Xtensa] Minor fixes.

---
 llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp b/llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp
index 8bff0f6660b52d..c11c4b7038bdb7 100644
--- a/llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp
+++ b/llvm/lib/Target/Xtensa/Disassembler/XtensaDisassembler.cpp
@@ -283,7 +283,7 @@ static DecodeStatus readInstruction16(ArrayRef<uint8_t> Bytes, uint64_t Address,
   }
 
   if (!IsLittleEndian) {
-    llvm_unreachable("Big-endian mode currently is not supported!");
+    report_fatal_error("Big-endian mode currently is not supported!");
   } else {
     Insn = (Bytes[1] << 8) | Bytes[0];
   }



More information about the llvm-commits mailing list