[llvm] [GlobalISel][AArch64] Implement `llvm.{get,set}.rounding` lowering (PR #210665)

Ömer Sinan Ağacan via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 20 03:38:38 PDT 2026


https://github.com/osa1 updated https://github.com/llvm/llvm-project/pull/210665

>From 93971d84ed8c1cbdc0e2d08dd7fb5a00e9c3ace1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= <omeragacan at gmail.com>
Date: Thu, 16 Jul 2026 08:55:04 +0100
Subject: [PATCH 1/2] [GlobalISel][AArch64] Implement `llvm.{get,set}.rounding`
 lowering

---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 10 +--
 .../AArch64/GISel/AArch64LegalizerInfo.cpp    | 68 +++++++++++++++++++
 .../AArch64/GISel/AArch64LegalizerInfo.h      |  6 ++
 .../GlobalISel/legalize-get-set-rounding.mir  | 40 +++++++++++
 4 files changed, 119 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/GlobalISel/legalize-get-set-rounding.mir

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index be92d0917459d..a134847cf540e 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -5880,9 +5880,9 @@ static bool isAddSubZExt(SDValue N, SelectionDAG &DAG) {
 
 SDValue AArch64TargetLowering::LowerGET_ROUNDING(SDValue Op,
                                                  SelectionDAG &DAG) const {
-  // The rounding mode is in bits 23:22 of the FPSCR.
+  // The rounding mode is in bits 23:22 of the FPCR.
   // The ARM rounding mode value to FLT_ROUNDS mapping is 0->1, 1->2, 2->3, 3->0
-  // The formula we use to implement this is (((FPSCR + 1 << 22) >> 22) & 3)
+  // The formula we use to implement this is (((FPCR + (1 << 22)) >> 22) & 3)
   // so that the shift + and get folded into a bitfield extract.
   SDLoc DL(Op);
 
@@ -5935,7 +5935,7 @@ SDValue AArch64TargetLowering::LowerSET_ROUNDING(SDValue Op,
   Chain = FPCR.getValue(1);
   FPCR = FPCR.getValue(0);
 
-  // Put new rounding mode into FPSCR[23:22].
+  // Put new rounding mode into FPCR[23:22].
   const int RMMask = ~(AArch64::Rounding::rmMask << AArch64::RoundingBitsPos);
   FPCR = DAG.getNode(ISD::AND, DL, MVT::i64, FPCR,
                      DAG.getConstant(RMMask, DL, MVT::i64));
@@ -5995,14 +5995,14 @@ SDValue AArch64TargetLowering::LowerRESET_FPMODE(SDValue Op,
   FPCR = FPCR.getValue(0);
 
   // Clear bits that are not reserved.
-  SDValue FPSCRMasked = DAG.getNode(
+  SDValue FPCRMasked = DAG.getNode(
       ISD::AND, DL, MVT::i64, FPCR,
       DAG.getConstant(AArch64::ReservedFPControlBits, DL, MVT::i64));
 
   // Set new value of FPCR.
   SDValue Ops2[] = {
       Chain, DAG.getTargetConstant(Intrinsic::aarch64_set_fpcr, DL, MVT::i64),
-      FPSCRMasked};
+      FPCRMasked};
   return DAG.getNode(ISD::INTRINSIC_VOID, DL, MVT::Other, Ops2);
 }
 
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
index 94ca171c0b207..448badfee98d0 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
+++ b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp
@@ -1526,6 +1526,9 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST)
                                G_GET_FPMODE, G_SET_FPMODE, G_RESET_FPMODE})
       .libcall();
 
+  getActionDefinitionsBuilder({G_GET_ROUNDING, G_SET_ROUNDING})
+      .customFor({s32});
+
   getActionDefinitionsBuilder(G_IS_FPCLASS).lower();
 
   getActionDefinitionsBuilder(G_PREFETCH).custom();
@@ -1599,6 +1602,10 @@ bool AArch64LegalizerInfo::legalizeCustom(
     // In order to lower f16 to f64 properly, we need to use f32 as an
     // intermediary
     return legalizeFptrunc(MI, MIRBuilder, MRI);
+  case TargetOpcode::G_GET_ROUNDING:
+    return legalizeGetRounding(MI, MIRBuilder, MRI, Helper);
+  case TargetOpcode::G_SET_ROUNDING:
+    return legalizeSetRounding(MI, MIRBuilder, MRI, Helper);
   }
 
   llvm_unreachable("expected switch to return");
@@ -2791,3 +2798,64 @@ bool AArch64LegalizerInfo::legalizeFptrunc(MachineInstr &MI,
   MI.eraseFromParent();
   return true;
 }
+
+bool AArch64LegalizerInfo::legalizeGetRounding(MachineInstr &MI,
+                                               MachineIRBuilder &MIRBuilder,
+                                               MachineRegisterInfo &MRI,
+                                               LegalizerHelper &Helper) const {
+  const LLT I32 = LLT::integer(32);
+  const LLT I64 = LLT::integer(64);
+
+  Register Dst = MI.getOperand(0).getReg();
+  Register FPCR64 = MRI.createGenericVirtualRegister(I64);
+  MachineInstrBuilder GetFPCR =
+      MIRBuilder.buildIntrinsic(Intrinsic::aarch64_get_fpcr, ArrayRef{FPCR64});
+
+  // `((FPCR >> 22) + 1) & 0b11`, implemented as bitfield extraction of
+  // `(FPCR + (1 << 22))` as this version generates one less instruction.
+  auto FPCR32 = MIRBuilder.buildTrunc(I32, GetFPCR);
+  auto One = MIRBuilder.buildConstant(I32, 1U << 22);
+  auto Added = MIRBuilder.buildAdd(I32, FPCR32, One);
+  auto LSB = MIRBuilder.buildConstant(I32, 22);
+  auto Width = MIRBuilder.buildConstant(I32, 2);
+  MIRBuilder.buildInstr(TargetOpcode::G_UBFX, {Dst}, {Added, LSB, Width});
+
+  MI.eraseFromParent();
+  return true;
+}
+
+bool AArch64LegalizerInfo::legalizeSetRounding(MachineInstr &MI,
+                                               MachineIRBuilder &MIRBuilder,
+                                               MachineRegisterInfo &MRI,
+                                               LegalizerHelper &Helper) const {
+  const LLT I32 = LLT::integer(32);
+  const LLT I64 = LLT::integer(64);
+
+  // Calculate new value of FPCR[23:22].
+  Register RM = MI.getOperand(0).getReg();
+  auto One = MIRBuilder.buildConstant(I32, 1);
+  auto Subtracted = MIRBuilder.buildSub(I32, RM, One);
+  auto Mask = MIRBuilder.buildConstant(I32, 0b11);
+  auto Masked = MIRBuilder.buildAnd(I32, Subtracted, Mask);
+  auto ShiftAmount = MIRBuilder.buildConstant(I32, 22);
+  auto Shifted = MIRBuilder.buildShl(I32, Masked, ShiftAmount);
+
+  // Get current value of FPCR.
+  Register FPCR64 = MRI.createGenericVirtualRegister(I64);
+  MachineInstrBuilder GetFPCR =
+      MIRBuilder.buildIntrinsic(Intrinsic::aarch64_get_fpcr, ArrayRef{FPCR64});
+
+  // (FPCR & ~Mask) | Shifted
+  auto FPCRMask = MIRBuilder.buildConstant(I64, ~((int64_t)0b11 << 22));
+  auto FPCRMasked = MIRBuilder.buildAnd(I64, GetFPCR, FPCRMask);
+  auto ShiftedS64 = MIRBuilder.buildZExt(I64, Shifted);
+  auto FPCRUpdated = MIRBuilder.buildOr(I64, FPCRMasked, ShiftedS64);
+
+  // Write new FPCR.
+  MIRBuilder.buildIntrinsic(Intrinsic::aarch64_set_fpcr, ArrayRef<Register>())
+      .addUse(FPCRUpdated.getReg(0));
+
+  MI.eraseFromParent();
+
+  return true;
+}
diff --git a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.h b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.h
index 535240a67a2cc..6999ae5880634 100644
--- a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.h
+++ b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.h
@@ -71,6 +71,12 @@ class AArch64LegalizerInfo : public LegalizerInfo {
                              MachineIRBuilder &MIRBuilder) const;
   bool legalizeFptrunc(MachineInstr &MI, MachineIRBuilder &MIRBuilder,
                        MachineRegisterInfo &MRI) const;
+  bool legalizeGetRounding(MachineInstr &MI, MachineIRBuilder &MIRBuilder,
+                           MachineRegisterInfo &MRI,
+                           LegalizerHelper &Helper) const;
+  bool legalizeSetRounding(MachineInstr &MI, MachineIRBuilder &MIRBuilder,
+                           MachineRegisterInfo &MRI,
+                           LegalizerHelper &Helper) const;
   const AArch64Subtarget *ST;
 };
 } // End llvm namespace.
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalize-get-set-rounding.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-get-set-rounding.mir
new file mode 100644
index 0000000000000..fdf650382f501
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-get-set-rounding.mir
@@ -0,0 +1,40 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 2
+# RUN: llc -mtriple=aarch64-linux-gnu -run-pass=legalizer %s -o - | FileCheck %s
+
+---
+name:            test_get_rounding
+body:             |
+  bb.1.entry:
+    ; CHECK-LABEL: name: test_get_rounding
+    ; CHECK: [[INT:%[0-9]+]]:_(i64) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.aarch64.get.fpcr)
+    ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i32) = G_TRUNC [[INT]](i64)
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 4194304
+    ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i32) = G_ADD [[TRUNC]], [[C]]
+    ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 22
+    ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 2
+    ; CHECK-NEXT: [[UBFX:%[0-9]+]]:_(i32) = G_UBFX [[ADD]], [[C1]](i32), [[C2]]
+    ; CHECK-NEXT: $w0 = COPY [[UBFX]](i32)
+    ; CHECK-NEXT: RET_ReallyLR implicit $w0
+    %0:_(i32) = G_GET_ROUNDING
+    $w0 = COPY %0:_(i32)
+    RET_ReallyLR implicit $w0
+
+...
+
+---
+name:            test_set_rounding
+body:             |
+  bb.1.entry:
+    ; CHECK-LABEL: name: test_set_rounding
+    ; CHECK: [[INT:%[0-9]+]]:_(i64) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.aarch64.get.fpcr)
+    ; CHECK-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 -12582913
+    ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i64) = G_AND [[INT]], [[C]]
+    ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i64) = G_CONSTANT i64 4194304
+    ; CHECK-NEXT: [[OR:%[0-9]+]]:_(i64) = G_OR [[AND]], [[C1]]
+    ; CHECK-NEXT: G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.aarch64.set.fpcr), [[OR]](i64)
+    ; CHECK-NEXT: RET_ReallyLR
+    %0:_(i32) = G_CONSTANT i32 2
+    G_SET_ROUNDING %0:_(i32)
+    RET_ReallyLR
+
+...

