[llvm] [CodeGen] Teach ReplaceWithVeclib split vector llvm.sincos when only sin/cos veclib mappings exist (PR #194639)

Craig Topper via llvm-commits llvm-commits at lists.llvm.org
Wed May 6 18:35:11 PDT 2026


================
@@ -208,19 +208,116 @@ static bool replaceWithCallToVeclib(const TargetLibraryInfo &TLI,
   return true;
 }
 
+/// Returns true when \p TLI has a vector mapping for the scalar function name
+/// \p Name at \p EC (matching either masked or unmasked variants).
+static bool hasVectorMapping(const TargetLibraryInfo &TLI, StringRef Name,
+                             ElementCount EC) {
+  return TLI.getVectorMappingInfo(Name, EC, /*Masked=*/false) ||
+         TLI.getVectorMappingInfo(Name, EC, /*Masked=*/true);
+}
+
+/// Returns true when \p TLI has a vector mapping for \p IID at the given
+/// element type and \p EC.
+static bool hasIntrinsicVectorMapping(const TargetLibraryInfo &TLI,
+                                      Intrinsic::ID IID, Type *ScalarTy,
+                                      ElementCount EC, Module *M) {
+  std::string Name = Intrinsic::getName(IID, {ScalarTy}, M);
+  return hasVectorMapping(TLI, Name, EC);
+}
+
+/// If \p II is a vector llvm.sincos with no direct vector library mapping but
+/// the target does have vector mappings for both llvm.sin and llvm.cos at the
+/// same element count, replace it with separate llvm.sin and llvm.cos calls
+/// and run the standard veclib replacement on each.
+static bool trySplitVectorSinCos(const TargetLibraryInfo &TLI,
+                                 IntrinsicInst *II,
+                                 SmallVectorImpl<Instruction *> &Replaced) {
+  if (II->getIntrinsicID() != Intrinsic::sincos)
+    return false;
+  Value *Arg = II->getArgOperand(0);
+  auto *VTy = dyn_cast<VectorType>(Arg->getType());
+  if (!VTy)
+    return false;
+
+  ElementCount EC = VTy->getElementCount();
+  Type *ScalarTy = VTy->getElementType();
+  Module *M = II->getModule();
+
+  // If a vector sincos mapping exists for the intrinsic name (e.g.
+  // "llvm.sincos.f32") or for the scalar libcall name ("sincos"/"sincosf"),
+  // leave the call alone -- SelectionDAG legalization will handle it via
+  // expandMultipleResultFPLibCall when the runtime libcall impl is enabled.
+  if (hasIntrinsicVectorMapping(TLI, Intrinsic::sincos, ScalarTy, EC, M))
+    return false;
+  StringRef LibcallName;
+  if (ScalarTy->isFloatTy())
+    LibcallName = "sincosf";
+  else if (ScalarTy->isDoubleTy())
+    LibcallName = "sincos";
+  if (!LibcallName.empty() && hasVectorMapping(TLI, LibcallName, EC))
+    return false;
+
+  // Splitting is only worthwhile when both sin and cos have vector mappings.
+  if (!hasIntrinsicVectorMapping(TLI, Intrinsic::sin, ScalarTy, EC, M) ||
+      !hasIntrinsicVectorMapping(TLI, Intrinsic::cos, ScalarTy, EC, M))
+    return false;
+
+  // All users must be extractvalue with index 0 or 1; otherwise we cannot
+  // safely rewire results.
+  for (User *U : II->users()) {
+    auto *EV = dyn_cast<ExtractValueInst>(U);
+    if (!EV || EV->getNumIndices() != 1 ||
+        (EV->getIndices()[0] != 0 && EV->getIndices()[0] != 1))
+      return false;
+  }
+
+  IRBuilder<> B(II);
+  Function *SinFn =
+      Intrinsic::getOrInsertDeclaration(M, Intrinsic::sin, Arg->getType());
+  Function *CosFn =
+      Intrinsic::getOrInsertDeclaration(M, Intrinsic::cos, Arg->getType());
+  CallInst *SinCall = B.CreateCall(SinFn, {Arg}, "sin");
+  CallInst *CosCall = B.CreateCall(CosFn, {Arg}, "cos");
+  SinCall->copyFastMathFlags(II);
+  CosCall->copyFastMathFlags(II);
+
+  // Forward extractvalue uses to the new calls.
+  for (User *U : llvm::make_early_inc_range(II->users())) {
+    auto *EV = cast<ExtractValueInst>(U);
+    EV->replaceAllUsesWith(EV->getIndices()[0] == 0 ? SinCall : CosCall);
+    EV->eraseFromParent();
+  }
+
+  // Replace each new call with the vector library function.
+  if (replaceWithCallToVeclib(TLI, cast<IntrinsicInst>(SinCall)))
+    Replaced.push_back(SinCall);
+  if (replaceWithCallToVeclib(TLI, cast<IntrinsicInst>(CosCall)))
+    Replaced.push_back(CosCall);
+
+  return true;
+}
+
 static bool runImpl(const TargetLibraryInfo &TLI, Function &F) {
   SmallVector<Instruction *> ReplacedCalls;
   for (auto &I : instructions(F)) {
-    // Process only intrinsic calls that return void or a vector.
-    if (auto *II = dyn_cast<IntrinsicInst>(&I)) {
-      if (II->getIntrinsicID() == Intrinsic::not_intrinsic)
-        continue;
-      if (!II->getType()->isVectorTy() && !II->getType()->isVoidTy())
-        continue;
-
-      if (replaceWithCallToVeclib(TLI, II))
-        ReplacedCalls.push_back(&I);
+    auto *II = dyn_cast<IntrinsicInst>(&I);
+    if (!II || II->getIntrinsicID() == Intrinsic::not_intrinsic)
----------------
topperc wrote:

Looks like the dyn_cast only checks that the function name starts with "llvm.". It doesn't check if it is a valid intrinsic.

https://github.com/llvm/llvm-project/pull/194639


More information about the llvm-commits mailing list