Skip to content

Conversation

@heiher
Copy link
Member

@heiher heiher commented Nov 7, 2025

@llvmbot
Copy link
Member

llvmbot commented Nov 7, 2025

@llvm/pr-subscribers-backend-loongarch

Author: hev (heiher)

Changes

Relevant GCC patch: https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=15c8169ec55b52630ab884dd7a30c3237ada98b2


Full diff: https://github.com/llvm/llvm-project/pull/166913.diff

4 Files Affected:

  • (modified) llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp (+24-1)
  • (modified) llvm/lib/Target/LoongArch/LoongArchISelLowering.h (+2)
  • (modified) llvm/lib/Target/LoongArch/LoongArchInstrInfo.td (+14-5)
  • (modified) llvm/test/CodeGen/LoongArch/trap.ll (+19-6)
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index cf4ffc82f6009..f3b725c731a95 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -74,6 +74,13 @@ static cl::opt<bool> ZeroDivCheck("loongarch-check-zero-division", cl::Hidden,
                                   cl::desc("Trap on integer division by zero."),
                                   cl::init(false));
 
+static cl::opt<int> TrapBreakCode("loongarch-trap-break-code", cl::init(-1),
+                                  cl::desc("Use 'break CODE' for traps "
+                                           "supposed to be unrecoverable, "
+                                           "or an 'amswap.w' instruction "
+                                           "leading to INE if CODE is out "
+                                           "of range."));
+
 LoongArchTargetLowering::LoongArchTargetLowering(const TargetMachine &TM,
                                                  const LoongArchSubtarget &STI)
     : TargetLowering(TM), Subtarget(STI) {
@@ -125,7 +132,7 @@ LoongArchTargetLowering::LoongArchTargetLowering(const TargetMachine &TM,
   setOperationAction({ISD::VAARG, ISD::VACOPY, ISD::VAEND}, MVT::Other, Expand);
 
   setOperationAction(ISD::DEBUGTRAP, MVT::Other, Legal);
-  setOperationAction(ISD::TRAP, MVT::Other, Legal);
+  setOperationAction(ISD::TRAP, MVT::Other, Custom);
 
   setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom);
   setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom);
@@ -610,10 +617,25 @@ SDValue LoongArchTargetLowering::LowerOperation(SDValue Op,
     return lowerVECREDUCE(Op, DAG);
   case ISD::ConstantFP:
     return lowerConstantFP(Op, DAG);
+  case ISD::TRAP:
+    return lowerTrap(Op, DAG);
   }
   return SDValue();
 }
 
