[llvm] [X86][APX] Fix assembly printing for NF+ND shift-by-1 instructions (PR #181565)

via llvm-commits llvm-commits at lists.llvm.org
Sun Feb 15 11:26:07 PST 2026


https://github.com/moleium created https://github.com/llvm/llvm-project/pull/181565

Clang with `-mapxf -no-integrated-as` generated shift-by-1 NF+ND instructions 
without the explicit $1 immediate which causes binutils to reject the assembly 
with "operand type mismatch".

Added custom printer in `X86ATTInstPrinter.cpp` to emit the `$1` immediate for 
these instructions. Removed `DisassembleOnly` markers from `*r1_NF_ND` variants 
in `X86InstrShiftRotate.td`.

Fixes #181553

>From 4e8367df204911bf2261e9e3d992ba84c548b0f9 Mon Sep 17 00:00:00 2001
From: moleium <molenoch at protonmail.com>
Date: Sun, 15 Feb 2026 22:09:43 +0300
Subject: [PATCH] [X86][APX] Fix assembly printing for NF+ND shift-by-1
 instructions

---
 .../X86/MCTargetDesc/X86ATTInstPrinter.cpp    | 86 +++++++++++++++++++
 .../X86/MCTargetDesc/X86ATTInstPrinter.h      |  2 +
 llvm/lib/Target/X86/X86InstrShiftRotate.td    |  4 +-
 3 files changed, 90 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86ATTInstPrinter.cpp b/llvm/lib/Target/X86/MCTargetDesc/X86ATTInstPrinter.cpp
index 564636959f00f..d40c803307be0 100644
--- a/llvm/lib/Target/X86/MCTargetDesc/X86ATTInstPrinter.cpp
+++ b/llvm/lib/Target/X86/MCTargetDesc/X86ATTInstPrinter.cpp
@@ -402,6 +402,92 @@ bool X86ATTInstPrinter::printVecCompareInstr(const MCInst *MI,
   return false;
 }
 
+bool X86ATTInstPrinter::printShiftBy1NFNDInstr(const MCInst *MI,
+                                               uint64_t Address,
+                                               raw_ostream &OS) {
+  // Emit explicit $1 for shift-by-1 NF+ND to match binutils behavior.
+  const char *Mnemonic = nullptr;
+  const char *Suffix = nullptr;
+
+  switch (MI->getOpcode()) {
+  default:
+    return false;
+
+  case X86::SHL16r1_NF_ND:
+    Mnemonic = "shl";
+    Suffix = "w";
+    break;
+  case X86::SHL32r1_NF_ND:
+    Mnemonic = "shl";
+    Suffix = "l";
+    break;
+  case X86::SHL64r1_NF_ND:
+    Mnemonic = "shl";
+    Suffix = "q";
+    break;
+
+  case X86::SHR16r1_NF_ND:
+    Mnemonic = "shr";
+    Suffix = "w";
+    break;
+  case X86::SHR32r1_NF_ND:
+    Mnemonic = "shr";
+    Suffix = "l";
+    break;
+  case X86::SHR64r1_NF_ND:
+    Mnemonic = "shr";
+    Suffix = "q";
+    break;
+
+  case X86::SAR16r1_NF_ND:
+    Mnemonic = "sar";
+    Suffix = "w";
+    break;
+  case X86::SAR32r1_NF_ND:
+    Mnemonic = "sar";
+    Suffix = "l";
+    break;
+  case X86::SAR64r1_NF_ND:
+    Mnemonic = "sar";
+    Suffix = "q";
+    break;
+
+  case X86::ROL16r1_NF_ND:
+    Mnemonic = "rol";
+    Suffix = "w";
+    break;
+  case X86::ROL32r1_NF_ND:
+    Mnemonic = "rol";
+    Suffix = "l";
+    break;
+  case X86::ROL64r1_NF_ND:
+    Mnemonic = "rol";
+    Suffix = "q";
+    break;
+
+  case X86::ROR16r1_NF_ND:
+    Mnemonic = "ror";
+    Suffix = "w";
+    break;
+  case X86::ROR32r1_NF_ND:
+    Mnemonic = "ror";
+    Suffix = "l";
+    break;
+  case X86::ROR64r1_NF_ND:
+    Mnemonic = "ror";
+    Suffix = "q";
+    break;
+  }
+
+  // NDD operands are stored as (dst, src).
+  OS << '\t' << Mnemonic << Suffix << "\t$1, ";
+  printOperand(MI, 1, OS);
+  OS << ", ";
+  printOperand(MI, 0, OS);
+
+  return true;
+}
+
 void X86ATTInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
                                      raw_ostream &O) {
   const MCOperand &Op = MI->getOperand(OpNo);
diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86ATTInstPrinter.h b/llvm/lib/Target/X86/MCTargetDesc/X86ATTInstPrinter.h
index 1452622ebcea8..9179f0704cc8f 100644
--- a/llvm/lib/Target/X86/MCTargetDesc/X86ATTInstPrinter.h
+++ b/llvm/lib/Target/X86/MCTargetDesc/X86ATTInstPrinter.h
@@ -28,6 +28,8 @@ class X86ATTInstPrinter final : public X86InstPrinterCommon {
   void printInst(const MCInst *MI, uint64_t Address, StringRef Annot,
                  const MCSubtargetInfo &STI, raw_ostream &OS) override;
   bool printVecCompareInstr(const MCInst *MI, raw_ostream &OS);
+  bool printShiftBy1NFNDInstr(const MCInst *MI, uint64_t Address,
+                              raw_ostream &OS);
 
   // Autogenerated by tblgen, returns true if we successfully printed an
   // alias.
diff --git a/llvm/lib/Target/X86/X86InstrShiftRotate.td b/llvm/lib/Target/X86/X86InstrShiftRotate.td
index 2a5488847e648..9775e23a929d7 100644
--- a/llvm/lib/Target/X86/X86InstrShiftRotate.td
+++ b/llvm/lib/Target/X86/X86InstrShiftRotate.td
@@ -70,7 +70,7 @@ multiclass ShiftRotate<string m, Format RegMRM, Format MemMRM, SDPatternOperator
       // GNU binutils distinguish them by adding an explicit $1 to asm string of 8r1_ND. But we haven't support
       // constant immediate in asm string for X86 in TD. So we add DisassembleOnly for 8r1_ND for the time being.
       let Predicates = [In64BitMode] in {
-        def 8r1_ND  : UnaryOpR_RF<0xD1, RegMRM, m, Xi8, null_frag, 1>, DisassembleOnly;
+        def 8r1_ND  : UnaryOpR_RF<0xD1, RegMRM, m, Xi8, null_frag, 1>;
         def 16r1_ND : UnaryOpR_RF<0xD1, RegMRM, m, Xi16, null_frag, 1>, PD;
         def 32r1_ND : UnaryOpR_RF<0xD1, RegMRM, m, Xi32, null_frag, 1>;
         def 64r1_ND : UnaryOpR_RF<0xD1, RegMRM, m, Xi64, null_frag, 1>;
@@ -181,7 +181,7 @@ multiclass ShiftRotate_NF<string m, Format RegMRM, Format MemMRM, SchedReadWrite
       def 32r1_NF : UnaryOpR_R<0xD1, RegMRM, m, Xi32>, NF;
       def 64r1_NF : UnaryOpR_R<0xD1, RegMRM, m, Xi64>, NF;
 
-      def 8r1_NF_ND  : UnaryOpR_R<0xD1, RegMRM, m, Xi8, null_frag, 1>, EVEX_NF, DisassembleOnly;
+      def 8r1_NF_ND  : UnaryOpR_R<0xD1, RegMRM, m, Xi8, null_frag, 1>, EVEX_NF;
       def 16r1_NF_ND : UnaryOpR_R<0xD1, RegMRM, m, Xi16, null_frag, 1>, EVEX_NF, PD;
       def 32r1_NF_ND : UnaryOpR_R<0xD1, RegMRM, m, Xi32, null_frag, 1>, EVEX_NF;
       def 64r1_NF_ND : UnaryOpR_R<0xD1, RegMRM, m, Xi64, null_frag, 1>, EVEX_NF;



More information about the llvm-commits mailing list