[llvm] [LoongArch] Add support for the ud macro instruction (PR #171583)

via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 10 03:51:44 PST 2025


https://github.com/heiher updated https://github.com/llvm/llvm-project/pull/171583

>From 423c47acc8840b1350115fc889f941479e8f0694 Mon Sep 17 00:00:00 2001
From: WANG Rui <wangrui at loongson.cn>
Date: Wed, 10 Dec 2025 16:35:08 +0800
Subject: [PATCH 1/2] [LoongArch] Add support for the ud macro instruction

This patch adds support for the `ud ui5` macro instruction. The `ui5`
operand must be inthe range `0-31`. The macro expands to:

`amswap.w $rd, $r1, $rj`

where `ui5` specifies the register number used for `$rd` in the expanded
instruction, and `$rd` is the same as `$rj`.

Relevant binutils patch:

https://sourceware.org/pipermail/binutils/2025-December/146042.html
---
 .../Disassembler/LoongArchDisassembler.cpp    | 23 +++++++++++++++++++
 .../Target/LoongArch/LoongArchInstrFormats.td | 13 +++++++++++
 .../Target/LoongArch/LoongArchInstrInfo.td    |  9 +++++++-
 llvm/test/MC/LoongArch/Basic/Integer/misc.s   |  7 ++++++
 4 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/LoongArch/Disassembler/LoongArchDisassembler.cpp b/llvm/lib/Target/LoongArch/Disassembler/LoongArchDisassembler.cpp
index d4058fac4304a..6b356e2cae6ff 100644
--- a/llvm/lib/Target/LoongArch/Disassembler/LoongArchDisassembler.cpp
+++ b/llvm/lib/Target/LoongArch/Disassembler/LoongArchDisassembler.cpp
@@ -157,6 +157,29 @@ static DecodeStatus decodeSImmOperand(MCInst &Inst, uint64_t Imm,
   return MCDisassembler::Success;
 }
 
+// Decode AMOSWAP.W and UD, which share the same base encoding.
+// If rk == 1 and rd == rj, interpret the instruction as UD;
+// otherwise decode as AMSWAP.W.
+static DecodeStatus DecodeAMOrUDInstruction(MCInst &Inst, unsigned Insn,
+                                            uint64_t Address,
+                                            const MCDisassembler *Decoder) {
+  unsigned rd = fieldFromInstruction(Insn, 0, 5);
+  unsigned rj = fieldFromInstruction(Insn, 5, 5);
+  unsigned rk = fieldFromInstruction(Insn, 10, 5);
+
+  if (rk == 1 && rd == rj) {
+    Inst.setOpcode(LoongArch::UD);
+    Inst.addOperand(MCOperand::createImm(rd));
+  } else {
+    Inst.setOpcode(LoongArch::AMSWAP_W);
+    Inst.addOperand(MCOperand::createReg(LoongArch::R0 + rd));
+    Inst.addOperand(MCOperand::createReg(LoongArch::R0 + rk));
+    Inst.addOperand(MCOperand::createReg(LoongArch::R0 + rj));
+  }
+
+  return MCDisassembler::Success;
+}
+
 #include "LoongArchGenDisassemblerTables.inc"
 
 DecodeStatus LoongArchDisassembler::getInstruction(MCInst &MI, uint64_t &Size,
diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrFormats.td b/llvm/lib/Target/LoongArch/LoongArchInstrFormats.td
index 419e20431c59f..fa049fcbc2d21 100644
--- a/llvm/lib/Target/LoongArch/LoongArchInstrFormats.td
+++ b/llvm/lib/Target/LoongArch/LoongArchInstrFormats.td
@@ -401,3 +401,16 @@ class FmtLDPTE<dag outs, dag ins, string opnstr, list<dag> pattern = []>
   let Inst{9-5} = rj;
   let Inst{4-0} = 0b00000;
 }
+
+// FmtUD
+// <0b0011100001100000000001 | I5 | I5>
+class FmtUD<dag outs, dag ins, string opnstr, list<dag> pattern = []>
+    : LAInst<outs, ins, deriveInsnMnemonic<NAME>.ret, opnstr, pattern> {
+  bits<5> imm5;
+
+  let Inst{31-10} = 0b0011100001100000000001;
+  let Inst{9-5} = imm5;
+  let Inst{4-0} = imm5;
+
+  let DecoderMethod = "DecodeAMOrUDInstruction";
+}
diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
index 2e6653e1a09ac..2057d35906ba3 100644
--- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
+++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
@@ -800,6 +800,10 @@ class AM_3R<bits<32> op>
   let Constraints = "@earlyclobber $rd";
 }
 