>From 65d37ab3f08bd3354b6c54e2f6e28b0901bc00b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= <omeragacan at gmail.com>
Date: Mon, 20 Jul 2026 11:38:03 +0100
Subject: [PATCH 2/2] Update tests

---
 .../GlobalISel/legalizer-info-validation.mir  | 13 +++++--
 llvm/test/CodeGen/AArch64/strict-fp-opt.ll    | 39 ++++++++++++-------
 2 files changed, 33 insertions(+), 19 deletions(-)

diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
index 26c0aaa3bb3c9..1753b1c6fc9a1 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir
@@ -1,3 +1,4 @@
+# NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
 # RUN: llc -mtriple=aarch64-- -run-pass=legalizer %s \
 # RUN:     -mcpu=cortex-a75 -o - 2>&1 | FileCheck %s --check-prefixes=CHECK
 
@@ -679,11 +680,12 @@
 # DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
 # DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
 # DEBUG-NEXT: G_GET_ROUNDING (opcode {{[0-9]+}}): 1 type index, 0 imm indices
-# DEBUG-NEXT:.. type index coverage check SKIPPED: no rules defined
-# DEBUG-NEXT:.. imm index coverage check SKIPPED: no rules defined
+# DEBUG-NEXT: .. the first uncovered type index: 1, OK
+# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
 # DEBUG-NEXT: G_SET_ROUNDING (opcode {{[0-9]+}}): 1 type index, 0 imm indices
-# DEBUG-NEXT:.. type index coverage check SKIPPED: no rules defined
-# DEBUG-NEXT:.. imm index coverage check SKIPPED: no rules defined
+# DEBUG-NEXT: .. opcode {{[0-9]+}} is aliased to {{[0-9]+}}
+# DEBUG-NEXT: .. the first uncovered type index: 1, OK
+# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
 # DEBUG-NEXT: G_PTR_ADD (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
 # DEBUG-NEXT: .. the first uncovered type index: 2, OK
 # DEBUG-NEXT: .. the first uncovered imm index: 0, OK
@@ -999,3 +1001,6 @@ name: dummy
 body: |
   bb.0:
 ...
+## NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+# CHECK: {{.*}}
+# DEBUG: {{.*}}
diff --git a/llvm/test/CodeGen/AArch64/strict-fp-opt.ll b/llvm/test/CodeGen/AArch64/strict-fp-opt.ll
index c433291ff576a..66714de5cc826 100644
--- a/llvm/test/CodeGen/AArch64/strict-fp-opt.ll
+++ b/llvm/test/CodeGen/AArch64/strict-fp-opt.ll
@@ -6,7 +6,6 @@
 ; CHECK-GI-NEXT:  warning: Instruction selection used fallback path for unused_div_round_dynamic
 ; CHECK-GI-NEXT:  warning: Instruction selection used fallback path for add_twice_fpexcept_strict
 ; CHECK-GI-NEXT:  warning: Instruction selection used fallback path for add_twice_round_dynamic
-; CHECK-GI-NEXT:  warning: Instruction selection used fallback path for set_rounding
 ; CHECK-GI-NEXT:  warning: Instruction selection used fallback path for set_rounding_fpexcept_strict
 ; CHECK-GI-NEXT:  warning: Instruction selection used fallback path for set_rounding_round_dynamic
 
@@ -121,17 +120,30 @@ if.end:
 ; (the llvm.set.rounding is irrelevant, but both could trap).
 
 define float @set_rounding(float %x, float %y) {
-; CHECK-LABEL: set_rounding:
-; CHECK:       // %bb.0: // %entry
-; CHECK-NEXT:    mrs x8, FPCR
-; CHECK-NEXT:    fadd s0, s0, s1
-; CHECK-NEXT:    orr x8, x8, #0xc00000
-; CHECK-NEXT:    msr FPCR, x8
-; CHECK-NEXT:    mrs x8, FPCR
-; CHECK-NEXT:    and x8, x8, #0xffffffffff3fffff
-; CHECK-NEXT:    msr FPCR, x8
-; CHECK-NEXT:    fsub s0, s0, s0
-; CHECK-NEXT:    ret
+; CHECK-SD-LABEL: set_rounding:
+; CHECK-SD:       // %bb.0: // %entry
+; CHECK-SD-NEXT:    mrs x8, FPCR
+; CHECK-SD-NEXT:    fadd s0, s0, s1
+; CHECK-SD-NEXT:    orr x8, x8, #0xc00000
+; CHECK-SD-NEXT:    msr FPCR, x8
+; CHECK-SD-NEXT:    mrs x8, FPCR
+; CHECK-SD-NEXT:    and x8, x8, #0xffffffffff3fffff
+; CHECK-SD-NEXT:    msr FPCR, x8
+; CHECK-SD-NEXT:    fsub s0, s0, s0
+; CHECK-SD-NEXT:    ret
+;
+; CHECK-GI-LABEL: set_rounding:
+; CHECK-GI:       // %bb.0: // %entry
+; CHECK-GI-NEXT:    mrs x8, FPCR
+; CHECK-GI-NEXT:    fadd s0, s0, s1
+; CHECK-GI-NEXT:    and x8, x8, #0xffffffffff3fffff
+; CHECK-GI-NEXT:    orr x8, x8, #0xc00000
+; CHECK-GI-NEXT:    msr FPCR, x8
+; CHECK-GI-NEXT:    mrs x8, FPCR
+; CHECK-GI-NEXT:    and x8, x8, #0xffffffffff3fffff
+; CHECK-GI-NEXT:    msr FPCR, x8
+; CHECK-GI-NEXT:    fsub s0, s0, s0
+; CHECK-GI-NEXT:    ret
 entry:
   %add1 = fadd float %x, %y
   call void @llvm.set.rounding(i32 0)
@@ -193,6 +205,3 @@ declare i32 @llvm.get.rounding()
 declare void @llvm.set.rounding(i32)
 
 attributes #0 = { strictfp }
-;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
-; CHECK-GI: {{.*}}
-; CHECK-SD: {{.*}}



More information about the llvm-commits mailing list