[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