Skip to content

[AArch64] missed fold vector fptoui(fdiv(uitofp(x),uitofp(y))) or fptosi(fdiv(sitofp(x),sitofp(y))) into udiv(x,y) or sdiv(x,y) for sve - #215146

Open
dnmohanty wants to merge 3 commits into
llvm:mainfrom
dnmohanty:aarch64-sve-fdiv-fold-clean
Open

[AArch64] missed fold vector fptoui(fdiv(uitofp(x),uitofp(y))) or fptosi(fdiv(sitofp(x),sitofp(y))) into udiv(x,y) or sdiv(x,y) for sve#215146
dnmohanty wants to merge 3 commits into
llvm:mainfrom
dnmohanty:aarch64-sve-fdiv-fold-clean

Conversation

@dnmohanty

Copy link
Copy Markdown
Contributor

This PR implements a DAG combine in AArch64ISelLowering.cpp to fold vector floating-point division and conversions into native SVE integer division (udiv/sdiv).

DAG Combine Logic: Added performFPToIntToDivCombine supporting ISD::FP_TO_UINT and ISD::FP_TO_SINT with strict type checks ensuring safe conversions (i32vectors viaf64). Tests: Added llvm/test/CodeGen/AArch64/sve-fdiv-int-fold.ll` to verify optimal single-instruction codegen.
Commit History: Structured into two clean commits as requested:

  1. Pre-commit baseline test (NFC)
  2. Fixup commit with compiler changes and updated test checks

Fixes #214927

@llvmorg-github-actions

Copy link
Copy Markdown

@llvm/pr-subscribers-backend-aarch64

Author: Durgesh Nandan Mohanty (dnmohanty)

Changes

This PR implements a DAG combine in AArch64ISelLowering.cpp to fold vector floating-point division and conversions into native SVE integer division (udiv/sdiv).

DAG Combine Logic: Added performFPToIntToDivCombine supporting ISD::FP_TO_UINT and ISD::FP_TO_SINT with strict type checks ensuring safe conversions (i32vectors viaf64). Tests: Added llvm/test/CodeGen/AArch64/sve-fdiv-int-fold.ll` to verify optimal single-instruction codegen.
Commit History: Structured into two clean commits as requested:

  1. Pre-commit baseline test (NFC)
  2. Fixup commit with compiler changes and updated test checks

Fixes #214927


Full diff: https://github.com/llvm/llvm-project/pull/215146.diff

2 Files Affected:

  • (modified) llvm/lib/Target/AArch64/AArch64ISelLowering.cpp (+32)
  • (added) llvm/test/CodeGen/AArch64/sve-fdiv-int-fold.ll (+34)
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 2ac6c5fbc471a..2bde72a6a9593 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -31167,7 +31167,36 @@ static SDValue performPredicateLoadCombine(SDNode *N,
   DAG.makeEquivalentMemoryOrdering(Load, LoadPred);
   return LoadPred;
 }
