[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