+class AU_3R<bits<32> op> : AM_3R<op> {
+  let DecoderMethod = "DecodeAMOrUDInstruction";
+}
+
 class AMCAS_3R<bits<32> op>
     : Fmt3R<op, (outs GPR:$dst), (ins GPR:$rd, GPR:$rk, GPRMemAtomic:$rj),
             "$rd, $rk, $rj"> {
@@ -923,6 +927,9 @@ def BREAK   : MISC_I15<0x002a0000>;
 def RDTIMEL_W : RDTIME_2R<0x00006000>;
 def RDTIMEH_W : RDTIME_2R<0x00006400>;
 
+let hasSideEffects = 1, mayLoad = 0, mayStore = 0 in
+def UD : FmtUD<(outs), (ins uimm5:$imm5), "$imm5">;
+
 // The CPUCFG instruction offers a reliable way to probing CPU features.
 // Although support is not guaranteed on LA32R, having compiler support
 // nevertheless enables applications to rely on its presence, potentially
@@ -1087,7 +1094,7 @@ def STLE_D : STORE_3R<0x387f8000>;
 // Atomic Memory Access Instructions for 64-bits
 def AMSWAP_B     : AM_3R<0x385c0000>;
 def AMSWAP_H     : AM_3R<0x385c8000>;
-def AMSWAP_W     : AM_3R<0x38600000>;
+def AMSWAP_W     : AU_3R<0x38600000>;
 def AMSWAP_D     : AM_3R<0x38608000>;
 def AMADD_B      : AM_3R<0x385d0000>;
 def AMADD_H      : AM_3R<0x385d8000>;
diff --git a/llvm/test/MC/LoongArch/Basic/Integer/misc.s b/llvm/test/MC/LoongArch/Basic/Integer/misc.s
index 182d1da9b237e..6bed1cdf1ff4e 100644
--- a/llvm/test/MC/LoongArch/Basic/Integer/misc.s
+++ b/llvm/test/MC/LoongArch/Basic/Integer/misc.s
@@ -33,6 +33,13 @@ rdtimeh.w $a7, $a1
 # CHECK-ASM: encoding: [0x03,0x6d,0x00,0x00]
 cpucfg $sp, $a4
 
+# CHECK-ASM-AND-OBJ: ud 0
+# CHECK-ASM: encoding: [0x00,0x04,0x60,0x38]
+ud 0
+
+# CHECK-ASM-AND-OBJ: ud 31
+# CHECK-ASM: encoding: [0xff,0x07,0x60,0x38]
+ud 31
 
 #############################################################
 ## Instructions only for loongarch64

>From 360ea647473e71edd22ca9140982446d0bb1ed39 Mon Sep 17 00:00:00 2001
From: WANG Rui <wangrui at loongson.cn>
Date: Wed, 10 Dec 2025 19:48:28 +0800
Subject: [PATCH 2/2] Lower `trap` to `ud 0`

---
 llvm/lib/Target/LoongArch/LoongArchInstrInfo.td | 8 ++------
 llvm/test/CodeGen/LoongArch/trap.ll             | 2 +-
 2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
index 2057d35906ba3..d971f8bc1986b 100644
--- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
+++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
@@ -1417,12 +1417,8 @@ def : Pat<(and GPR:$rj, BstrinsImm:$imm),
 
 /// Traps
 
-// We lower `trap` to `amswap.w rd:$r0, rk:$r1, rj:$r0`, as this is guaranteed
-// to trap with an INE (non-existent on LA32, explicitly documented to INE on
-// LA64). And the resulting signal is different from `debugtrap` like on some
-// other existing ports so programs/porters might have an easier time.
-def PseudoUNIMP : Pseudo<(outs), (ins), [(trap)]>,
-                  PseudoInstExpansion<(AMSWAP_W R0, R1, R0)>;
+// We lower `trap` to `ud 0`, which is an alias for `amswap.w $r0, $r1, $r0`.
+def PseudoUNIMP : Pseudo<(outs), (ins), [(trap)]>, PseudoInstExpansion<(UD 0)>;
 
 // We lower `debugtrap` to `break 0`, as this is guaranteed to exist and work,
 // even for LA32 Primary. Also, because so far the ISA does not provide a
diff --git a/llvm/test/CodeGen/LoongArch/trap.ll b/llvm/test/CodeGen/LoongArch/trap.ll
index 15a7ad82bd7a8..d433266b47e47 100644
--- a/llvm/test/CodeGen/LoongArch/trap.ll
+++ b/llvm/test/CodeGen/LoongArch/trap.ll
@@ -10,7 +10,7 @@ declare void @llvm.debugtrap()
 define void @test_trap() nounwind {
 ; CHECK-LABEL: test_trap:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    amswap.w $zero, $ra, $zero
+; CHECK-NEXT:    ud 0
 ; CHECK-NEXT:    ret
   tail call void @llvm.trap()
   ret void



More information about the llvm-commits mailing list