[llvm] [LoongArch] Support ISD::SET_ROUNDING (llvm.set.rounding) (PR #206395)

Zhaoxin Yang via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 30 01:31:11 PDT 2026


https://github.com/ylzsx updated https://github.com/llvm/llvm-project/pull/206395

>From e52d842b186bebc3b7af572276883bf44b7273a8 Mon Sep 17 00:00:00 2001
From: yangzhaoxin <yangzhaoxin at loongson.cn>
Date: Mon, 29 Jun 2026 10:38:03 +0800
Subject: [PATCH 1/2] [LoongArch] Support ISD::SET_ROUNDING (llvm.set.rounding)

Fix https://github.com/llvm/llvm-project/issues/205039.

The LoongArch FCSR RM field supports four rounding modes, whose
encodings differ from LLVM's rounding mode values:
  FCSR: 0=RNE, 1=RZ,  2=RP,  3=RM
  LLVM: 0=RTZ, 1=RNE, 2=RUP, 3=RDN

For LLVM rounding mode 4, round to nearest with ties away from zero,
diagnose an error when it is a constant.

Co-authored-by: tangyuan0821 <tangyuan0821 at email.cn>
---
 .../LoongArch/LoongArchISelLowering.cpp       | 60 +++++++++++++++
 .../Target/LoongArch/LoongArchISelLowering.h  |  1 +
 .../LoongArch/isel-set-invalid-rounding.ll    | 31 ++++++++
 llvm/test/CodeGen/LoongArch/set-rounding.ll   | 75 +++++++++++++++++++
 4 files changed, 167 insertions(+)
 create mode 100644 llvm/test/CodeGen/LoongArch/isel-set-invalid-rounding.ll
 create mode 100644 llvm/test/CodeGen/LoongArch/set-rounding.ll

diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index 61e77c35fef9d..0202832bf28e0 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -244,6 +244,7 @@ LoongArchTargetLowering::LoongArchTargetLowering(const TargetMachine &TM,
     setOperationAction(ISD::BF16_TO_FP, MVT::f32, Custom);
     setOperationAction(ISD::FP_TO_BF16, MVT::f32,
                        Subtarget.isSoftFPABI() ? LibCall : Custom);
+    setOperationAction(ISD::SET_ROUNDING, MVT::Other, Custom);
 
     if (Subtarget.is64Bit())
       setOperationAction(ISD::FRINT, MVT::f32, Legal);
@@ -613,6 +614,8 @@ SDValue LoongArchTargetLowering::LowerOperation(SDValue Op,
     return lowerFRAMEADDR(Op, DAG);
   case ISD::RETURNADDR:
     return lowerRETURNADDR(Op, DAG);
+  case ISD::SET_ROUNDING:
+    return lowerSET_ROUNDING(Op, DAG);
   case ISD::WRITE_REGISTER:
     return lowerWRITE_REGISTER(Op, DAG);
   case ISD::INSERT_VECTOR_ELT:
@@ -4015,6 +4018,63 @@ SDValue LoongArchTargetLowering::lowerATOMIC_FENCE(SDValue Op,
   return Op;
 }
 
+SDValue LoongArchTargetLowering::lowerSET_ROUNDING(SDValue Op,
+                                                   SelectionDAG &DAG) const {
+  MVT GRLenVT = Subtarget.getGRLenVT();
+  SDLoc DL(Op);
+  SDValue Chain = Op.getOperand(0);
+  SDValue RMValue = Op.getOperand(1);
+
+  if (auto *CVal = dyn_cast<ConstantSDNode>(RMValue)) {
+    uint64_t RM = CVal->getZExtValue();
+    if (RM > 3) {
+      MachineFunction &MF = DAG.getMachineFunction();
+      LLVMContext &C = MF.getFunction().getContext();
+      C.diagnose(DiagnosticInfoUnsupported(
+          MF.getFunction(),
+          "rounding mode is not supported by LoongArch hardware",
+          DiagnosticLocation(DL.getDebugLoc()), DS_Error));
+      return Chain;
+    }
+  }
+
+  RMValue = DAG.getNode(ISD::ZERO_EXTEND, DL, GRLenVT, RMValue);
+
+  // LLVM rounding mode encoding differs from LoongArch FCSR encoding:
+  //   LLVM: 0=RTZ, 1=RNE, 2=RUP, 3=RDN
+  //   FCSR: 0=RNE, 1=RZ,  2=RP,  3=RN
+  //
+  // Need to convert argument into bits of control word:
+  //    0 Round to 0       -> 01
+  //    1 Round to nearest -> 00
+  //    2 Round to +inf    -> 10
+  //    3 Round to -inf    -> 11
+  //
+  // Transformation: RM ^ (~(RM >> 1) & 1)
+  SDValue ShiftRight1 = DAG.getNode(ISD::SRL, DL, GRLenVT, RMValue,
+                                    DAG.getConstant(1, DL, GRLenVT));
+  SDValue SwapMask = DAG.getNode(ISD::AND, DL, GRLenVT,
+                                 DAG.getNode(ISD::XOR, DL, GRLenVT, ShiftRight1,
+                                             DAG.getConstant(1, DL, GRLenVT)),
+                                 DAG.getConstant(1, DL, GRLenVT));
+  RMValue = DAG.getNode(ISD::XOR, DL, GRLenVT, RMValue, SwapMask);
+
+  RMValue = DAG.getNode(ISD::AND, DL, GRLenVT, RMValue,
+                        DAG.getConstant(0x3, DL, GRLenVT));
+
+  // The RM field in FCSR is at bits [9:8]. Shift the rounding mode value
+  // into position before writing via WRFCSR.
+  RMValue = DAG.getNode(ISD::SHL, DL, GRLenVT, RMValue,
+                        DAG.getConstant(8, DL, GRLenVT));
+
+  // FCSR3 is an alias of the RM field; writing it avoids clobbering
+  // unrelated fields in FCSR0.
+  SDValue FCSRNo = DAG.getTargetConstant(3, DL, GRLenVT);
+  MachineSDNode *RN = DAG.getMachineNode(LoongArch::WRFCSR, DL, MVT::Other,
+                                         FCSRNo, RMValue, Chain);
+  return SDValue(RN, 0);
+}
+
 SDValue LoongArchTargetLowering::lowerWRITE_REGISTER(SDValue Op,
                                                      SelectionDAG &DAG) const {
 
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
index 2f2eda1e2c7d9..8d9ec6020478b 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
@@ -236,6 +236,7 @@ class LoongArchTargetLowering : public TargetLowering {
   SDValue lowerINTRINSIC_VOID(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const;
+  SDValue lowerSET_ROUNDING(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerWRITE_REGISTER(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerEXTRACT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerINSERT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) const;
diff --git a/llvm/test/CodeGen/LoongArch/isel-set-invalid-rounding.ll b/llvm/test/CodeGen/LoongArch/isel-set-invalid-rounding.ll
new file mode 100644
index 0000000000000..c51fa4ae499f7
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/isel-set-invalid-rounding.ll
@@ -0,0 +1,31 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: not llc -mtriple=loongarch64 < %s 2>&1 | FileCheck %s --check-prefix=ERROR
+
+; ERROR: error: isel-set-invalid-rounding:3:3: in function foo void (): rounding mode is not supported by LoongArch hardware
+
+define void @foo() !dbg !9 {
+entry:
+  tail call void @llvm.set.rounding(i32 4), !dbg !12
+  ret void, !dbg !13
+}
+
+declare void @llvm.set.rounding(i32)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7}
+!llvm.ident = !{!8}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "isel-set-invalid-rounding", directory: "/tmp")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 8, !"PIC Level", i32 2}
+!6 = !{i32 7, !"PIE Level", i32 2}
+!7 = !{i32 7, !"uwtable", i32 2}
+!8 = !{!"clang"}
+!9 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !10, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, keyInstructions: true)
+!10 = !DISubroutineType(types: !11)
+!11 = !{null}
+!12 = !DILocation(line: 3, column: 3, scope: !9)
+!13 = !DILocation(line: 4, column: 1, scope: !9, atomGroup: 1, atomRank: 1)
diff --git a/llvm/test/CodeGen/LoongArch/set-rounding.ll b/llvm/test/CodeGen/LoongArch/set-rounding.ll
new file mode 100644
index 0000000000000..bdbcf461c382b
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/set-rounding.ll
@@ -0,0 +1,75 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc --mtriple=loongarch32 -mattr=+f < %s | FileCheck %s --check-prefixes=CHECK,LA32
+; RUN: llc --mtriple=loongarch64 -mattr=+f < %s | FileCheck %s --check-prefixes=CHECK,LA64
+
+;; LLVM rounding mode encoding: 0=RTZ, 1=RNE, 2=RUP, 3=RDN.
+;; LoongArch FCSR encoding:     0=RNE, 1=RZ,  2=RP,  3=RN.
+;; Translation: swap 0 <--> 1, keep 2 and 3.
+;; The RM field in FCSR is at bits [9:8], so the value is shifted left by 8.
+
+define void @set_rounding_rne() nounwind {
+; CHECK-LABEL: set_rounding_rne:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    movgr2fcsr $fcsr3, $zero
+; CHECK-NEXT:    ret
+  tail call void @llvm.set.rounding(i32 1)
+  ret void
+}
+
+define void @set_rounding_rtz() nounwind {
+; CHECK-LABEL: set_rounding_rtz:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    ori $a0, $zero, 256
+; CHECK-NEXT:    movgr2fcsr $fcsr3, $a0
+; CHECK-NEXT:    ret
+  tail call void @llvm.set.rounding(i32 0)
+  ret void
+}
+
+define void @set_rounding_rup() nounwind {
+; CHECK-LABEL: set_rounding_rup:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    ori $a0, $zero, 512
+; CHECK-NEXT:    movgr2fcsr $fcsr3, $a0
+; CHECK-NEXT:    ret
+  tail call void @llvm.set.rounding(i32 2)
+  ret void
+}
+
+define void @set_rounding_rdn() nounwind {
+; CHECK-LABEL: set_rounding_rdn:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    ori $a0, $zero, 768
+; CHECK-NEXT:    movgr2fcsr $fcsr3, $a0
+; CHECK-NEXT:    ret
+  tail call void @llvm.set.rounding(i32 3)
+  ret void
+}
+
+;; Test dynamic rounding mode argument.
+define void @set_rounding_dynamic(i32 %rm) nounwind {
+; LA32-LABEL: set_rounding_dynamic:
+; LA32:       # %bb.0:
+; LA32-NEXT:    nor $a1, $a0, $zero
+; LA32-NEXT:    srli.w $a1, $a1, 1
+; LA32-NEXT:    andi $a1, $a1, 1
+; LA32-NEXT:    xor $a0, $a0, $a1
+; LA32-NEXT:    andi $a0, $a0, 3
+; LA32-NEXT:    slli.w $a0, $a0, 8
+; LA32-NEXT:    movgr2fcsr $fcsr3, $a0
+; LA32-NEXT:    ret
+;
+; LA64-LABEL: set_rounding_dynamic:
+; LA64:       # %bb.0:
+; LA64-NEXT:    nor $a1, $a0, $zero
+; LA64-NEXT:    bstrpick.d $a1, $a1, 1, 1
+; LA64-NEXT:    xor $a0, $a0, $a1
+; LA64-NEXT:    andi $a0, $a0, 3
+; LA64-NEXT:    slli.d $a0, $a0, 8
+; LA64-NEXT:    movgr2fcsr $fcsr3, $a0
+; LA64-NEXT:    ret
+  tail call void @llvm.set.rounding(i32 %rm)
+  ret void
+}
+
+declare void @llvm.set.rounding(i32)

>From 9b7c2208e855bab1670a6132ac04004e4ccc77d1 Mon Sep 17 00:00:00 2001
From: yangzhaoxin <yangzhaoxin at loongson.cn>
Date: Tue, 30 Jun 2026 16:30:52 +0800
Subject: [PATCH 2/2] fixes accroding to review

---
 .../LoongArch/isel-set-invalid-rounding.ll    | 29 ++++---------------
 1 file changed, 5 insertions(+), 24 deletions(-)

diff --git a/llvm/test/CodeGen/LoongArch/isel-set-invalid-rounding.ll b/llvm/test/CodeGen/LoongArch/isel-set-invalid-rounding.ll
index c51fa4ae499f7..a0f769017a440 100644
--- a/llvm/test/CodeGen/LoongArch/isel-set-invalid-rounding.ll
+++ b/llvm/test/CodeGen/LoongArch/isel-set-invalid-rounding.ll
@@ -1,31 +1,12 @@
-; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
 ; RUN: not llc -mtriple=loongarch64 < %s 2>&1 | FileCheck %s --check-prefix=ERROR
+; RUN: not llc -mtriple=loongarch64 -mattr=+f < %s 2>&1 | FileCheck %s --check-prefix=ERROR
 
-; ERROR: error: isel-set-invalid-rounding:3:3: in function foo void (): rounding mode is not supported by LoongArch hardware
+; ERROR: in function foo void (): rounding mode is not supported by LoongArch hardware
 
-define void @foo() !dbg !9 {
+define void @foo() {
 entry:
-  tail call void @llvm.set.rounding(i32 4), !dbg !12
-  ret void, !dbg !13
+  tail call void @llvm.set.rounding(i32 4)
+  ret void
 }
 
 declare void @llvm.set.rounding(i32)
-
-!llvm.dbg.cu = !{!0}
-!llvm.module.flags = !{!2, !3, !4, !5, !6, !7}
-!llvm.ident = !{!8}
-
-!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
-!1 = !DIFile(filename: "isel-set-invalid-rounding", directory: "/tmp")
-!2 = !{i32 7, !"Dwarf Version", i32 5}
-!3 = !{i32 2, !"Debug Info Version", i32 3}
-!4 = !{i32 1, !"wchar_size", i32 4}
-!5 = !{i32 8, !"PIC Level", i32 2}
-!6 = !{i32 7, !"PIE Level", i32 2}
-!7 = !{i32 7, !"uwtable", i32 2}
-!8 = !{!"clang"}
-!9 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 2, type: !10, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, keyInstructions: true)
-!10 = !DISubroutineType(types: !11)
-!11 = !{null}
-!12 = !DILocation(line: 3, column: 3, scope: !9)
-!13 = !DILocation(line: 4, column: 1, scope: !9, atomGroup: 1, atomRank: 1)



More information about the llvm-commits mailing list