[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