+SDValue LoongArchTargetLowering::lowerTrap(SDValue Op,
+                                           SelectionDAG &DAG) const {
+  SDLoc DL(Op);
+  SDValue Chain = Op.getOperand(0);
+
+  if (isUInt<15>(TrapBreakCode))
+    return DAG.getNode(
+        LoongArchISD::BREAK, DL, MVT::Other, Chain,
+        DAG.getConstant(TrapBreakCode, DL, Subtarget.getGRLenVT()));
+
+  return DAG.getNode(LoongArchISD::UNIMP, DL, MVT::Other, Chain);
+}
+
 SDValue LoongArchTargetLowering::lowerConstantFP(SDValue Op,
                                                  SelectionDAG &DAG) const {
   EVT VT = Op.getValueType();
@@ -7500,6 +7522,7 @@ const char *LoongArchTargetLowering::getTargetNodeName(unsigned Opcode) const {
     NODE_NAME_CASE(TAIL)
     NODE_NAME_CASE(TAIL_MEDIUM)
     NODE_NAME_CASE(TAIL_LARGE)
+    NODE_NAME_CASE(UNIMP)
     NODE_NAME_CASE(SELECT_CC)
     NODE_NAME_CASE(BR_CC)
     NODE_NAME_CASE(BRCOND)
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
index 8a4d7748467c7..86682d4b9e9e6 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
@@ -33,6 +33,7 @@ enum NodeType : unsigned {
   TAIL,
   TAIL_MEDIUM,
   TAIL_LARGE,
+  UNIMP,
 
   // Select
   SELECT_CC,
@@ -415,6 +416,7 @@ class LoongArchTargetLowering : public TargetLowering {
   SDValue lowerVECREDUCE_ADD(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerVECREDUCE(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerConstantFP(SDValue Op, SelectionDAG &DAG) const;
+  SDValue lowerTrap(SDValue Op, SelectionDAG &DAG) const;
 
   bool isFPImmLegal(const APFloat &Imm, EVT VT,
                     bool ForCodeSize) const override;
diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
index 9565a55e4c6c5..72cda642d0546 100644
--- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
+++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
@@ -97,6 +97,8 @@ def loongarch_call_large : SDNode<"LoongArchISD::CALL_LARGE", SDT_LoongArchCall,
 def loongarch_tail_large : SDNode<"LoongArchISD::TAIL_LARGE", SDT_LoongArchCall,
                                   [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
                                    SDNPVariadic]>;
+def loongarch_unimp : SDNode<"LoongArchISD::UNIMP", SDTypeProfile<0, 0, []>,
+                             [SDNPHasChain, SDNPSideEffect]>;
 def loongarch_selectcc : SDNode<"LoongArchISD::SELECT_CC", SDT_LoongArchSelectCC>;
 def loongarch_brcc : SDNode<"LoongArchISD::BR_CC", SDT_LoongArchBrCC,
                             [SDNPHasChain]>;
@@ -1373,11 +1375,18 @@ 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)]>,
+// ISD::TRAP is lowered to either LoongArchISD::UNIMP or LoongArchISD::BREAK
+// depending on the --loongarch-trap-break-code=<N> option (default: -1).
+// - If N is a valid 15-bit unsigned integer (0 <= N <= 32767), it expands to
+//   LoongArchISD::BREAK N, producing a BREAK instruction with the specified
+//   code.
+// - Otherwise, it expands to LoongArchISD::UNIMP.
+
+// We lower `loongarch_unimp` 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), [(loongarch_unimp)]>,
                   PseudoInstExpansion<(AMSWAP_W R0, R1, R0)>;
 
 // We lower `debugtrap` to `break 0`, as this is guaranteed to exist and work,
diff --git a/llvm/test/CodeGen/LoongArch/trap.ll b/llvm/test/CodeGen/LoongArch/trap.ll
index 15a7ad82bd7a8..434807207f63d 100644
--- a/llvm/test/CodeGen/LoongArch/trap.ll
+++ b/llvm/test/CodeGen/LoongArch/trap.ll
@@ -1,6 +1,14 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc --mtriple=loongarch32 -mattr=+d --verify-machineinstrs < %s | FileCheck %s
-; RUN: llc --mtriple=loongarch64 -mattr=+d --verify-machineinstrs < %s | FileCheck %s
+; RUN: llc --mtriple=loongarch32 -mattr=+d --verify-machineinstrs < %s | \
+; RUN:   FileCheck %s --check-prefixes=CHECK,CHECK-AMSWAP
+; RUN: llc --mtriple=loongarch32 -mattr=+d --verify-machineinstrs \
+; RUN:   --loongarch-trap-break-code=1 < %s | \
+; RUN:   FileCheck %s --check-prefixes=CHECK,CHECK-BREAK
+; RUN: llc --mtriple=loongarch64 -mattr=+d --verify-machineinstrs < %s | \
+; RUN:   FileCheck %s --check-prefixes=CHECK,CHECK-AMSWAP
+; RUN: llc --mtriple=loongarch64 -mattr=+d --verify-machineinstrs \
+; RUN:   --loongarch-trap-break-code=1 < %s | \
+; RUN:   FileCheck %s --check-prefixes=CHECK,CHECK-BREAK
 
 ;; Verify that we lower @llvm.trap() and @llvm.debugtrap() correctly.
 
@@ -8,10 +16,15 @@ declare void @llvm.trap()
 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:    ret
+; CHECK-AMSWAP-LABEL: test_trap:
+; CHECK-AMSWAP:       # %bb.0:
+; CHECK-AMSWAP-NEXT:    amswap.w $zero, $ra, $zero
+; CHECK-AMSWAP-NEXT:    ret
+;
+; CHECK-BREAK-LABEL: test_trap:
+; CHECK-BREAK:       # %bb.0:
+; CHECK-BREAK-NEXT:    break 1
+; CHECK-BREAK-NEXT:    ret
   tail call void @llvm.trap()
   ret void
 }

@heiher
Copy link
Member Author

heiher commented Nov 7, 2025

cc @xry111 @xen0n

@heiher
Copy link
Member Author

heiher commented Nov 7, 2025

Should we make Clang support a GCC-style -mbreak-code= flag? This flag obviously doesn't work with rustc. For such a niche need, I suggest we have both Clang and rustc use: --llvm --loongarch-trap-break-code=<N>.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants