[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 01:50:34 PDT 2026


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

None

>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] [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
+
+...



More information about the llvm-commits mailing list