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

via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 30 18:32:06 PDT 2026


Author: Zhaoxin Yang
Date: 2026-07-01T09:32:01+08:00
New Revision: e2fc1e7467e0b4c124aa6aa1422b1fed3afd1907

URL: https://github.com/llvm/llvm-project/commit/e2fc1e7467e0b4c124aa6aa1422b1fed3afd1907
DIFF: https://github.com/llvm/llvm-project/commit/e2fc1e7467e0b4c124aa6aa1422b1fed3afd1907.diff

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

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>

Added: 
    llvm/test/CodeGen/LoongArch/isel-set-invalid-rounding.ll
    llvm/test/CodeGen/LoongArch/set-rounding.ll

Modified: 
    llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
    llvm/lib/Target/LoongArch/LoongArchISelLowering.h

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index 6e9922ee9cf1b..187f1791661a3 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:
@@ -4019,6 +4022,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 
diff ers 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 189ecbe4820d2..96dc3df7f7a27 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h
@@ -235,6 +235,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..a0f769017a440
--- /dev/null
+++ b/llvm/test/CodeGen/LoongArch/isel-set-invalid-rounding.ll
@@ -0,0 +1,12 @@
+; 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: in function foo void (): rounding mode is not supported by LoongArch hardware
+
+define void @foo() {
+entry:
+  tail call void @llvm.set.rounding(i32 4)
+  ret void
+}
+
+declare void @llvm.set.rounding(i32)

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)


        


More information about the llvm-commits mailing list