[llvm] 478f9da - [SelectionDAG] Widen vector math libcalls when no routine is available (#218948)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 8 01:56:43 PDT 2026


Author: Mattéo Rizza Murgier
Date: 2026-09-08T10:56:38+02:00
New Revision: 478f9da04672855854811d5bca7f1fd05c52ed90

URL: https://github.com/llvm/llvm-project/commit/478f9da04672855854811d5bca7f1fd05c52ed90
DIFF: https://github.com/llvm/llvm-project/commit/478f9da04672855854811d5bca7f1fd05c52ed90.diff

LOG: [SelectionDAG] Widen vector math libcalls when no routine is available (#218948)

`tryExpandVecMathCall` currently only checks for a vector math routine
matching the node's exact vector type, unrolling when none is found.
This is suboptimal, and can lead to crashes for scalable types (which
cannot be unrolled).

This PR implements widening to first check if a routine with a wider vec
type exists before falling back to unrolling.

Example:
```
; llc -mtriple=aarch64 -mattr=+sve -vector-library=sleefgnuabi crash.ll
define <vscale x 2 x float> @frem_nxv2f32(<vscale x 2 x float> %a, <vscale x 2 x float> %b) {
  %res = frem <vscale x 2 x float> %a, %b
  ret <vscale x 2 x float> %res
}
```

NOTE: When the routines take a mask (e.g. if they're scalable variants),
only relevant lanes are activated. Vectors are padded with copies of the
operand to avoid running on poison.

Added: 
    

Modified: 
    llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
    llvm/test/CodeGen/AArch64/fp-veclib-expansion.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
index 03fae7d7af4be..1776c3304b840 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp
@@ -27,6 +27,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/VectorUtils.h"
@@ -152,7 +153,8 @@ class VectorLegalizer {
   void ExpandStrictFPOp(SDNode *Node, SmallVectorImpl<SDValue> &Results);
   void ExpandREM(SDNode *Node, SmallVectorImpl<SDValue> &Results);
 
-  bool tryExpandVecMathCall(SDNode *Node, RTLIB::Libcall LC,
+  bool tryExpandVecMathCall(SDNode *Node,
+                            function_ref<RTLIB::Libcall(EVT)> GetLibcall,
                             SmallVectorImpl<SDValue> &Results);
 
   void UnrollStrictFPOp(SDNode *Node, SmallVectorImpl<SDValue> &Results);
@@ -1250,13 +1252,10 @@ void VectorLegalizer::Expand(SDNode *Node, SmallVectorImpl<SDValue> &Results) {
       return;
     }
     break;
-  case ISD::FREM: {
-    RTLIB::Libcall LC = RTLIB::getREM(Node->getValueType(0));
-    if (tryExpandVecMathCall(Node, LC, Results))
+  case ISD::FREM:
+    if (tryExpandVecMathCall(Node, RTLIB::getREM, Results))
       return;
-
     break;
-  }
   case ISD::FSINCOS:
   case ISD::FSINCOSPI: {
     EVT VT = Node->getValueType(0);
@@ -1271,24 +1270,20 @@ void VectorLegalizer::Expand(SDNode *Node, SmallVectorImpl<SDValue> &Results) {
     // scalarizing.
     break;
   }
-  case ISD::FPOW: {
-    RTLIB::Libcall LC = RTLIB::getPOW(Node->getValueType(0));
-    if (tryExpandVecMathCall(Node, LC, Results))
+  case ISD::FPOW:
+    if (tryExpandVecMathCall(Node, RTLIB::getPOW, Results))
       return;
 
     // TODO: Try to see if there's a narrower call available to use before
     // scalarizing.
     break;
-  }
-  case ISD::FCBRT: {
-    RTLIB::Libcall LC = RTLIB::getCBRT(Node->getValueType(0));
-    if (tryExpandVecMathCall(Node, LC, Results))
+  case ISD::FCBRT:
+    if (tryExpandVecMathCall(Node, RTLIB::getCBRT, Results))
       return;
 
     // TODO: Try to see if there's a narrower call available to use before
     // scalarizing.
     break;
-  }
   case ISD::FMODF: {
     EVT VT = Node->getValueType(0);
     RTLIB::Libcall LC = RTLIB::getMODF(VT);
@@ -2153,24 +2148,36 @@ void VectorLegalizer::ExpandREM(SDNode *Node,
 }
 
 // Try to expand libm nodes into vector math routine calls. Callers provide the
-// LibFunc equivalent of the passed in Node, which is used to lookup mappings
-// within TargetLibraryInfo. The only mappings considered are those where the
-// result and all operands are the same vector type. While predicated nodes are
-// not supported, we will emit calls to masked routines by passing in an all
-// true mask.
-bool VectorLegalizer::tryExpandVecMathCall(SDNode *Node, RTLIB::Libcall LC,
-                                           SmallVectorImpl<SDValue> &Results) {
+// RTLIB::get<OP>(EVT) selector of the node's libcall family, which is used to
+// look up mappings within RuntimeLibcallsInfo. The only mappings considered are
+// those where the result and all operands are the same vector type. While
+// predicated nodes are not supported, we will emit calls to masked routines by
+// passing in a mask that is true for the lanes computed by the node.
+bool VectorLegalizer::tryExpandVecMathCall(
+    SDNode *Node, function_ref<RTLIB::Libcall(EVT)> GetLibcall,
+    SmallVectorImpl<SDValue> &Results) {
   // Chain must be propagated but currently strict fp operations are down
   // converted to their none strict counterpart.
   assert(!Node->isStrictFPOpcode() && "Unexpected strict fp operation!");
 
-  RTLIB::LibcallImpl LCImpl = DAG.getLibcalls().getLibcallImpl(LC);
-  if (LCImpl == RTLIB::Unsupported)
+  EVT VT = Node->getValueType(0);
+  LLVMContext &Ctx = *DAG.getContext();
+  const LibcallLoweringInfo &Libcalls = DAG.getLibcalls();
+
+  // Try to widen the vector type when no libcall is available at that width.
+  EVT CallVT = VT;
+  RTLIB::LibcallImpl LCImpl = Libcalls.getLibcallImpl(GetLibcall(CallVT));
+  if (LCImpl == RTLIB::Unsupported && VT.getVectorElementCount().isScalar())
     return false;
+  while (LCImpl == RTLIB::Unsupported) {
+    CallVT = CallVT.getDoubleNumVectorElementsVT(Ctx);
+    if (!CallVT.isSimple())
+      return false;
+    if (TLI.isTypeLegal(CallVT))
+      LCImpl = Libcalls.getLibcallImpl(GetLibcall(CallVT));
+  }
 
-  EVT VT = Node->getValueType(0);
   const RTLIB::RuntimeLibcallsInfo &RTLCI = TLI.getRuntimeLibcallsInfo();
-  LLVMContext &Ctx = *DAG.getContext();
 
   auto [FuncTy, FuncAttrs] = RTLCI.getFunctionTy(
       Ctx, DAG.getSubtarget().getTargetTriple(), DAG.getDataLayout(), LCImpl);
@@ -2182,7 +2189,7 @@ bool VectorLegalizer::tryExpandVecMathCall(SDNode *Node, RTLIB::Libcall LC,
 
   // Sanity check just in case function has unexpected parameters.
   assert(FuncTy->getNumParams() == Node->getNumOperands() + HasMaskArg &&
-         EVT::getEVT(FuncTy->getReturnType(), true) == VT &&
+         EVT::getEVT(FuncTy->getReturnType(), true) == CallVT &&
          "mismatch in value type and call signature type");
 
   for (unsigned I = 0, E = FuncTy->getNumParams(); I != E; ++I) {
@@ -2190,13 +2197,27 @@ bool VectorLegalizer::tryExpandVecMathCall(SDNode *Node, RTLIB::Libcall LC,
 
     if (HasMaskArg && I == E - 1) {
       assert(cast<VectorType>(ParamTy)->getElementType()->isIntegerTy(1) &&
+             cast<VectorType>(ParamTy)->getElementCount() ==
+                 CallVT.getVectorElementCount() &&
              "unexpected vector mask type");
-      EVT MaskVT = TLI.getSetCCResultType(DAG.getDataLayout(), Ctx, VT);
-      Args.emplace_back(DAG.getBoolConstant(true, DL, MaskVT, VT),
-                        MaskVT.getTypeForEVT(Ctx));
-
+      EVT MaskVT = EVT::getEVT(ParamTy, /*HandleUnknown=*/true);
+      EVT SubMaskVT =
+          MaskVT.changeVectorElementCount(Ctx, VT.getVectorElementCount());
+      SDValue Mask = DAG.getBoolConstant(true, DL, SubMaskVT, VT);
+      // Only the lanes holding the node's elements need to be active.
+      if (CallVT != VT)
+        Mask = DAG.getInsertSubvector(
+            DL, DAG.getBoolConstant(false, DL, MaskVT, CallVT), Mask, 0);
+      Args.emplace_back(Mask, ParamTy);
     } else {
       SDValue Op = Node->getOperand(I);
+      assert(Op.getValueType() == VT && "mismatch in vector types");
+      if (CallVT != VT) {
+        unsigned NumConcat =
+            CallVT.getVectorMinNumElements() / VT.getVectorMinNumElements();
+        SmallVector<SDValue, 4> Ops(NumConcat, Op);
+        Op = DAG.getNode(ISD::CONCAT_VECTORS, DL, CallVT, Ops);
+      }
       assert(Op.getValueType() == EVT::getEVT(ParamTy, true) &&
              "mismatch in value type and call argument type");
       Args.emplace_back(Op, ParamTy);
@@ -2214,7 +2235,10 @@ bool VectorLegalizer::tryExpandVecMathCall(SDNode *Node, RTLIB::Libcall LC,
       .setLibCallee(CC, FuncTy->getReturnType(), Callee, std::move(Args));
 
   std::pair<SDValue, SDValue> CallResult = TLI.LowerCallTo(CLI);
-  Results.push_back(CallResult.first);
+  SDValue Result = CallResult.first;
+  if (CallVT != VT)
+    Result = DAG.getExtractSubvector(DL, VT, Result, 0);
+  Results.push_back(Result);
   return true;
 }
 

diff  --git a/llvm/test/CodeGen/AArch64/fp-veclib-expansion.ll b/llvm/test/CodeGen/AArch64/fp-veclib-expansion.ll
index 2c8e2190f8209..8293f90f4e034 100644
--- a/llvm/test/CodeGen/AArch64/fp-veclib-expansion.ll
+++ b/llvm/test/CodeGen/AArch64/fp-veclib-expansion.ll
@@ -8,8 +8,6 @@ define <2 x double> @frem_v2f64(<2 x double> %unused, <2 x double> %a, <2 x doub
 ; ARMPL-LABEL: frem_v2f64:
 ; ARMPL:       // %bb.0:
 ; ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
-; ARMPL-NEXT:    .cfi_def_cfa_offset 16
-; ARMPL-NEXT:    .cfi_offset w30, -16
 ; ARMPL-NEXT:    mov v0.16b, v1.16b
 ; ARMPL-NEXT:    mov v1.16b, v2.16b
 ; ARMPL-NEXT:    bl armpl_vfmodq_f64
@@ -19,8 +17,6 @@ define <2 x double> @frem_v2f64(<2 x double> %unused, <2 x double> %a, <2 x doub
 ; SLEEF-LABEL: frem_v2f64:
 ; SLEEF:       // %bb.0:
 ; SLEEF-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
-; SLEEF-NEXT:    .cfi_def_cfa_offset 16
-; SLEEF-NEXT:    .cfi_offset w30, -16
 ; SLEEF-NEXT:    mov v0.16b, v1.16b
 ; SLEEF-NEXT:    mov v1.16b, v2.16b
 ; SLEEF-NEXT:    bl _ZGVnN2vv_fmod
@@ -34,8 +30,6 @@ define <4 x float> @frem_strict_v4f32(<4 x float> %unused, <4 x float> %a, <4 x
 ; ARMPL-LABEL: frem_strict_v4f32:
 ; ARMPL:       // %bb.0:
 ; ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
-; ARMPL-NEXT:    .cfi_def_cfa_offset 16
-; ARMPL-NEXT:    .cfi_offset w30, -16
 ; ARMPL-NEXT:    mov v0.16b, v1.16b
 ; ARMPL-NEXT:    mov v1.16b, v2.16b
 ; ARMPL-NEXT:    bl armpl_vfmodq_f32
@@ -45,8 +39,6 @@ define <4 x float> @frem_strict_v4f32(<4 x float> %unused, <4 x float> %a, <4 x
 ; SLEEF-LABEL: frem_strict_v4f32:
 ; SLEEF:       // %bb.0:
 ; SLEEF-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
-; SLEEF-NEXT:    .cfi_def_cfa_offset 16
-; SLEEF-NEXT:    .cfi_offset w30, -16
 ; SLEEF-NEXT:    mov v0.16b, v1.16b
 ; SLEEF-NEXT:    mov v1.16b, v2.16b
 ; SLEEF-NEXT:    bl _ZGVnN4vv_fmodf
@@ -60,8 +52,6 @@ define <vscale x 4 x float> @frem_nxv4f32(<vscale x 4 x float> %unused, <vscale
 ; ARMPL-LABEL: frem_nxv4f32:
 ; ARMPL:       // %bb.0:
 ; ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
-; ARMPL-NEXT:    .cfi_def_cfa_offset 16
-; ARMPL-NEXT:    .cfi_offset w30, -16
 ; ARMPL-NEXT:    mov z0.d, z1.d
 ; ARMPL-NEXT:    mov z1.d, z2.d
 ; ARMPL-NEXT:    ptrue p0.s
@@ -72,8 +62,6 @@ define <vscale x 4 x float> @frem_nxv4f32(<vscale x 4 x float> %unused, <vscale
 ; SLEEF-LABEL: frem_nxv4f32:
 ; SLEEF:       // %bb.0:
 ; SLEEF-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
-; SLEEF-NEXT:    .cfi_def_cfa_offset 16
-; SLEEF-NEXT:    .cfi_offset w30, -16
 ; SLEEF-NEXT:    mov z0.d, z1.d
 ; SLEEF-NEXT:    mov z1.d, z2.d
 ; SLEEF-NEXT:    ptrue p0.s
@@ -88,8 +76,6 @@ define <vscale x 2 x double> @frem_strict_nxv2f64(<vscale x 2 x double> %unused,
 ; ARMPL-LABEL: frem_strict_nxv2f64:
 ; ARMPL:       // %bb.0:
 ; ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
-; ARMPL-NEXT:    .cfi_def_cfa_offset 16
-; ARMPL-NEXT:    .cfi_offset w30, -16
 ; ARMPL-NEXT:    mov z0.d, z1.d
 ; ARMPL-NEXT:    mov z1.d, z2.d
 ; ARMPL-NEXT:    ptrue p0.d
@@ -100,8 +86,6 @@ define <vscale x 2 x double> @frem_strict_nxv2f64(<vscale x 2 x double> %unused,
 ; SLEEF-LABEL: frem_strict_nxv2f64:
 ; SLEEF:       // %bb.0:
 ; SLEEF-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
-; SLEEF-NEXT:    .cfi_def_cfa_offset 16
-; SLEEF-NEXT:    .cfi_offset w30, -16
 ; SLEEF-NEXT:    mov z0.d, z1.d
 ; SLEEF-NEXT:    mov z1.d, z2.d
 ; SLEEF-NEXT:    ptrue p0.d
@@ -112,5 +96,193 @@ define <vscale x 2 x double> @frem_strict_nxv2f64(<vscale x 2 x double> %unused,
   ret <vscale x 2 x double> %res
 }
 
-attributes #0 = { "target-features"="+sve" }
-attributes #1 = { "target-features"="+sve" strictfp }
+; Expected to be widened.
+define <2 x float> @frem_v2f32(<2 x float> %unused, <2 x float> %a, <2 x float> %b) #0 {
+; ARMPL-LABEL: frem_v2f32:
+; ARMPL:       // %bb.0:
+; ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; ARMPL-NEXT:    // kill: def $d2 killed $d2 def $q2
+; ARMPL-NEXT:    fmov d0, d1
+; ARMPL-NEXT:    mov v2.d[1], v2.d[0]
+; ARMPL-NEXT:    mov v0.d[1], v0.d[0]
+; ARMPL-NEXT:    mov v1.16b, v2.16b
+; ARMPL-NEXT:    bl armpl_vfmodq_f32
+; ARMPL-NEXT:    // kill: def $d0 killed $d0 killed $q0
+; ARMPL-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; ARMPL-NEXT:    ret
+;
+; SLEEF-LABEL: frem_v2f32:
+; SLEEF:       // %bb.0:
+; SLEEF-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; SLEEF-NEXT:    // kill: def $d2 killed $d2 def $q2
+; SLEEF-NEXT:    fmov d0, d1
+; SLEEF-NEXT:    mov v2.d[1], v2.d[0]
+; SLEEF-NEXT:    mov v0.d[1], v0.d[0]
+; SLEEF-NEXT:    mov v1.16b, v2.16b
+; SLEEF-NEXT:    bl _ZGVnN4vv_fmodf
+; SLEEF-NEXT:    // kill: def $d0 killed $d0 killed $q0
+; SLEEF-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; SLEEF-NEXT:    ret
+  %res = frem <2 x float> %a, %b
+  ret <2 x float> %res
+}
+
+; Expected not to be widened.
+define <1 x double> @frem_v1f64(<1 x double> %unused, <1 x double> %a, <1 x double> %b) #0 {
+; ARMPL-LABEL: frem_v1f64:
+; ARMPL:       // %bb.0:
+; ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; ARMPL-NEXT:    fmov d0, d1
+; ARMPL-NEXT:    fmov d1, d2
+; ARMPL-NEXT:    bl fmod
+; ARMPL-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; ARMPL-NEXT:    ret
+;
+; SLEEF-LABEL: frem_v1f64:
+; SLEEF:       // %bb.0:
+; SLEEF-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; SLEEF-NEXT:    fmov d0, d1
+; SLEEF-NEXT:    fmov d1, d2
+; SLEEF-NEXT:    bl fmod
+; SLEEF-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; SLEEF-NEXT:    ret
+  %res = frem <1 x double> %a, %b
+  ret <1 x double> %res
+}
+
+; Expected to widen with a mask.
+define <vscale x 2 x float> @frem_nxv2f32(<vscale x 2 x float> %unused, <vscale x 2 x float> %a, <vscale x 2 x float> %b) #0 {
+; ARMPL-LABEL: frem_nxv2f32:
+; ARMPL:       // %bb.0:
+; ARMPL-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; ARMPL-NEXT:    pfalse p0.b
+; ARMPL-NEXT:    uzp1 z0.s, z1.s, z1.s
+; ARMPL-NEXT:    uzp1 z1.s, z2.s, z2.s
+; ARMPL-NEXT:    ptrue p1.d
+; ARMPL-NEXT:    uzp1 p0.s, p1.s, p0.s
+; ARMPL-NEXT:    bl armpl_svfmod_f32_x
+; ARMPL-NEXT:    uunpklo z0.d, z0.s
+; ARMPL-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; ARMPL-NEXT:    ret
+;
+; SLEEF-LABEL: frem_nxv2f32:
+; SLEEF:       // %bb.0:
+; SLEEF-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; SLEEF-NEXT:    pfalse p0.b
+; SLEEF-NEXT:    uzp1 z0.s, z1.s, z1.s
+; SLEEF-NEXT:    uzp1 z1.s, z2.s, z2.s
+; SLEEF-NEXT:    ptrue p1.d
+; SLEEF-NEXT:    uzp1 p0.s, p1.s, p0.s
+; SLEEF-NEXT:    bl _ZGVsMxvv_fmodf
+; SLEEF-NEXT:    uunpklo z0.d, z0.s
+; SLEEF-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; SLEEF-NEXT:    ret
+  %res = frem <vscale x 2 x float> %a, %b
+  ret <vscale x 2 x float> %res
+}
+
+; Expected to get scalarized: no libcalls exist for this type.
+define <4 x half> @frem_v4f16(<4 x half> %unused, <4 x half> %a, <4 x half> %b) #0 {
+; ARMPL-LABEL: frem_v4f16:
+; ARMPL:       // %bb.0:
+; ARMPL-NEXT:    sub sp, sp, #64
+; ARMPL-NEXT:    // kill: def $d1 killed $d1 def $q1
+; ARMPL-NEXT:    // kill: def $d2 killed $d2 def $q2
+; ARMPL-NEXT:    stp q1, q2, [sp, #16] // 32-byte Folded Spill
+; ARMPL-NEXT:    mov h0, v1.h[1]
+; ARMPL-NEXT:    mov h1, v2.h[1]
+; ARMPL-NEXT:    str x30, [sp, #48] // 8-byte Spill
+; ARMPL-NEXT:    fcvt s0, h0
+; ARMPL-NEXT:    fcvt s1, h1
+; ARMPL-NEXT:    bl fmodf
+; ARMPL-NEXT:    ldr q1, [sp, #16] // 16-byte Reload
+; ARMPL-NEXT:    fcvt h0, s0
+; ARMPL-NEXT:    fcvt s2, h1
+; ARMPL-NEXT:    str q0, [sp] // 16-byte Spill
+; ARMPL-NEXT:    ldr q0, [sp, #32] // 16-byte Reload
+; ARMPL-NEXT:    fcvt s1, h0
+; ARMPL-NEXT:    fmov s0, s2
+; ARMPL-NEXT:    bl fmodf
+; ARMPL-NEXT:    ldr q1, [sp, #16] // 16-byte Reload
+; ARMPL-NEXT:    fcvt h3, s0
+; ARMPL-NEXT:    ldr q0, [sp, #32] // 16-byte Reload
+; ARMPL-NEXT:    mov h1, v1.h[2]
+; ARMPL-NEXT:    mov h2, v0.h[2]
+; ARMPL-NEXT:    ldr q0, [sp] // 16-byte Reload
+; ARMPL-NEXT:    mov v3.h[1], v0.h[0]
+; ARMPL-NEXT:    fcvt s0, h1
+; ARMPL-NEXT:    fcvt s1, h2
+; ARMPL-NEXT:    str q3, [sp] // 16-byte Spill
+; ARMPL-NEXT:    bl fmodf
+; ARMPL-NEXT:    ldp q1, q2, [sp, #16] // 32-byte Folded Reload
+; ARMPL-NEXT:    fcvt h0, s0
+; ARMPL-NEXT:    ldr q3, [sp] // 16-byte Reload
+; ARMPL-NEXT:    mov h1, v1.h[3]
+; ARMPL-NEXT:    mov h2, v2.h[3]
+; ARMPL-NEXT:    mov v3.h[2], v0.h[0]
+; ARMPL-NEXT:    fcvt s0, h1
+; ARMPL-NEXT:    fcvt s1, h2
+; ARMPL-NEXT:    str q3, [sp] // 16-byte Spill
+; ARMPL-NEXT:    bl fmodf
+; ARMPL-NEXT:    fcvt h1, s0
+; ARMPL-NEXT:    ldr q0, [sp] // 16-byte Reload
+; ARMPL-NEXT:    ldr x30, [sp, #48] // 8-byte Reload
+; ARMPL-NEXT:    mov v0.h[3], v1.h[0]
+; ARMPL-NEXT:    // kill: def $d0 killed $d0 killed $q0
+; ARMPL-NEXT:    add sp, sp, #64
+; ARMPL-NEXT:    ret
+;
+; SLEEF-LABEL: frem_v4f16:
+; SLEEF:       // %bb.0:
+; SLEEF-NEXT:    sub sp, sp, #64
+; SLEEF-NEXT:    // kill: def $d1 killed $d1 def $q1
+; SLEEF-NEXT:    // kill: def $d2 killed $d2 def $q2
+; SLEEF-NEXT:    stp q1, q2, [sp, #16] // 32-byte Folded Spill
+; SLEEF-NEXT:    mov h0, v1.h[1]
+; SLEEF-NEXT:    mov h1, v2.h[1]
+; SLEEF-NEXT:    str x30, [sp, #48] // 8-byte Spill
+; SLEEF-NEXT:    fcvt s0, h0
+; SLEEF-NEXT:    fcvt s1, h1
+; SLEEF-NEXT:    bl fmodf
+; SLEEF-NEXT:    ldr q1, [sp, #16] // 16-byte Reload
+; SLEEF-NEXT:    fcvt h0, s0
+; SLEEF-NEXT:    fcvt s2, h1
+; SLEEF-NEXT:    str q0, [sp] // 16-byte Spill
+; SLEEF-NEXT:    ldr q0, [sp, #32] // 16-byte Reload
+; SLEEF-NEXT:    fcvt s1, h0
+; SLEEF-NEXT:    fmov s0, s2
+; SLEEF-NEXT:    bl fmodf
+; SLEEF-NEXT:    ldr q1, [sp, #16] // 16-byte Reload
+; SLEEF-NEXT:    fcvt h3, s0
+; SLEEF-NEXT:    ldr q0, [sp, #32] // 16-byte Reload
+; SLEEF-NEXT:    mov h1, v1.h[2]
+; SLEEF-NEXT:    mov h2, v0.h[2]
+; SLEEF-NEXT:    ldr q0, [sp] // 16-byte Reload
+; SLEEF-NEXT:    mov v3.h[1], v0.h[0]
+; SLEEF-NEXT:    fcvt s0, h1
+; SLEEF-NEXT:    fcvt s1, h2
+; SLEEF-NEXT:    str q3, [sp] // 16-byte Spill
+; SLEEF-NEXT:    bl fmodf
+; SLEEF-NEXT:    ldp q1, q2, [sp, #16] // 32-byte Folded Reload
+; SLEEF-NEXT:    fcvt h0, s0
+; SLEEF-NEXT:    ldr q3, [sp] // 16-byte Reload
+; SLEEF-NEXT:    mov h1, v1.h[3]
+; SLEEF-NEXT:    mov h2, v2.h[3]
+; SLEEF-NEXT:    mov v3.h[2], v0.h[0]
+; SLEEF-NEXT:    fcvt s0, h1
+; SLEEF-NEXT:    fcvt s1, h2
+; SLEEF-NEXT:    str q3, [sp] // 16-byte Spill
+; SLEEF-NEXT:    bl fmodf
+; SLEEF-NEXT:    fcvt h1, s0
+; SLEEF-NEXT:    ldr q0, [sp] // 16-byte Reload
+; SLEEF-NEXT:    ldr x30, [sp, #48] // 8-byte Reload
+; SLEEF-NEXT:    mov v0.h[3], v1.h[0]
+; SLEEF-NEXT:    // kill: def $d0 killed $d0 killed $q0
+; SLEEF-NEXT:    add sp, sp, #64
+; SLEEF-NEXT:    ret
+  %res = frem <4 x half> %a, %b
+  ret <4 x half> %res
+}
+
+attributes #0 = { "target-features"="+sve" nounwind }
+attributes #1 = { "target-features"="+sve" strictfp nounwind }


        


More information about the llvm-commits mailing list