+static SDValue performFPToIntToDivCombine(SDNode *N, SelectionDAG &DAG) {
+  unsigned Opc = N->getOpcode();
+  bool IsSigned = (Opc == ISD::FP_TO_SINT);
+
+  SDValue FDiv = N->getOperand(0);
+  if (FDiv.getOpcode() != ISD::FDIV)
+    return SDValue();
+
+  EVT IntVT = N->getValueType(0);
+  EVT FPVT = FDiv.getValueType();
+
+  if (IntVT.getVectorElementType() != MVT::i32 ||
+      FPVT.getVectorElementType() != MVT::f64)
+    return SDValue();
+
+  unsigned CastOpc = IsSigned ? ISD::SINT_TO_FP : ISD::UINT_TO_FP;
+  SDValue Op0 = FDiv.getOperand(0);
+  SDValue Op1 = FDiv.getOperand(1);
 
+  if (Op0.getOpcode() != CastOpc || Op1.getOpcode() != CastOpc)
+    return SDValue();
+
+  if (Op0.getOperand(0).getValueType() != IntVT ||
+      Op1.getOperand(0).getValueType() != IntVT)
+    return SDValue();
+
+  unsigned DivOpc = IsSigned ? ISD::SDIV : ISD::UDIV;
+  return DAG.getNode(DivOpc, SDLoc(N), IntVT, Op0.getOperand(0),
+                     Op1.getOperand(0));
+}
 SDValue AArch64TargetLowering::PerformDAGCombine(SDNode *N,
                                                  DAGCombinerInfo &DCI) const {
   SelectionDAG &DAG = DCI.DAG;
@@ -31175,6 +31204,9 @@ SDValue AArch64TargetLowering::PerformDAGCombine(SDNode *N,
   default:
     LLVM_DEBUG(dbgs() << "Custom combining: skipping\n");
     break;
+  case ISD::FP_TO_UINT:
+  case ISD::FP_TO_SINT:
+    return performFPToIntToDivCombine(N, DAG);
   case ISD::VECTOR_DEINTERLEAVE:
     return performVectorDeinterleaveCombine(N, DCI, DAG);
   case ISD::VECREDUCE_AND:
diff --git a/llvm/test/CodeGen/AArch64/sve-fdiv-int-fold.ll b/llvm/test/CodeGen/AArch64/sve-fdiv-int-fold.ll
new file mode 100644
index 0000000000000..953187c7208d5
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/sve-fdiv-int-fold.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=aarch64-linux-gnu -mattr=+sve < %s | FileCheck %s
+
+define <vscale x 2 x i32> @fptoui_fdiv_uitofp_nxv2i32(<vscale x 2 x i32> %a, <vscale x 2 x i32> %b) {
+; CHECK-LABEL: fptoui_fdiv_uitofp_nxv2i32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    ptrue p0.d
+; CHECK-NEXT:    ucvtf z0.d, p0/m, z0.s
+; CHECK-NEXT:    ucvtf z1.d, p0/m, z1.s
+; CHECK-NEXT:    fdiv z0.d, p0/m, z0.d, z1.d
+; CHECK-NEXT:    fcvtzs z0.d, p0/m, z0.d
+; CHECK-NEXT:    ret
+  %fa = uitofp <vscale x 2 x i32> %a to <vscale x 2 x double>
+  %fb = uitofp <vscale x 2 x i32> %b to <vscale x 2 x double>
+  %fdiv = fdiv <vscale x 2 x double> %fa, %fb
+  %res = fptoui <vscale x 2 x double> %fdiv to <vscale x 2 x i32>
+  ret <vscale x 2 x i32> %res
+}
+
+define <vscale x 2 x i32> @fptosi_fdiv_sitofp_nxv2i32(<vscale x 2 x i32> %a, <vscale x 2 x i32> %b) {
+; CHECK-LABEL: fptosi_fdiv_sitofp_nxv2i32:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    ptrue p0.d
+; CHECK-NEXT:    scvtf z0.d, p0/m, z0.s
+; CHECK-NEXT:    scvtf z1.d, p0/m, z1.s
+; CHECK-NEXT:    fdiv z0.d, p0/m, z0.d, z1.d
+; CHECK-NEXT:    fcvtzs z0.d, p0/m, z0.d
+; CHECK-NEXT:    ret
+  %fa = sitofp <vscale x 2 x i32> %a to <vscale x 2 x double>
+  %fb = sitofp <vscale x 2 x i32> %b to <vscale x 2 x double>
+  %fdiv = fdiv <vscale x 2 x double> %fa, %fb
+  %res = fptosi <vscale x 2 x double> %fdiv to <vscale x 2 x i32>
+  ret <vscale x 2 x i32> %res
+}

Comment thread llvm/lib/Target/AArch64/AArch64ISelLowering.cpp Outdated
EVT IntVT = N->getValueType(0);
EVT FPVT = FDiv.getValueType();

if (IntVT.getVectorElementType() != MVT::i32 ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That assumes that the type is a vector. Why is that important anyway?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the isVector() safety check in the latest commit. It's important because the original issue specifically targets SVE vector folding, and as Andarwinux noted, we need to strictly restrict this fold to i32-f64-i32 vector conversions to avoid altering the original behavior for other types

return SDValue();

unsigned DivOpc = IsSigned ? ISD::SDIV : ISD::UDIV;
return DAG.getNode(DivOpc, SDLoc(N), IntVT, Op0.getOperand(0),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this transformation is problematic whenever the divisor is zero which would cause UB.

@Andarwinux Andarwinux Aug 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://support.arm.com/documentation/ddi0487/mc/-Part-C-The-AArch64-Instruction-Set/-Chapter-C3-A64-Instruction-Set-Overview/-C3-7-Data-processing---register/-C3-7-10-Multiply-and-divide

If a signed integer division (INT_MIN / -1) is performed where INT_MIN is the most negative integer value representable in the selected register size, then the result overflows the signed integer range. No indication of this overflow is produced and the result that is written to the destination register is INT_MIN.

A division by zero results in a zero being written to the destination register, without any indication that the division by zero occurred.

https://llvm.org/docs/LangRef.html#fptoui-to-instruction

The ‘fptoui’ instruction converts its floating-point operand into the nearest (rounding towards zero) unsigned integer value. If the value cannot fit in ty2, the result is a poison value.

The ‘fptosi’ instruction converts its floating-point operand into the nearest (rounding towards zero) signed integer value. If the value cannot fit in ty2, the result is a poison value.

It's still just return a poison.

@dnmohanty
dnmohanty force-pushed the aarch64-sve-fdiv-fold-clean branch from 135937d to 7b9c170 Compare August 9, 2026 20:56
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

✅ With the latest revision this PR passed the C/C++ code formatter.

@dnmohanty
dnmohanty force-pushed the aarch64-sve-fdiv-fold-clean branch 2 times, most recently from 06605f9 to c45c77f Compare August 10, 2026 03:50
@dnmohanty
dnmohanty force-pushed the aarch64-sve-fdiv-fold-clean branch from c45c77f to 4f3696c Compare August 10, 2026 03:57
@github-actions

Copy link
Copy Markdown

🪟 Windows x64 Test Results

The build failed before running any tests. Click on a failure below to see the details.

[code=1] lib/Target/AArch64/CMakeFiles/LLVMAArch64CodeGen.dir/AArch64ISelLowering.cpp.obj
FAILED: [code=1] lib/Target/AArch64/CMakeFiles/LLVMAArch64CodeGen.dir/AArch64ISelLowering.cpp.obj
sccache C:\clang\clang-msvc\bin\clang-cl.exe  /nologo -TP -DUNICODE -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_NONSTDC_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_SECURE_NO_WARNINGS -D_GLIBCXX_ASSERTIONS -D_HAS_EXCEPTIONS=0 -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE -D_SCL_SECURE_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS -D_UNICODE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -IC:\_work\llvm-project\llvm-project\build\lib\Target\AArch64 -IC:\_work\llvm-project\llvm-project\llvm\lib\Target\AArch64 -IC:\_work\llvm-project\llvm-project\build\include -IC:\_work\llvm-project\llvm-project\llvm\include /DWIN32 /D_WINDOWS   /Zc:inline /Zc:__cplusplus /Oi /Brepro /bigobj /permissive- -Werror=unguarded-availability-new /W4  -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wno-pass-failed -Wmisleading-indentation -Wctad-maybe-unsupported /Gw /O2 /Ob2  -std:c++17 -MD -UNDEBUG /EHs-c- /GR- /showIncludes /Folib\Target\AArch64\CMakeFiles\LLVMAArch64CodeGen.dir\AArch64ISelLowering.cpp.obj /Fdlib\Target\AArch64\CMakeFiles\LLVMAArch64CodeGen.dir\LLVMAArch64CodeGen.pdb -c -- C:\_work\llvm-project\llvm-project\llvm\lib\Target\AArch64\AArch64ISelLowering.cpp
C:\_work\llvm-project\llvm-project\llvm\lib\Target\AArch64\AArch64ISelLowering.cpp(25270,36): error: no member named 'SADDWB' in namespace 'llvm::AArch64ISD'
25270 |     return DAG.getNode(AArch64ISD::SADDWB, SDLoc(N), N->getValueType(0),
|                                    ^~~~~~
C:\_work\llvm-project\llvm-project\llvm\lib\Target\AArch64\AArch64ISelLowering.cpp(25273,36): error: no member named 'SADDWT' in namespace 'llvm::AArch64ISD'
25273 |     return DAG.getNode(AArch64ISD::SADDWT, SDLoc(N), N->getValueType(0),
|                                    ^~~~~~
C:\_work\llvm-project\llvm-project\llvm\lib\Target\AArch64\AArch64ISelLowering.cpp(25276,36): error: no member named 'UADDWB' in namespace 'llvm::AArch64ISD'
25276 |     return DAG.getNode(AArch64ISD::UADDWB, SDLoc(N), N->getValueType(0),
|                                    ^~~~~~
C:\_work\llvm-project\llvm-project\llvm\lib\Target\AArch64\AArch64ISelLowering.cpp(25279,36): error: no member named 'UADDWT' in namespace 'llvm::AArch64ISD'
25279 |     return DAG.getNode(AArch64ISD::UADDWT, SDLoc(N), N->getValueType(0),
|                                    ^~~~~~
C:\_work\llvm-project\llvm-project\llvm\lib\Target\AArch64\AArch64ISelLowering.cpp(34836,50): error: no member named 'UADDWB' in namespace 'llvm::AArch64ISD'
34836 |     unsigned LoOpcode = IsUnsigned ? AArch64ISD::UADDWB : AArch64ISD::SADDWB;
|                                                  ^~~~~~
C:\_work\llvm-project\llvm-project\llvm\lib\Target\AArch64\AArch64ISelLowering.cpp(34836,71): error: no member named 'SADDWB' in namespace 'llvm::AArch64ISD'
34836 |     unsigned LoOpcode = IsUnsigned ? AArch64ISD::UADDWB : AArch64ISD::SADDWB;
|                                                                       ^~~~~~
C:\_work\llvm-project\llvm-project\llvm\lib\Target\AArch64\AArch64ISelLowering.cpp(34837,50): error: no member named 'UADDWT' in namespace 'llvm::AArch64ISD'
34837 |     unsigned HiOpcode = IsUnsigned ? AArch64ISD::UADDWT : AArch64ISD::SADDWT;
|                                                  ^~~~~~
C:\_work\llvm-project\llvm-project\llvm\lib\Target\AArch64\AArch64ISelLowering.cpp(34837,71): error: no member named 'SADDWT' in namespace 'llvm::AArch64ISD'
34837 |     unsigned HiOpcode = IsUnsigned ? AArch64ISD::UADDWT : AArch64ISD::SADDWT;
|                                                                       ^~~~~~
8 errors generated.

If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the infrastructure label.

@github-actions

Copy link
Copy Markdown

🐧 Linux x64 Test Results

The build failed before running any tests. Click on a failure below to see the details.

lib/Target/AArch64/CMakeFiles/LLVMAArch64CodeGen.dir/AArch64ISelLowering.cpp.o
FAILED: lib/Target/AArch64/CMakeFiles/LLVMAArch64CodeGen.dir/AArch64ISelLowering.cpp.o
sccache /opt/llvm/bin/clang++ -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_GLIBCXX_USE_CXX11_ABI=1 -D_GNU_SOURCE -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/home/gha/actions-runner/_work/llvm-project/llvm-project/build/lib/Target/AArch64 -I/home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/Target/AArch64 -I/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include -I/home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/include -gmlt -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wno-pass-failed -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -O3 -DNDEBUG -std=c++17 -fvisibility=hidden -UNDEBUG -fno-exceptions -funwind-tables -fno-rtti -MD -MT lib/Target/AArch64/CMakeFiles/LLVMAArch64CodeGen.dir/AArch64ISelLowering.cpp.o -MF lib/Target/AArch64/CMakeFiles/LLVMAArch64CodeGen.dir/AArch64ISelLowering.cpp.o.d -o lib/Target/AArch64/CMakeFiles/LLVMAArch64CodeGen.dir/AArch64ISelLowering.cpp.o -c /home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
/home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp:25270:36: error: no member named 'SADDWB' in namespace 'llvm::AArch64ISD'
25270 |     return DAG.getNode(AArch64ISD::SADDWB, SDLoc(N), N->getValueType(0),
|                                    ^~~~~~
/home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp:25273:36: error: no member named 'SADDWT' in namespace 'llvm::AArch64ISD'
25273 |     return DAG.getNode(AArch64ISD::SADDWT, SDLoc(N), N->getValueType(0),
|                                    ^~~~~~
/home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp:25276:36: error: no member named 'UADDWB' in namespace 'llvm::AArch64ISD'
25276 |     return DAG.getNode(AArch64ISD::UADDWB, SDLoc(N), N->getValueType(0),
|                                    ^~~~~~
/home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp:25279:36: error: no member named 'UADDWT' in namespace 'llvm::AArch64ISD'
25279 |     return DAG.getNode(AArch64ISD::UADDWT, SDLoc(N), N->getValueType(0),
|                                    ^~~~~~
/home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp:34836:50: error: no member named 'UADDWB' in namespace 'llvm::AArch64ISD'
34836 |     unsigned LoOpcode = IsUnsigned ? AArch64ISD::UADDWB : AArch64ISD::SADDWB;
|                                                  ^~~~~~
/home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp:34836:71: error: no member named 'SADDWB' in namespace 'llvm::AArch64ISD'
34836 |     unsigned LoOpcode = IsUnsigned ? AArch64ISD::UADDWB : AArch64ISD::SADDWB;
|                                                                       ^~~~~~
/home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp:34837:50: error: no member named 'UADDWT' in namespace 'llvm::AArch64ISD'
34837 |     unsigned HiOpcode = IsUnsigned ? AArch64ISD::UADDWT : AArch64ISD::SADDWT;
|                                                  ^~~~~~
/home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp:34837:71: error: no member named 'SADDWT' in namespace 'llvm::AArch64ISD'
34837 |     unsigned HiOpcode = IsUnsigned ? AArch64ISD::UADDWT : AArch64ISD::SADDWT;
|                                                                       ^~~~~~
8 errors generated.

If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the infrastructure label.

@dnmohanty

dnmohanty commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

@guy-david / @Andarwinux, I noticed the CI builds are failing due to some missing AArch64ISD opcodes (like SADDWB). Since my code doesn't interact with those lines, I'm wondering if I accidentally pulled in a temporary broken state from main, or if I genuinely messed something up on my end? let me know if I need to fix anything

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[AArch64] missed fold vector fptoui(fdiv(uitofp(x),uitofp(y))) or fptosi(fdiv(sitofp(x),sitofp(y))) into udiv(x,y) or sdiv(x,y) for sve

3 participants