[clang] [llvm] [InstCombine] Combine llvm.sin/llvm.cos libcall pairs into llvm.sincos (PR #184760)
Kito Cheng via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 10 08:08:25 PDT 2026
https://github.com/kito-cheng updated https://github.com/llvm/llvm-project/pull/184760
>From 2b6c8bb9bd72ae0c530d37d051a6d285a7d0680d Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at sifive.com>
Date: Fri, 27 Feb 2026 09:49:01 +0800
Subject: [PATCH 1/8] [SimplifyLibCalls] Combine sin/cos libcall pairs into
llvm.sincos
When both sin(x) and cos(x) are called with the same argument in a
function, replace them with a single llvm.sincos intrinsic call.
This is analogous to the existing sinpi/cospi -> sincospi_stret
optimization. The two optimizations now share a unified code path
via optimizeSinCos() with an IsPi flag.
Also remove the completed sincos TODO from Target/README.txt.
---
.../llvm/Transforms/Utils/SimplifyLibCalls.h | 5 +-
llvm/lib/Target/README.txt | 15 --
.../lib/Transforms/Utils/SimplifyLibCalls.cpp | 93 +++++++++---
llvm/test/Transforms/InstCombine/sincos.ll | 136 ++++++++++++++++++
4 files changed, 210 insertions(+), 39 deletions(-)
create mode 100644 llvm/test/Transforms/InstCombine/sincos.ll
diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
index df98131a54ca9..14da88a66fd61 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
@@ -210,6 +210,8 @@ class LibCallSimplifier {
Value *optimizeFMod(CallInst *CI, IRBuilderBase &B);
Value *mergeSqrtToExp(CallInst *CI, IRBuilderBase &B);
Value *optimizeSinCosPi(CallInst *CI, bool IsSin, IRBuilderBase &B);
+ Value *optimizeSinCos(CallInst *CI, bool IsSin, IRBuilderBase &B,
+ bool IsPi = false);
Value *optimizeTrigInversionPairs(CallInst *CI, IRBuilderBase &B);
Value *optimizeSymmetric(CallInst *CI, LibFunc Func, IRBuilderBase &B);
Value *optimizeRemquo(CallInst *CI, IRBuilderBase &B);
@@ -247,7 +249,8 @@ class LibCallSimplifier {
void classifyArgUse(Value *Val, Function *F, bool IsFloat,
SmallVectorImpl<CallInst *> &SinCalls,
SmallVectorImpl<CallInst *> &CosCalls,
- SmallVectorImpl<CallInst *> &SinCosCalls);
+ SmallVectorImpl<CallInst *> &SinCosCalls,
+ bool IsPi);
Value *optimizePrintFString(CallInst *CI, IRBuilderBase &B);
Value *optimizeSPrintFString(CallInst *CI, IRBuilderBase &B);
Value *optimizeSnPrintFString(CallInst *CI, IRBuilderBase &B);
diff --git a/llvm/lib/Target/README.txt b/llvm/lib/Target/README.txt
index adf75c3368677..4d98a5fac3984 100644
--- a/llvm/lib/Target/README.txt
+++ b/llvm/lib/Target/README.txt
@@ -132,21 +132,6 @@ emit:
//===---------------------------------------------------------------------===//
-Combine: a = sin(x), b = cos(x) into a,b = sincos(x).
-
-Expand these to calls of sin/cos and stores:
- double sincos(double x, double *sin, double *cos);
- float sincosf(float x, float *sin, float *cos);
- long double sincosl(long double x, long double *sin, long double *cos);
-
-Doing so could allow SROA of the destination pointers. See also:
-http://gcc.gnu.org/bugzilla/show_bug.cgi?id=17687
-
-This is now easily doable with MRVs. We could even make an intrinsic for this
-if anyone cared enough about sincos.
-
-//===---------------------------------------------------------------------===//
-
quantum_sigma_x in 462.libquantum contains the following loop:
for(i=0; i<reg->size; i++)
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 3b68afe8700dd..4ce540c4376e2 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -2941,9 +2941,10 @@ static bool isTrigLibCall(CallInst *CI) {
return CI->doesNotThrow() && CI->doesNotAccessMemory();
}
-static bool insertSinCosCall(IRBuilderBase &B, Function *OrigCallee, Value *Arg,
- bool UseFloat, Value *&Sin, Value *&Cos,
- Value *&SinCos, const TargetLibraryInfo *TLI) {
+static bool insertSinCosPiCall(IRBuilderBase &B, Function *OrigCallee,
+ Value *Arg, bool UseFloat, Value *&Sin,
+ Value *&Cos, Value *&SinCos,
+ const TargetLibraryInfo *TLI) {
Module *M = OrigCallee->getParent();
Type *ArgTy = Arg->getType();
Type *ResTy;
@@ -3051,7 +3052,13 @@ Value *LibCallSimplifier::optimizeSymmetric(CallInst *CI, LibFunc Func,
}
}
-Value *LibCallSimplifier::optimizeSinCosPi(CallInst *CI, bool IsSin, IRBuilderBase &B) {
+Value *LibCallSimplifier::optimizeSinCosPi(CallInst *CI, bool IsSin,
+ IRBuilderBase &B) {
+ return optimizeSinCos(CI, IsSin, B, /*IsPi=*/true);
+}
+
+Value *LibCallSimplifier::optimizeSinCos(CallInst *CI, bool IsSin,
+ IRBuilderBase &B, bool IsPi) {
// Make sure the prototype is as expected, otherwise the rest of the
// function is probably invalid and likely to abort.
if (!isTrigLibCall(CI))
@@ -3067,21 +3074,41 @@ Value *LibCallSimplifier::optimizeSinCosPi(CallInst *CI, bool IsSin, IRBuilderBa
bool IsFloat = Arg->getType()->isFloatTy();
- // Look for all compatible sinpi, cospi and sincospi calls with the same
+ // Look for all compatible sin/cos (or sinpi/cospi) calls with the same
// argument. If there are enough (in some sense) we can make the
// substitution.
Function *F = CI->getFunction();
for (User *U : Arg->users())
- classifyArgUse(U, F, IsFloat, SinCalls, CosCalls, SinCosCalls);
+ classifyArgUse(U, F, IsFloat, SinCalls, CosCalls, SinCosCalls, IsPi);
- // It's only worthwhile if both sinpi and cospi are actually used.
+ // It's only worthwhile if both sin and cos are actually used.
if (SinCalls.empty() || CosCalls.empty())
return nullptr;
Value *Sin, *Cos, *SinCos;
- if (!insertSinCosCall(B, CI->getCalledFunction(), Arg, IsFloat, Sin, Cos,
- SinCos, TLI))
- return nullptr;
+ if (IsPi) {
+ // For sinpi/cospi, use platform-specific __sincospi_stret libcall.
+ if (!insertSinCosPiCall(B, CI->getCalledFunction(), Arg, IsFloat, Sin, Cos,
+ SinCos, TLI))
+ return nullptr;
+ } else {
+ // For sin/cos, use the llvm.sincos intrinsic.
+ IRBuilderBase::InsertPointGuard Guard(B);
+ if (Instruction *ArgInst = dyn_cast<Instruction>(Arg)) {
+ B.SetInsertPoint(ArgInst->getParent(), ++ArgInst->getIterator());
+ } else {
+ BasicBlock &EntryBB = B.GetInsertBlock()->getParent()->getEntryBlock();
+ B.SetInsertPoint(&EntryBB, EntryBB.begin());
+ }
+
+ Module *M = CI->getModule();
+ Type *ArgTy = Arg->getType();
+ Function *SinCosFunc =
+ Intrinsic::getOrInsertDeclaration(M, Intrinsic::sincos, ArgTy);
+ SinCos = B.CreateCall(SinCosFunc, Arg, "sincos");
+ Sin = B.CreateExtractValue(SinCos, 0, "sin");
+ Cos = B.CreateExtractValue(SinCos, 1, "cos");
+ }
auto replaceTrigInsts = [this](SmallVectorImpl<CallInst *> &Calls,
Value *Res) {
@@ -3100,7 +3127,7 @@ void LibCallSimplifier::classifyArgUse(
Value *Val, Function *F, bool IsFloat,
SmallVectorImpl<CallInst *> &SinCalls,
SmallVectorImpl<CallInst *> &CosCalls,
- SmallVectorImpl<CallInst *> &SinCosCalls) {
+ SmallVectorImpl<CallInst *> &SinCosCalls, bool IsPi) {
auto *CI = dyn_cast<CallInst>(Val);
if (!CI || CI->use_empty())
return;
@@ -3117,20 +3144,29 @@ void LibCallSimplifier::classifyArgUse(
!isTrigLibCall(CI))
return;
- if (IsFloat) {
- if (Func == LibFunc_sinpif)
- SinCalls.push_back(CI);
- else if (Func == LibFunc_cospif)
- CosCalls.push_back(CI);
- else if (Func == LibFunc_sincospif_stret)
- SinCosCalls.push_back(CI);
+ if (IsPi) {
+ if (IsFloat) {
+ if (Func == LibFunc_sinpif)
+ SinCalls.push_back(CI);
+ else if (Func == LibFunc_cospif)
+ CosCalls.push_back(CI);
+ else if (Func == LibFunc_sincospif_stret)
+ SinCosCalls.push_back(CI);
+ } else {
+ if (Func == LibFunc_sinpi)
+ SinCalls.push_back(CI);
+ else if (Func == LibFunc_cospi)
+ CosCalls.push_back(CI);
+ else if (Func == LibFunc_sincospi_stret)
+ SinCosCalls.push_back(CI);
+ }
} else {
- if (Func == LibFunc_sinpi)
+ if (Func == LibFunc_sin || Func == LibFunc_sinf ||
+ Func == LibFunc_sinl)
SinCalls.push_back(CI);
- else if (Func == LibFunc_cospi)
+ else if (Func == LibFunc_cos || Func == LibFunc_cosf ||
+ Func == LibFunc_cosl)
CosCalls.push_back(CI);
- else if (Func == LibFunc_sincospi_stret)
- SinCosCalls.push_back(CI);
}
}
@@ -4029,6 +4065,12 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
case LibFunc_cospif:
case LibFunc_cospi:
return optimizeSinCosPi(CI, /*IsSin*/false, Builder);
+ case LibFunc_sinf:
+ case LibFunc_sinl:
+ return optimizeSinCos(CI, /*IsSin*/true, Builder);
+ case LibFunc_cosf:
+ case LibFunc_cosl:
+ return optimizeSinCos(CI, /*IsSin*/false, Builder);
case LibFunc_powf:
case LibFunc_pow:
case LibFunc_powl:
@@ -4103,8 +4145,13 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
case LibFunc_exp:
case LibFunc_exp10:
case LibFunc_expm1:
- case LibFunc_cos:
case LibFunc_sin:
+ case LibFunc_cos:
+ if (Value *V = optimizeSinCos(CI, Func == LibFunc_sin, Builder))
+ return V;
+ if (UnsafeFPShrink && hasFloatVersion(M, CI->getCalledFunction()->getName()))
+ return optimizeUnaryDoubleFP(CI, Builder, TLI, true);
+ return nullptr;
case LibFunc_tanh:
if (UnsafeFPShrink && hasFloatVersion(M, CI->getCalledFunction()->getName()))
return optimizeUnaryDoubleFP(CI, Builder, TLI, true);
diff --git a/llvm/test/Transforms/InstCombine/sincos.ll b/llvm/test/Transforms/InstCombine/sincos.ll
new file mode 100644
index 0000000000000..00fc060b3287d
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/sincos.ll
@@ -0,0 +1,136 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes=instcombine -S < %s -mtriple=x86_64-apple-macosx10.9 | FileCheck %s --check-prefixes=CHECK,CHECK-DOUBLE-ALIGN8
+; RUN: opt -passes=instcombine -S < %s -mtriple=arm-apple-ios7.0 | FileCheck %s --check-prefixes=CHECK,CHECK-DOUBLE-ALIGN4
+; RUN: opt -passes=instcombine -S < %s -mtriple=x86_64-none-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-DOUBLE-ALIGN8-LINUX
+; REQUIRES: arm-registered-target, x86-registered-target
+
+attributes #0 = { readnone nounwind }
+
+declare float @sinf(float) #0
+declare float @cosf(float) #0
+declare double @sin(double) #0
+declare double @cos(double) #0
+
+ at var32 = global float 0.0
+ at var64 = global double 0.0
+
+; Basic sin+cos combination for float
+define float @sincos_f32() {
+; CHECK-LABEL: @sincos_f32(
+; CHECK-NEXT: [[VAL:%.*]] = load float, ptr @var32, align 4
+; CHECK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[VAL]])
+; CHECK-NEXT: [[SIN:%.*]] = extractvalue { float, float } [[SINCOS]], 0
+; CHECK-NEXT: [[COS:%.*]] = extractvalue { float, float } [[SINCOS]], 1
+; CHECK-NEXT: [[C:%.*]] = call float @cosf(float [[VAL]]) #[[ATTR0:[0-9]+]]
+; CHECK-NEXT: [[RES:%.*]] = fadd float [[SIN]], [[COS]]
+; CHECK-NEXT: ret float [[RES]]
+;
+ %val = load float, ptr @var32
+ %s = call float @sinf(float %val) #0
+ %c = call float @cosf(float %val) #0
+ %res = fadd float %s, %c
+ ret float %res
+}
+
+; Basic sin+cos combination for double
+define double @sincos_f64() {
+; CHECK-DOUBLE-ALIGN8-LABEL: @sincos_f64(
+; CHECK-DOUBLE-ALIGN8-NEXT: [[VAL:%.*]] = load double, ptr @var64, align 8
+; CHECK-DOUBLE-ALIGN8-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[VAL]])
+; CHECK-DOUBLE-ALIGN8-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-DOUBLE-ALIGN8-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
+; CHECK-DOUBLE-ALIGN8-NEXT: [[C:%.*]] = call double @cos(double [[VAL]]) #[[ATTR0]]
+; CHECK-DOUBLE-ALIGN8-NEXT: [[RES:%.*]] = fadd double [[SIN]], [[COS]]
+; CHECK-DOUBLE-ALIGN8-NEXT: ret double [[RES]]
+;
+; CHECK-DOUBLE-ALIGN4-LABEL: @sincos_f64(
+; CHECK-DOUBLE-ALIGN4-NEXT: [[VAL:%.*]] = load double, ptr @var64, align 4
+; CHECK-DOUBLE-ALIGN4-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[VAL]])
+; CHECK-DOUBLE-ALIGN4-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-DOUBLE-ALIGN4-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
+; CHECK-DOUBLE-ALIGN4-NEXT: [[C:%.*]] = call double @cos(double [[VAL]]) #[[ATTR0]]
+; CHECK-DOUBLE-ALIGN4-NEXT: [[RES:%.*]] = fadd double [[SIN]], [[COS]]
+; CHECK-DOUBLE-ALIGN4-NEXT: ret double [[RES]]
+;
+; CHECK-DOUBLE-ALIGN8-LINUX-LABEL: @sincos_f64(
+; CHECK-DOUBLE-ALIGN8-LINUX-NEXT: [[VAL:%.*]] = load double, ptr @var64, align 8
+; CHECK-DOUBLE-ALIGN8-LINUX-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[VAL]])
+; CHECK-DOUBLE-ALIGN8-LINUX-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-DOUBLE-ALIGN8-LINUX-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
+; CHECK-DOUBLE-ALIGN8-LINUX-NEXT: [[C:%.*]] = call double @cos(double [[VAL]]) #[[ATTR0]]
+; CHECK-DOUBLE-ALIGN8-LINUX-NEXT: [[RES:%.*]] = fadd double [[SIN]], [[COS]]
+; CHECK-DOUBLE-ALIGN8-LINUX-NEXT: ret double [[RES]]
+;
+ %val = load double, ptr @var64
+ %s = call double @sin(double %val) #0
+ %c = call double @cos(double %val) #0
+ %res = fadd double %s, %c
+ ret double %res
+}
+
+; Only sin, no cos - should NOT combine
+define float @sin_only_f32(float %x) {
+; CHECK-LABEL: @sin_only_f32(
+; CHECK-NEXT: [[S:%.*]] = call float @sinf(float [[X:%.*]]) #[[ATTR0]]
+; CHECK-NEXT: ret float [[S]]
+;
+ %s = call float @sinf(float %x) #0
+ ret float %s
+}
+
+; Only cos, no sin - should NOT combine
+define float @cos_only_f32(float %x) {
+; CHECK-LABEL: @cos_only_f32(
+; CHECK-NEXT: [[C:%.*]] = call float @cosf(float [[X:%.*]]) #[[ATTR0]]
+; CHECK-NEXT: ret float [[C]]
+;
+ %c = call float @cosf(float %x) #0
+ ret float %c
+}
+
+; Different arguments - should NOT combine
+define float @sincos_different_args(float %x, float %y) {
+; CHECK-LABEL: @sincos_different_args(
+; CHECK-NEXT: [[S:%.*]] = call float @sinf(float [[X:%.*]]) #[[ATTR0]]
+; CHECK-NEXT: [[C:%.*]] = call float @cosf(float [[Y:%.*]]) #[[ATTR0]]
+; CHECK-NEXT: [[RES:%.*]] = fadd float [[S]], [[C]]
+; CHECK-NEXT: ret float [[RES]]
+;
+ %s = call float @sinf(float %x) #0
+ %c = call float @cosf(float %y) #0
+ %res = fadd float %s, %c
+ ret float %res
+}
+
+; Constant argument - should NOT combine
+define float @sincos_const_arg() {
+; CHECK-LABEL: @sincos_const_arg(
+; CHECK-NEXT: [[S:%.*]] = call float @sinf(float 1.000000e+00) #[[ATTR0]]
+; CHECK-NEXT: [[C:%.*]] = call float @cosf(float 1.000000e+00) #[[ATTR0]]
+; CHECK-NEXT: ret float 0x3FF61BBE40000000
+;
+ %s = call float @sinf(float 1.0) #0
+ %c = call float @cosf(float 1.0) #0
+ %res = fadd float %s, %c
+ ret float %res
+}
+
+; Multiple uses of sin and cos results
+define float @sincos_multi_use(float %x) {
+; CHECK-LABEL: @sincos_multi_use(
+; CHECK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[X:%.*]])
+; CHECK-NEXT: [[SIN:%.*]] = extractvalue { float, float } [[SINCOS]], 0
+; CHECK-NEXT: [[COS:%.*]] = extractvalue { float, float } [[SINCOS]], 1
+; CHECK-NEXT: [[C:%.*]] = call float @cosf(float [[X]]) #[[ATTR0]]
+; CHECK-NEXT: [[ADD:%.*]] = fadd float [[SIN]], [[COS]]
+; CHECK-NEXT: [[MUL:%.*]] = fmul float [[SIN]], [[COS]]
+; CHECK-NEXT: [[RES:%.*]] = fadd float [[ADD]], [[MUL]]
+; CHECK-NEXT: ret float [[RES]]
+;
+ %s = call float @sinf(float %x) #0
+ %c = call float @cosf(float %x) #0
+ %add = fadd float %s, %c
+ %mul = fmul float %s, %c
+ %res = fadd float %add, %mul
+ ret float %res
+}
>From 6cbacc50a29f2fe4d2f66bd75ca455dda04dfdcd Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at sifive.com>
Date: Thu, 5 Mar 2026 17:49:50 +0800
Subject: [PATCH 2/8] !fixup: Fix clang-format issues in SimplifyLibCalls
---
.../llvm/Transforms/Utils/SimplifyLibCalls.h | 3 +--
.../lib/Transforms/Utils/SimplifyLibCalls.cpp | 22 +++++++++----------
2 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
index 14da88a66fd61..e174a1c484eec 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
@@ -249,8 +249,7 @@ class LibCallSimplifier {
void classifyArgUse(Value *Val, Function *F, bool IsFloat,
SmallVectorImpl<CallInst *> &SinCalls,
SmallVectorImpl<CallInst *> &CosCalls,
- SmallVectorImpl<CallInst *> &SinCosCalls,
- bool IsPi);
+ SmallVectorImpl<CallInst *> &SinCosCalls, bool IsPi);
Value *optimizePrintFString(CallInst *CI, IRBuilderBase &B);
Value *optimizeSPrintFString(CallInst *CI, IRBuilderBase &B);
Value *optimizeSnPrintFString(CallInst *CI, IRBuilderBase &B);
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 4ce540c4376e2..155a037c9495a 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -3053,7 +3053,7 @@ Value *LibCallSimplifier::optimizeSymmetric(CallInst *CI, LibFunc Func,
}
Value *LibCallSimplifier::optimizeSinCosPi(CallInst *CI, bool IsSin,
- IRBuilderBase &B) {
+ IRBuilderBase &B) {
return optimizeSinCos(CI, IsSin, B, /*IsPi=*/true);
}
@@ -3123,11 +3123,11 @@ Value *LibCallSimplifier::optimizeSinCos(CallInst *CI, bool IsSin,
return IsSin ? Sin : Cos;
}
-void LibCallSimplifier::classifyArgUse(
- Value *Val, Function *F, bool IsFloat,
- SmallVectorImpl<CallInst *> &SinCalls,
- SmallVectorImpl<CallInst *> &CosCalls,
- SmallVectorImpl<CallInst *> &SinCosCalls, bool IsPi) {
+void LibCallSimplifier::classifyArgUse(Value *Val, Function *F, bool IsFloat,
+ SmallVectorImpl<CallInst *> &SinCalls,
+ SmallVectorImpl<CallInst *> &CosCalls,
+ SmallVectorImpl<CallInst *> &SinCosCalls,
+ bool IsPi) {
auto *CI = dyn_cast<CallInst>(Val);
if (!CI || CI->use_empty())
return;
@@ -3161,8 +3161,7 @@ void LibCallSimplifier::classifyArgUse(
SinCosCalls.push_back(CI);
}
} else {
- if (Func == LibFunc_sin || Func == LibFunc_sinf ||
- Func == LibFunc_sinl)
+ if (Func == LibFunc_sin || Func == LibFunc_sinf || Func == LibFunc_sinl)
SinCalls.push_back(CI);
else if (Func == LibFunc_cos || Func == LibFunc_cosf ||
Func == LibFunc_cosl)
@@ -4067,10 +4066,10 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
return optimizeSinCosPi(CI, /*IsSin*/false, Builder);
case LibFunc_sinf:
case LibFunc_sinl:
- return optimizeSinCos(CI, /*IsSin*/true, Builder);
+ return optimizeSinCos(CI, /*IsSin*/ true, Builder);
case LibFunc_cosf:
case LibFunc_cosl:
- return optimizeSinCos(CI, /*IsSin*/false, Builder);
+ return optimizeSinCos(CI, /*IsSin*/ false, Builder);
case LibFunc_powf:
case LibFunc_pow:
case LibFunc_powl:
@@ -4149,7 +4148,8 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
case LibFunc_cos:
if (Value *V = optimizeSinCos(CI, Func == LibFunc_sin, Builder))
return V;
- if (UnsafeFPShrink && hasFloatVersion(M, CI->getCalledFunction()->getName()))
+ if (UnsafeFPShrink &&
+ hasFloatVersion(M, CI->getCalledFunction()->getName()))
return optimizeUnaryDoubleFP(CI, Builder, TLI, true);
return nullptr;
case LibFunc_tanh:
>From c998b245164c17e9845947656f448c0e1f1a7786 Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at sifive.com>
Date: Fri, 6 Mar 2026 18:38:24 +0800
Subject: [PATCH 3/8] !fixup: Address review feedback on sincos.ll test
- Move attributes block to end of file, use memory(none)
- Add intrinsic-only and mixed libcall/intrinsic test cases
---
llvm/test/Transforms/InstCombine/sincos.ll | 46 +++++++++++++++++++++-
1 file changed, 44 insertions(+), 2 deletions(-)
diff --git a/llvm/test/Transforms/InstCombine/sincos.ll b/llvm/test/Transforms/InstCombine/sincos.ll
index 00fc060b3287d..8485f66b348f3 100644
--- a/llvm/test/Transforms/InstCombine/sincos.ll
+++ b/llvm/test/Transforms/InstCombine/sincos.ll
@@ -4,8 +4,6 @@
; RUN: opt -passes=instcombine -S < %s -mtriple=x86_64-none-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-DOUBLE-ALIGN8-LINUX
; REQUIRES: arm-registered-target, x86-registered-target
-attributes #0 = { readnone nounwind }
-
declare float @sinf(float) #0
declare float @cosf(float) #0
declare double @sin(double) #0
@@ -134,3 +132,47 @@ define float @sincos_multi_use(float %x) {
%res = fadd float %add, %mul
ret float %res
}
+
+; Intrinsic sin + intrinsic cos - should combine
+define float @sincos_intrinsic_f32(float %x) {
+; CHECK-LABEL: @sincos_intrinsic_f32(
+; CHECK-NEXT: [[S:%.*]] = call float @llvm.sin.f32(float [[X:%.*]])
+; CHECK-NEXT: [[C:%.*]] = call float @llvm.cos.f32(float [[X]])
+; CHECK-NEXT: [[RES:%.*]] = fadd float [[S]], [[C]]
+; CHECK-NEXT: ret float [[RES]]
+;
+ %s = call float @llvm.sin.f32(float %x)
+ %c = call float @llvm.cos.f32(float %x)
+ %res = fadd float %s, %c
+ ret float %res
+}
+
+; Mixed: libcall sin + intrinsic cos
+define float @sincos_mixed_libcall_sin_intrinsic_cos(float %x) {
+; CHECK-LABEL: @sincos_mixed_libcall_sin_intrinsic_cos(
+; CHECK-NEXT: [[S:%.*]] = call float @sinf(float [[X:%.*]]) #[[ATTR0]]
+; CHECK-NEXT: [[C:%.*]] = call float @llvm.cos.f32(float [[X]])
+; CHECK-NEXT: [[RES:%.*]] = fadd float [[S]], [[C]]
+; CHECK-NEXT: ret float [[RES]]
+;
+ %s = call float @sinf(float %x) #0
+ %c = call float @llvm.cos.f32(float %x)
+ %res = fadd float %s, %c
+ ret float %res
+}
+
+; Mixed: intrinsic sin + libcall cos
+define float @sincos_mixed_intrinsic_sin_libcall_cos(float %x) {
+; CHECK-LABEL: @sincos_mixed_intrinsic_sin_libcall_cos(
+; CHECK-NEXT: [[S:%.*]] = call float @llvm.sin.f32(float [[X:%.*]])
+; CHECK-NEXT: [[C:%.*]] = call float @cosf(float [[X]]) #[[ATTR0]]
+; CHECK-NEXT: [[RES:%.*]] = fadd float [[S]], [[C]]
+; CHECK-NEXT: ret float [[RES]]
+;
+ %s = call float @llvm.sin.f32(float %x)
+ %c = call float @cosf(float %x) #0
+ %res = fadd float %s, %c
+ ret float %res
+}
+
+attributes #0 = { nounwind memory(none) }
>From 8d76d9c774ff8e8c2f4fa6461d8661e6dee0ed34 Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at sifive.com>
Date: Fri, 6 Mar 2026 21:47:06 +0800
Subject: [PATCH 4/8] Refactor sincos optimization: convert sin/cos libcalls to
intrinsics first
Instead of combining sin/cos libcall pairs directly into llvm.sincos in
SimplifyLibCalls, split the optimization into two phases:
1. SimplifyLibCalls: Convert sin/cos libcalls to llvm.sin/llvm.cos
intrinsics (similar to fabs -> llvm.fabs). UnsafeFPShrink narrowing
(double -> float) is attempted first for LibFunc_sin/LibFunc_cos.
2. InstCombineCalls: Combine llvm.sin(x) + llvm.cos(x) intrinsic pairs
into a single llvm.sincos(x) call via foldSinCosToSinCos().
This approach unifies sincos merging at the intrinsic level, enabling
combination of libcall pairs, intrinsic pairs, and mixed cases. The
sinpi/cospi path in SimplifyLibCalls remains unchanged.
---
.../llvm/Transforms/Utils/SimplifyLibCalls.h | 4 +-
.../InstCombine/InstCombineCalls.cpp | 72 ++++++++
.../lib/Transforms/Utils/SimplifyLibCalls.cpp | 104 ++++-------
llvm/test/Transforms/InstCombine/sincos.ll | 167 +++++++++++++++---
4 files changed, 247 insertions(+), 100 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
index e174a1c484eec..df98131a54ca9 100644
--- a/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
+++ b/llvm/include/llvm/Transforms/Utils/SimplifyLibCalls.h
@@ -210,8 +210,6 @@ class LibCallSimplifier {
Value *optimizeFMod(CallInst *CI, IRBuilderBase &B);
Value *mergeSqrtToExp(CallInst *CI, IRBuilderBase &B);
Value *optimizeSinCosPi(CallInst *CI, bool IsSin, IRBuilderBase &B);
- Value *optimizeSinCos(CallInst *CI, bool IsSin, IRBuilderBase &B,
- bool IsPi = false);
Value *optimizeTrigInversionPairs(CallInst *CI, IRBuilderBase &B);
Value *optimizeSymmetric(CallInst *CI, LibFunc Func, IRBuilderBase &B);
Value *optimizeRemquo(CallInst *CI, IRBuilderBase &B);
@@ -249,7 +247,7 @@ class LibCallSimplifier {
void classifyArgUse(Value *Val, Function *F, bool IsFloat,
SmallVectorImpl<CallInst *> &SinCalls,
SmallVectorImpl<CallInst *> &CosCalls,
- SmallVectorImpl<CallInst *> &SinCosCalls, bool IsPi);
+ SmallVectorImpl<CallInst *> &SinCosCalls);
Value *optimizePrintFString(CallInst *CI, IRBuilderBase &B);
Value *optimizeSPrintFString(CallInst *CI, IRBuilderBase &B);
Value *optimizeSnPrintFString(CallInst *CI, IRBuilderBase &B);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 3585c787bb880..31efc85723719 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -1879,6 +1879,62 @@ static Instruction *foldNeonShift(IntrinsicInst *II, InstCombinerImpl &IC) {
return IC.replaceInstUsesWith(*II, Result);
}
+// If II is llvm.sin(x) or llvm.cos(x), and there is a matching
+// llvm.cos(x) or llvm.sin(x) using the same argument, combine them
+// into a single llvm.sincos(x) call. Returns the sin or cos result
+// extracted from sincos via ResultForII, and the matched instruction
+// via MatchedInst, or returns false if no match is found.
+static bool foldSinCosToSinCos(IntrinsicInst *II, IRBuilderBase &B,
+ Value *&ResultForII,
+ IntrinsicInst *&MatchedInst) {
+ Intrinsic::ID IID = II->getIntrinsicID();
+ bool IsSin = (IID == Intrinsic::sin);
+ Intrinsic::ID MatchID = IsSin ? Intrinsic::cos : Intrinsic::sin;
+
+ Value *Arg = II->getArgOperand(0);
+
+ // Don't bother looking through uses of constants.
+ if (isa<Constant>(Arg))
+ return false;
+
+ // Look for a matching cos/sin intrinsic with the same argument.
+ IntrinsicInst *Match = nullptr;
+ for (User *U : Arg->users()) {
+ if (auto *Cand = dyn_cast<IntrinsicInst>(U)) {
+ if (Cand != II && !Cand->use_empty() &&
+ Cand->getIntrinsicID() == MatchID &&
+ Cand->getFunction() == II->getFunction()) {
+ Match = Cand;
+ break;
+ }
+ }
+ }
+
+ if (!Match)
+ return false;
+
+ // Insert sincos right after the argument definition.
+ IRBuilderBase::InsertPointGuard Guard(B);
+ if (auto *ArgInst = dyn_cast<Instruction>(Arg))
+ B.SetInsertPoint(ArgInst->getParent(), std::next(ArgInst->getIterator()));
+ else {
+ BasicBlock &EntryBB = II->getFunction()->getEntryBlock();
+ B.SetInsertPoint(&EntryBB, EntryBB.begin());
+ }
+
+ Function *SinCosFunc = Intrinsic::getOrInsertDeclaration(
+ II->getModule(), Intrinsic::sincos, Arg->getType());
+ Value *SinCos = B.CreateCall(SinCosFunc, Arg, "sincos");
+ Value *Sin = B.CreateExtractValue(SinCos, 0, "sin");
+ Value *Cos = B.CreateExtractValue(SinCos, 1, "cos");
+
+ // Replace the matching call and return both results.
+ Match->replaceAllUsesWith(IsSin ? Cos : Sin);
+ MatchedInst = Match;
+ ResultForII = IsSin ? Sin : Cos;
+ return true;
+}
+
/// CallInst simplification. This mostly only handles folding of intrinsic
/// instructions. For normal calls, it allows visitCallBase to do the heavy
/// lifting.
@@ -3179,6 +3235,14 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
// for f in {cos, cosh}
return replaceOperand(*II, 0, X);
}
+ if (IID == Intrinsic::cos) {
+ Value *Result;
+ IntrinsicInst *Match;
+ if (foldSinCosToSinCos(II, Builder, Result, Match)) {
+ eraseInstFromFunction(*Match);
+ return replaceInstUsesWith(*II, Result);
+ }
+ }
break;
}
case Intrinsic::sin:
@@ -3193,6 +3257,14 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
Value *NewFunc = Builder.CreateUnaryIntrinsic(IID, X, II);
return UnaryOperator::CreateFNegFMF(NewFunc, II);
}
+ if (IID == Intrinsic::sin) {
+ Value *Result;
+ IntrinsicInst *Match;
+ if (foldSinCosToSinCos(II, Builder, Result, Match)) {
+ eraseInstFromFunction(*Match);
+ return replaceInstUsesWith(*II, Result);
+ }
+ }
break;
}
case Intrinsic::ldexp: {
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 155a037c9495a..48f4eda1d7638 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -2941,10 +2941,9 @@ static bool isTrigLibCall(CallInst *CI) {
return CI->doesNotThrow() && CI->doesNotAccessMemory();
}
-static bool insertSinCosPiCall(IRBuilderBase &B, Function *OrigCallee,
- Value *Arg, bool UseFloat, Value *&Sin,
- Value *&Cos, Value *&SinCos,
- const TargetLibraryInfo *TLI) {
+static bool insertSinCosCall(IRBuilderBase &B, Function *OrigCallee, Value *Arg,
+ bool UseFloat, Value *&Sin, Value *&Cos,
+ Value *&SinCos, const TargetLibraryInfo *TLI) {
Module *M = OrigCallee->getParent();
Type *ArgTy = Arg->getType();
Type *ResTy;
@@ -3054,11 +3053,6 @@ Value *LibCallSimplifier::optimizeSymmetric(CallInst *CI, LibFunc Func,
Value *LibCallSimplifier::optimizeSinCosPi(CallInst *CI, bool IsSin,
IRBuilderBase &B) {
- return optimizeSinCos(CI, IsSin, B, /*IsPi=*/true);
-}
-
-Value *LibCallSimplifier::optimizeSinCos(CallInst *CI, bool IsSin,
- IRBuilderBase &B, bool IsPi) {
// Make sure the prototype is as expected, otherwise the rest of the
// function is probably invalid and likely to abort.
if (!isTrigLibCall(CI))
@@ -3074,41 +3068,21 @@ Value *LibCallSimplifier::optimizeSinCos(CallInst *CI, bool IsSin,
bool IsFloat = Arg->getType()->isFloatTy();
- // Look for all compatible sin/cos (or sinpi/cospi) calls with the same
+ // Look for all compatible sinpi, cospi and sincospi calls with the same
// argument. If there are enough (in some sense) we can make the
// substitution.
Function *F = CI->getFunction();
for (User *U : Arg->users())
- classifyArgUse(U, F, IsFloat, SinCalls, CosCalls, SinCosCalls, IsPi);
+ classifyArgUse(U, F, IsFloat, SinCalls, CosCalls, SinCosCalls);
- // It's only worthwhile if both sin and cos are actually used.
+ // It's only worthwhile if both sinpi and cospi are actually used.
if (SinCalls.empty() || CosCalls.empty())
return nullptr;
Value *Sin, *Cos, *SinCos;
- if (IsPi) {
- // For sinpi/cospi, use platform-specific __sincospi_stret libcall.
- if (!insertSinCosPiCall(B, CI->getCalledFunction(), Arg, IsFloat, Sin, Cos,
- SinCos, TLI))
- return nullptr;
- } else {
- // For sin/cos, use the llvm.sincos intrinsic.
- IRBuilderBase::InsertPointGuard Guard(B);
- if (Instruction *ArgInst = dyn_cast<Instruction>(Arg)) {
- B.SetInsertPoint(ArgInst->getParent(), ++ArgInst->getIterator());
- } else {
- BasicBlock &EntryBB = B.GetInsertBlock()->getParent()->getEntryBlock();
- B.SetInsertPoint(&EntryBB, EntryBB.begin());
- }
-
- Module *M = CI->getModule();
- Type *ArgTy = Arg->getType();
- Function *SinCosFunc =
- Intrinsic::getOrInsertDeclaration(M, Intrinsic::sincos, ArgTy);
- SinCos = B.CreateCall(SinCosFunc, Arg, "sincos");
- Sin = B.CreateExtractValue(SinCos, 0, "sin");
- Cos = B.CreateExtractValue(SinCos, 1, "cos");
- }
+ if (!insertSinCosCall(B, CI->getCalledFunction(), Arg, IsFloat, Sin, Cos,
+ SinCos, TLI))
+ return nullptr;
auto replaceTrigInsts = [this](SmallVectorImpl<CallInst *> &Calls,
Value *Res) {
@@ -3123,11 +3097,11 @@ Value *LibCallSimplifier::optimizeSinCos(CallInst *CI, bool IsSin,
return IsSin ? Sin : Cos;
}
-void LibCallSimplifier::classifyArgUse(Value *Val, Function *F, bool IsFloat,
- SmallVectorImpl<CallInst *> &SinCalls,
- SmallVectorImpl<CallInst *> &CosCalls,
- SmallVectorImpl<CallInst *> &SinCosCalls,
- bool IsPi) {
+void LibCallSimplifier::classifyArgUse(
+ Value *Val, Function *F, bool IsFloat,
+ SmallVectorImpl<CallInst *> &SinCalls,
+ SmallVectorImpl<CallInst *> &CosCalls,
+ SmallVectorImpl<CallInst *> &SinCosCalls) {
auto *CI = dyn_cast<CallInst>(Val);
if (!CI || CI->use_empty())
return;
@@ -3144,28 +3118,20 @@ void LibCallSimplifier::classifyArgUse(Value *Val, Function *F, bool IsFloat,
!isTrigLibCall(CI))
return;
- if (IsPi) {
- if (IsFloat) {
- if (Func == LibFunc_sinpif)
- SinCalls.push_back(CI);
- else if (Func == LibFunc_cospif)
- CosCalls.push_back(CI);
- else if (Func == LibFunc_sincospif_stret)
- SinCosCalls.push_back(CI);
- } else {
- if (Func == LibFunc_sinpi)
- SinCalls.push_back(CI);
- else if (Func == LibFunc_cospi)
- CosCalls.push_back(CI);
- else if (Func == LibFunc_sincospi_stret)
- SinCosCalls.push_back(CI);
- }
+ if (IsFloat) {
+ if (Func == LibFunc_sinpif)
+ SinCalls.push_back(CI);
+ else if (Func == LibFunc_cospif)
+ CosCalls.push_back(CI);
+ else if (Func == LibFunc_sincospif_stret)
+ SinCosCalls.push_back(CI);
} else {
- if (Func == LibFunc_sin || Func == LibFunc_sinf || Func == LibFunc_sinl)
+ if (Func == LibFunc_sinpi)
SinCalls.push_back(CI);
- else if (Func == LibFunc_cos || Func == LibFunc_cosf ||
- Func == LibFunc_cosl)
+ else if (Func == LibFunc_cospi)
CosCalls.push_back(CI);
+ else if (Func == LibFunc_sincospi_stret)
+ SinCosCalls.push_back(CI);
}
}
@@ -4066,10 +4032,10 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
return optimizeSinCosPi(CI, /*IsSin*/false, Builder);
case LibFunc_sinf:
case LibFunc_sinl:
- return optimizeSinCos(CI, /*IsSin*/ true, Builder);
+ return replaceUnaryCall(CI, Builder, Intrinsic::sin);
case LibFunc_cosf:
case LibFunc_cosl:
- return optimizeSinCos(CI, /*IsSin*/ false, Builder);
+ return replaceUnaryCall(CI, Builder, Intrinsic::cos);
case LibFunc_powf:
case LibFunc_pow:
case LibFunc_powl:
@@ -4136,6 +4102,14 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
return replaceUnaryCall(CI, Builder, Intrinsic::rint);
case LibFunc_trunc:
return replaceUnaryCall(CI, Builder, Intrinsic::trunc);
+ case LibFunc_sin:
+ case LibFunc_cos:
+ if (UnsafeFPShrink &&
+ hasFloatVersion(M, CI->getCalledFunction()->getName()))
+ if (Value *V = optimizeUnaryDoubleFP(CI, Builder, TLI, true))
+ return V;
+ return replaceUnaryCall(
+ CI, Builder, Func == LibFunc_sin ? Intrinsic::sin : Intrinsic::cos);
case LibFunc_acos:
case LibFunc_acosh:
case LibFunc_asin:
@@ -4144,14 +4118,6 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
case LibFunc_exp:
case LibFunc_exp10:
case LibFunc_expm1:
- case LibFunc_sin:
- case LibFunc_cos:
- if (Value *V = optimizeSinCos(CI, Func == LibFunc_sin, Builder))
- return V;
- if (UnsafeFPShrink &&
- hasFloatVersion(M, CI->getCalledFunction()->getName()))
- return optimizeUnaryDoubleFP(CI, Builder, TLI, true);
- return nullptr;
case LibFunc_tanh:
if (UnsafeFPShrink && hasFloatVersion(M, CI->getCalledFunction()->getName()))
return optimizeUnaryDoubleFP(CI, Builder, TLI, true);
diff --git a/llvm/test/Transforms/InstCombine/sincos.ll b/llvm/test/Transforms/InstCombine/sincos.ll
index 8485f66b348f3..ced643c349fde 100644
--- a/llvm/test/Transforms/InstCombine/sincos.ll
+++ b/llvm/test/Transforms/InstCombine/sincos.ll
@@ -2,6 +2,7 @@
; RUN: opt -passes=instcombine -S < %s -mtriple=x86_64-apple-macosx10.9 | FileCheck %s --check-prefixes=CHECK,CHECK-DOUBLE-ALIGN8
; RUN: opt -passes=instcombine -S < %s -mtriple=arm-apple-ios7.0 | FileCheck %s --check-prefixes=CHECK,CHECK-DOUBLE-ALIGN4
; RUN: opt -passes=instcombine -S < %s -mtriple=x86_64-none-linux-gnu | FileCheck %s --check-prefixes=CHECK,CHECK-DOUBLE-ALIGN8-LINUX
+; RUN: opt -passes=instcombine -S < %s -mtriple=x86_64-apple-macosx10.9 -enable-double-float-shrink | FileCheck %s --check-prefixes=CHECK-SHRINK
; REQUIRES: arm-registered-target, x86-registered-target
declare float @sinf(float) #0
@@ -19,9 +20,16 @@ define float @sincos_f32() {
; CHECK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[VAL]])
; CHECK-NEXT: [[SIN:%.*]] = extractvalue { float, float } [[SINCOS]], 0
; CHECK-NEXT: [[COS:%.*]] = extractvalue { float, float } [[SINCOS]], 1
-; CHECK-NEXT: [[C:%.*]] = call float @cosf(float [[VAL]]) #[[ATTR0:[0-9]+]]
; CHECK-NEXT: [[RES:%.*]] = fadd float [[SIN]], [[COS]]
; CHECK-NEXT: ret float [[RES]]
+;
+; CHECK-SHRINK-LABEL: @sincos_f32(
+; CHECK-SHRINK-NEXT: [[VAL:%.*]] = load float, ptr @var32, align 4
+; CHECK-SHRINK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[VAL]])
+; CHECK-SHRINK-NEXT: [[SIN:%.*]] = extractvalue { float, float } [[SINCOS]], 0
+; CHECK-SHRINK-NEXT: [[COS:%.*]] = extractvalue { float, float } [[SINCOS]], 1
+; CHECK-SHRINK-NEXT: [[RES:%.*]] = fadd float [[SIN]], [[COS]]
+; CHECK-SHRINK-NEXT: ret float [[RES]]
;
%val = load float, ptr @var32
%s = call float @sinf(float %val) #0
@@ -37,7 +45,6 @@ define double @sincos_f64() {
; CHECK-DOUBLE-ALIGN8-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[VAL]])
; CHECK-DOUBLE-ALIGN8-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
; CHECK-DOUBLE-ALIGN8-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
-; CHECK-DOUBLE-ALIGN8-NEXT: [[C:%.*]] = call double @cos(double [[VAL]]) #[[ATTR0]]
; CHECK-DOUBLE-ALIGN8-NEXT: [[RES:%.*]] = fadd double [[SIN]], [[COS]]
; CHECK-DOUBLE-ALIGN8-NEXT: ret double [[RES]]
;
@@ -46,7 +53,6 @@ define double @sincos_f64() {
; CHECK-DOUBLE-ALIGN4-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[VAL]])
; CHECK-DOUBLE-ALIGN4-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
; CHECK-DOUBLE-ALIGN4-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
-; CHECK-DOUBLE-ALIGN4-NEXT: [[C:%.*]] = call double @cos(double [[VAL]]) #[[ATTR0]]
; CHECK-DOUBLE-ALIGN4-NEXT: [[RES:%.*]] = fadd double [[SIN]], [[COS]]
; CHECK-DOUBLE-ALIGN4-NEXT: ret double [[RES]]
;
@@ -55,9 +61,16 @@ define double @sincos_f64() {
; CHECK-DOUBLE-ALIGN8-LINUX-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[VAL]])
; CHECK-DOUBLE-ALIGN8-LINUX-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
; CHECK-DOUBLE-ALIGN8-LINUX-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
-; CHECK-DOUBLE-ALIGN8-LINUX-NEXT: [[C:%.*]] = call double @cos(double [[VAL]]) #[[ATTR0]]
; CHECK-DOUBLE-ALIGN8-LINUX-NEXT: [[RES:%.*]] = fadd double [[SIN]], [[COS]]
; CHECK-DOUBLE-ALIGN8-LINUX-NEXT: ret double [[RES]]
+;
+; CHECK-SHRINK-LABEL: @sincos_f64(
+; CHECK-SHRINK-NEXT: [[VAL:%.*]] = load double, ptr @var64, align 8
+; CHECK-SHRINK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[VAL]])
+; CHECK-SHRINK-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-SHRINK-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
+; CHECK-SHRINK-NEXT: [[RES:%.*]] = fadd double [[SIN]], [[COS]]
+; CHECK-SHRINK-NEXT: ret double [[RES]]
;
%val = load double, ptr @var64
%s = call double @sin(double %val) #0
@@ -69,8 +82,12 @@ define double @sincos_f64() {
; Only sin, no cos - should NOT combine
define float @sin_only_f32(float %x) {
; CHECK-LABEL: @sin_only_f32(
-; CHECK-NEXT: [[S:%.*]] = call float @sinf(float [[X:%.*]]) #[[ATTR0]]
+; CHECK-NEXT: [[S:%.*]] = call float @llvm.sin.f32(float [[X:%.*]])
; CHECK-NEXT: ret float [[S]]
+;
+; CHECK-SHRINK-LABEL: @sin_only_f32(
+; CHECK-SHRINK-NEXT: [[S:%.*]] = call float @llvm.sin.f32(float [[X:%.*]])
+; CHECK-SHRINK-NEXT: ret float [[S]]
;
%s = call float @sinf(float %x) #0
ret float %s
@@ -79,8 +96,12 @@ define float @sin_only_f32(float %x) {
; Only cos, no sin - should NOT combine
define float @cos_only_f32(float %x) {
; CHECK-LABEL: @cos_only_f32(
-; CHECK-NEXT: [[C:%.*]] = call float @cosf(float [[X:%.*]]) #[[ATTR0]]
+; CHECK-NEXT: [[C:%.*]] = call float @llvm.cos.f32(float [[X:%.*]])
; CHECK-NEXT: ret float [[C]]
+;
+; CHECK-SHRINK-LABEL: @cos_only_f32(
+; CHECK-SHRINK-NEXT: [[C:%.*]] = call float @llvm.cos.f32(float [[X:%.*]])
+; CHECK-SHRINK-NEXT: ret float [[C]]
;
%c = call float @cosf(float %x) #0
ret float %c
@@ -89,10 +110,16 @@ define float @cos_only_f32(float %x) {
; Different arguments - should NOT combine
define float @sincos_different_args(float %x, float %y) {
; CHECK-LABEL: @sincos_different_args(
-; CHECK-NEXT: [[S:%.*]] = call float @sinf(float [[X:%.*]]) #[[ATTR0]]
-; CHECK-NEXT: [[C:%.*]] = call float @cosf(float [[Y:%.*]]) #[[ATTR0]]
+; CHECK-NEXT: [[S:%.*]] = call float @llvm.sin.f32(float [[X:%.*]])
+; CHECK-NEXT: [[C:%.*]] = call float @llvm.cos.f32(float [[Y:%.*]])
; CHECK-NEXT: [[RES:%.*]] = fadd float [[S]], [[C]]
; CHECK-NEXT: ret float [[RES]]
+;
+; CHECK-SHRINK-LABEL: @sincos_different_args(
+; CHECK-SHRINK-NEXT: [[S:%.*]] = call float @llvm.sin.f32(float [[X:%.*]])
+; CHECK-SHRINK-NEXT: [[C:%.*]] = call float @llvm.cos.f32(float [[Y:%.*]])
+; CHECK-SHRINK-NEXT: [[RES:%.*]] = fadd float [[S]], [[C]]
+; CHECK-SHRINK-NEXT: ret float [[RES]]
;
%s = call float @sinf(float %x) #0
%c = call float @cosf(float %y) #0
@@ -100,30 +127,25 @@ define float @sincos_different_args(float %x, float %y) {
ret float %res
}
-; Constant argument - should NOT combine
-define float @sincos_const_arg() {
-; CHECK-LABEL: @sincos_const_arg(
-; CHECK-NEXT: [[S:%.*]] = call float @sinf(float 1.000000e+00) #[[ATTR0]]
-; CHECK-NEXT: [[C:%.*]] = call float @cosf(float 1.000000e+00) #[[ATTR0]]
-; CHECK-NEXT: ret float 0x3FF61BBE40000000
-;
- %s = call float @sinf(float 1.0) #0
- %c = call float @cosf(float 1.0) #0
- %res = fadd float %s, %c
- ret float %res
-}
-
; Multiple uses of sin and cos results
define float @sincos_multi_use(float %x) {
; CHECK-LABEL: @sincos_multi_use(
; CHECK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[X:%.*]])
; CHECK-NEXT: [[SIN:%.*]] = extractvalue { float, float } [[SINCOS]], 0
; CHECK-NEXT: [[COS:%.*]] = extractvalue { float, float } [[SINCOS]], 1
-; CHECK-NEXT: [[C:%.*]] = call float @cosf(float [[X]]) #[[ATTR0]]
; CHECK-NEXT: [[ADD:%.*]] = fadd float [[SIN]], [[COS]]
; CHECK-NEXT: [[MUL:%.*]] = fmul float [[SIN]], [[COS]]
; CHECK-NEXT: [[RES:%.*]] = fadd float [[ADD]], [[MUL]]
; CHECK-NEXT: ret float [[RES]]
+;
+; CHECK-SHRINK-LABEL: @sincos_multi_use(
+; CHECK-SHRINK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[X:%.*]])
+; CHECK-SHRINK-NEXT: [[SIN:%.*]] = extractvalue { float, float } [[SINCOS]], 0
+; CHECK-SHRINK-NEXT: [[COS:%.*]] = extractvalue { float, float } [[SINCOS]], 1
+; CHECK-SHRINK-NEXT: [[ADD:%.*]] = fadd float [[SIN]], [[COS]]
+; CHECK-SHRINK-NEXT: [[MUL:%.*]] = fmul float [[SIN]], [[COS]]
+; CHECK-SHRINK-NEXT: [[RES:%.*]] = fadd float [[ADD]], [[MUL]]
+; CHECK-SHRINK-NEXT: ret float [[RES]]
;
%s = call float @sinf(float %x) #0
%c = call float @cosf(float %x) #0
@@ -136,10 +158,18 @@ define float @sincos_multi_use(float %x) {
; Intrinsic sin + intrinsic cos - should combine
define float @sincos_intrinsic_f32(float %x) {
; CHECK-LABEL: @sincos_intrinsic_f32(
-; CHECK-NEXT: [[S:%.*]] = call float @llvm.sin.f32(float [[X:%.*]])
-; CHECK-NEXT: [[C:%.*]] = call float @llvm.cos.f32(float [[X]])
+; CHECK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[X:%.*]])
+; CHECK-NEXT: [[S:%.*]] = extractvalue { float, float } [[SINCOS]], 0
+; CHECK-NEXT: [[C:%.*]] = extractvalue { float, float } [[SINCOS]], 1
; CHECK-NEXT: [[RES:%.*]] = fadd float [[S]], [[C]]
; CHECK-NEXT: ret float [[RES]]
+;
+; CHECK-SHRINK-LABEL: @sincos_intrinsic_f32(
+; CHECK-SHRINK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[X:%.*]])
+; CHECK-SHRINK-NEXT: [[SIN:%.*]] = extractvalue { float, float } [[SINCOS]], 0
+; CHECK-SHRINK-NEXT: [[COS:%.*]] = extractvalue { float, float } [[SINCOS]], 1
+; CHECK-SHRINK-NEXT: [[RES:%.*]] = fadd float [[SIN]], [[COS]]
+; CHECK-SHRINK-NEXT: ret float [[RES]]
;
%s = call float @llvm.sin.f32(float %x)
%c = call float @llvm.cos.f32(float %x)
@@ -150,10 +180,18 @@ define float @sincos_intrinsic_f32(float %x) {
; Mixed: libcall sin + intrinsic cos
define float @sincos_mixed_libcall_sin_intrinsic_cos(float %x) {
; CHECK-LABEL: @sincos_mixed_libcall_sin_intrinsic_cos(
-; CHECK-NEXT: [[S:%.*]] = call float @sinf(float [[X:%.*]]) #[[ATTR0]]
-; CHECK-NEXT: [[C:%.*]] = call float @llvm.cos.f32(float [[X]])
+; CHECK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[X:%.*]])
+; CHECK-NEXT: [[S:%.*]] = extractvalue { float, float } [[SINCOS]], 0
+; CHECK-NEXT: [[C:%.*]] = extractvalue { float, float } [[SINCOS]], 1
; CHECK-NEXT: [[RES:%.*]] = fadd float [[S]], [[C]]
; CHECK-NEXT: ret float [[RES]]
+;
+; CHECK-SHRINK-LABEL: @sincos_mixed_libcall_sin_intrinsic_cos(
+; CHECK-SHRINK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[X:%.*]])
+; CHECK-SHRINK-NEXT: [[SIN:%.*]] = extractvalue { float, float } [[SINCOS]], 0
+; CHECK-SHRINK-NEXT: [[COS:%.*]] = extractvalue { float, float } [[SINCOS]], 1
+; CHECK-SHRINK-NEXT: [[RES:%.*]] = fadd float [[SIN]], [[COS]]
+; CHECK-SHRINK-NEXT: ret float [[RES]]
;
%s = call float @sinf(float %x) #0
%c = call float @llvm.cos.f32(float %x)
@@ -164,10 +202,18 @@ define float @sincos_mixed_libcall_sin_intrinsic_cos(float %x) {
; Mixed: intrinsic sin + libcall cos
define float @sincos_mixed_intrinsic_sin_libcall_cos(float %x) {
; CHECK-LABEL: @sincos_mixed_intrinsic_sin_libcall_cos(
-; CHECK-NEXT: [[S:%.*]] = call float @llvm.sin.f32(float [[X:%.*]])
-; CHECK-NEXT: [[C:%.*]] = call float @cosf(float [[X]]) #[[ATTR0]]
+; CHECK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[X:%.*]])
+; CHECK-NEXT: [[S:%.*]] = extractvalue { float, float } [[SINCOS]], 0
+; CHECK-NEXT: [[C:%.*]] = extractvalue { float, float } [[SINCOS]], 1
; CHECK-NEXT: [[RES:%.*]] = fadd float [[S]], [[C]]
; CHECK-NEXT: ret float [[RES]]
+;
+; CHECK-SHRINK-LABEL: @sincos_mixed_intrinsic_sin_libcall_cos(
+; CHECK-SHRINK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[X:%.*]])
+; CHECK-SHRINK-NEXT: [[SIN:%.*]] = extractvalue { float, float } [[SINCOS]], 0
+; CHECK-SHRINK-NEXT: [[COS:%.*]] = extractvalue { float, float } [[SINCOS]], 1
+; CHECK-SHRINK-NEXT: [[RES:%.*]] = fadd float [[SIN]], [[COS]]
+; CHECK-SHRINK-NEXT: ret float [[RES]]
;
%s = call float @llvm.sin.f32(float %x)
%c = call float @cosf(float %x) #0
@@ -175,4 +221,69 @@ define float @sincos_mixed_intrinsic_sin_libcall_cos(float %x) {
ret float %res
}
+; UnsafeFPShrink: sin(fpext float) -> fpext(sinf(float))
+; This should trigger optimizeUnaryDoubleFP before converting to intrinsic.
+define float @sin_double_to_float_shrink(float %x) {
+; CHECK-LABEL: @sin_double_to_float_shrink(
+; CHECK-NEXT: [[EXT:%.*]] = fpext float [[X:%.*]] to double
+; CHECK-NEXT: [[S:%.*]] = call double @llvm.sin.f64(double [[EXT]])
+; CHECK-NEXT: [[TRUNC:%.*]] = fptrunc double [[S]] to float
+; CHECK-NEXT: ret float [[TRUNC]]
+;
+; CHECK-SHRINK-LABEL: @sin_double_to_float_shrink(
+; CHECK-SHRINK-NEXT: [[SINF:%.*]] = call float @llvm.sin.f32(float [[X:%.*]])
+; CHECK-SHRINK-NEXT: ret float [[SINF]]
+;
+ %ext = fpext float %x to double
+ %s = call double @sin(double %ext) #0
+ %trunc = fptrunc double %s to float
+ ret float %trunc
+}
+
+; UnsafeFPShrink: cos(fpext float) -> fpext(cosf(float))
+define float @cos_double_to_float_shrink(float %x) {
+; CHECK-LABEL: @cos_double_to_float_shrink(
+; CHECK-NEXT: [[EXT:%.*]] = fpext float [[X:%.*]] to double
+; CHECK-NEXT: [[C:%.*]] = call double @llvm.cos.f64(double [[EXT]])
+; CHECK-NEXT: [[TRUNC:%.*]] = fptrunc double [[C]] to float
+; CHECK-NEXT: ret float [[TRUNC]]
+;
+; CHECK-SHRINK-LABEL: @cos_double_to_float_shrink(
+; CHECK-SHRINK-NEXT: [[COSF:%.*]] = call float @llvm.cos.f32(float [[X:%.*]])
+; CHECK-SHRINK-NEXT: ret float [[COSF]]
+;
+ %ext = fpext float %x to double
+ %c = call double @cos(double %ext) #0
+ %trunc = fptrunc double %c to float
+ ret float %trunc
+}
+
+; UnsafeFPShrink + sincos: sin(fpext float) + cos(fpext float) should
+; first shrink to sinf/cosf, then combine into llvm.sincos.f32.
+define { float, float } @sincos_double_to_float_shrink(float %x) {
+; CHECK-LABEL: @sincos_double_to_float_shrink(
+; CHECK-NEXT: [[EXT:%.*]] = fpext float [[X:%.*]] to double
+; CHECK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[EXT]])
+; CHECK-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
+; CHECK-NEXT: [[ST:%.*]] = fptrunc double [[SIN]] to float
+; CHECK-NEXT: [[CT:%.*]] = fptrunc double [[COS]] to float
+; CHECK-NEXT: [[R0:%.*]] = insertvalue { float, float } undef, float [[ST]], 0
+; CHECK-NEXT: [[R1:%.*]] = insertvalue { float, float } [[R0]], float [[CT]], 1
+; CHECK-NEXT: ret { float, float } [[R1]]
+;
+; CHECK-SHRINK-LABEL: @sincos_double_to_float_shrink(
+; CHECK-SHRINK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[X:%.*]])
+; CHECK-SHRINK-NEXT: ret { float, float } [[SINCOS]]
+;
+ %ext = fpext float %x to double
+ %s = call double @sin(double %ext) #0
+ %c = call double @cos(double %ext) #0
+ %st = fptrunc double %s to float
+ %ct = fptrunc double %c to float
+ %r0 = insertvalue { float, float } undef, float %st, 0
+ %r1 = insertvalue { float, float } %r0, float %ct, 1
+ ret { float, float } %r1
+}
+
attributes #0 = { nounwind memory(none) }
>From 41944e9bc9d7d5649d23b4f38fbb5e80827de895 Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at sifive.com>
Date: Fri, 6 Mar 2026 22:05:56 +0800
Subject: [PATCH 5/8] !fixup: Guard sin/cos to intrinsic conversion on
doesNotAccessMemory
Only convert sin/cos libcalls to llvm.sin/llvm.cos intrinsics when the
call has memory(none) attribute, preserving errno-setting semantics for
calls that may write to errno. Update affected test CHECK lines.
---
.../lib/Transforms/Utils/SimplifyLibCalls.cpp | 14 ++++--
.../test/Transforms/InstCombine/AMDGPU/tan.ll | 3 +-
.../Transforms/InstCombine/fdiv-cos-sin.ll | 50 ++++++++++++-------
.../Transforms/InstCombine/fdiv-sin-cos.ll | 40 +++++++++++----
.../Transforms/InstCombine/may-alias-errno.ll | 4 +-
5 files changed, 74 insertions(+), 37 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 48f4eda1d7638..920aac644b9c5 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -4032,10 +4032,14 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
return optimizeSinCosPi(CI, /*IsSin*/false, Builder);
case LibFunc_sinf:
case LibFunc_sinl:
- return replaceUnaryCall(CI, Builder, Intrinsic::sin);
+ if (CI->doesNotAccessMemory())
+ return replaceUnaryCall(CI, Builder, Intrinsic::sin);
+ return nullptr;
case LibFunc_cosf:
case LibFunc_cosl:
- return replaceUnaryCall(CI, Builder, Intrinsic::cos);
+ if (CI->doesNotAccessMemory())
+ return replaceUnaryCall(CI, Builder, Intrinsic::cos);
+ return nullptr;
case LibFunc_powf:
case LibFunc_pow:
case LibFunc_powl:
@@ -4108,8 +4112,10 @@ Value *LibCallSimplifier::optimizeFloatingPointLibCall(CallInst *CI,
hasFloatVersion(M, CI->getCalledFunction()->getName()))
if (Value *V = optimizeUnaryDoubleFP(CI, Builder, TLI, true))
return V;
- return replaceUnaryCall(
- CI, Builder, Func == LibFunc_sin ? Intrinsic::sin : Intrinsic::cos);
+ if (CI->doesNotAccessMemory())
+ return replaceUnaryCall(
+ CI, Builder, Func == LibFunc_sin ? Intrinsic::sin : Intrinsic::cos);
+ return nullptr;
case LibFunc_acos:
case LibFunc_acosh:
case LibFunc_asin:
diff --git a/llvm/test/Transforms/InstCombine/AMDGPU/tan.ll b/llvm/test/Transforms/InstCombine/AMDGPU/tan.ll
index 62160a6d3063a..f8103f0cc229b 100644
--- a/llvm/test/Transforms/InstCombine/AMDGPU/tan.ll
+++ b/llvm/test/Transforms/InstCombine/AMDGPU/tan.ll
@@ -3,8 +3,7 @@
; Check that sin/cos is not folded to tan on amdgcn.
; GCN-LABEL: define amdgpu_ps float @llpc.shader.FS.main
-; GCN: call float @llvm.sin.f32
-; GCN: call float @llvm.cos.f32
+; GCN: call { float, float } @llvm.sincos.f32
declare float @llvm.sin.f32(float) #0
declare float @llvm.cos.f32(float) #0
diff --git a/llvm/test/Transforms/InstCombine/fdiv-cos-sin.ll b/llvm/test/Transforms/InstCombine/fdiv-cos-sin.ll
index 6d945ede3b387..007125501b30b 100644
--- a/llvm/test/Transforms/InstCombine/fdiv-cos-sin.ll
+++ b/llvm/test/Transforms/InstCombine/fdiv-cos-sin.ll
@@ -3,8 +3,9 @@
define double @fdiv_cos_sin(double %a) {
; CHECK-LABEL: @fdiv_cos_sin(
-; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.cos.f64(double [[A:%.*]])
-; CHECK-NEXT: [[TMP2:%.*]] = call double @llvm.sin.f64(double [[A]])
+; CHECK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[A:%.*]])
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = extractvalue { double, double } [[SINCOS]], 1
; CHECK-NEXT: [[DIV:%.*]] = fdiv double [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret double [[DIV]]
;
@@ -16,8 +17,9 @@ define double @fdiv_cos_sin(double %a) {
define double @fdiv_strict_cos_strict_sin_reassoc(double %a) {
; CHECK-LABEL: @fdiv_strict_cos_strict_sin_reassoc(
-; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.cos.f64(double [[A:%.*]])
-; CHECK-NEXT: [[TMP2:%.*]] = call reassoc double @llvm.sin.f64(double [[A]])
+; CHECK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[A:%.*]])
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = extractvalue { double, double } [[SINCOS]], 1
; CHECK-NEXT: [[DIV:%.*]] = fdiv double [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret double [[DIV]]
;
@@ -29,8 +31,10 @@ define double @fdiv_strict_cos_strict_sin_reassoc(double %a) {
define double @fdiv_reassoc_cos_strict_sin_strict(double %a, ptr dereferenceable(2) %dummy) {
; CHECK-LABEL: @fdiv_reassoc_cos_strict_sin_strict(
-; CHECK-NEXT: [[TAN:%.*]] = call reassoc double @tan(double [[A:%.*]]) #[[ATTR1:[0-9]+]]
-; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double 1.000000e+00, [[TAN]]
+; CHECK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[A:%.*]])
+; CHECK-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
+; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double [[COS]], [[SIN]]
; CHECK-NEXT: ret double [[DIV]]
;
%1 = call double @llvm.cos.f64(double %a)
@@ -41,8 +45,10 @@ define double @fdiv_reassoc_cos_strict_sin_strict(double %a, ptr dereferenceable
define double @fdiv_reassoc_cos_reassoc_sin_strict(double %a) {
; CHECK-LABEL: @fdiv_reassoc_cos_reassoc_sin_strict(
-; CHECK-NEXT: [[TAN:%.*]] = call reassoc double @tan(double [[A:%.*]]) #[[ATTR1]]
-; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double 1.000000e+00, [[TAN]]
+; CHECK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[A:%.*]])
+; CHECK-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
+; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double [[COS]], [[SIN]]
; CHECK-NEXT: ret double [[DIV]]
;
%1 = call reassoc double @llvm.cos.f64(double %a)
@@ -53,8 +59,9 @@ define double @fdiv_reassoc_cos_reassoc_sin_strict(double %a) {
define double @fdiv_cos_sin_reassoc_multiple_uses(double %a) {
; CHECK-LABEL: @fdiv_cos_sin_reassoc_multiple_uses(
-; CHECK-NEXT: [[TMP1:%.*]] = call reassoc double @llvm.cos.f64(double [[A:%.*]])
-; CHECK-NEXT: [[TMP2:%.*]] = call reassoc double @llvm.sin.f64(double [[A]])
+; CHECK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[A:%.*]])
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = extractvalue { double, double } [[SINCOS]], 1
; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double [[TMP1]], [[TMP2]]
; CHECK-NEXT: call void @use(double [[TMP2]])
; CHECK-NEXT: ret double [[DIV]]
@@ -68,8 +75,10 @@ define double @fdiv_cos_sin_reassoc_multiple_uses(double %a) {
define double @fdiv_cos_sin_reassoc(double %a) {
; CHECK-LABEL: @fdiv_cos_sin_reassoc(
-; CHECK-NEXT: [[TAN:%.*]] = call reassoc double @tan(double [[A:%.*]]) #[[ATTR1]]
-; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double 1.000000e+00, [[TAN]]
+; CHECK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[A:%.*]])
+; CHECK-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
+; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double [[COS]], [[SIN]]
; CHECK-NEXT: ret double [[DIV]]
;
%1 = call reassoc double @llvm.cos.f64(double %a)
@@ -80,8 +89,9 @@ define double @fdiv_cos_sin_reassoc(double %a) {
define half @fdiv_cosf16_sinf16_reassoc(half %a) {
; CHECK-LABEL: @fdiv_cosf16_sinf16_reassoc(
-; CHECK-NEXT: [[TMP1:%.*]] = call reassoc half @llvm.cos.f16(half [[A:%.*]])
-; CHECK-NEXT: [[TMP2:%.*]] = call reassoc half @llvm.sin.f16(half [[A]])
+; CHECK-NEXT: [[SINCOS:%.*]] = call { half, half } @llvm.sincos.f16(half [[A:%.*]])
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { half, half } [[SINCOS]], 0
+; CHECK-NEXT: [[TMP1:%.*]] = extractvalue { half, half } [[SINCOS]], 1
; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc half [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret half [[DIV]]
;
@@ -93,8 +103,10 @@ define half @fdiv_cosf16_sinf16_reassoc(half %a) {
define float @fdiv_cosf_sinf_reassoc(float %a) {
; CHECK-LABEL: @fdiv_cosf_sinf_reassoc(
-; CHECK-NEXT: [[TANF:%.*]] = call reassoc float @tanf(float [[A:%.*]]) #[[ATTR1]]
-; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc float 1.000000e+00, [[TANF]]
+; CHECK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[A:%.*]])
+; CHECK-NEXT: [[SIN:%.*]] = extractvalue { float, float } [[SINCOS]], 0
+; CHECK-NEXT: [[COS:%.*]] = extractvalue { float, float } [[SINCOS]], 1
+; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc float [[COS]], [[SIN]]
; CHECK-NEXT: ret float [[DIV]]
;
%1 = call reassoc float @llvm.cos.f32(float %a)
@@ -105,8 +117,10 @@ define float @fdiv_cosf_sinf_reassoc(float %a) {
define fp128 @fdiv_cosfp128_sinfp128_reassoc(fp128 %a) {
; CHECK-LABEL: @fdiv_cosfp128_sinfp128_reassoc(
-; CHECK-NEXT: [[TANL:%.*]] = call reassoc fp128 @tanl(fp128 [[A:%.*]]) #[[ATTR1]]
-; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc fp128 0xL00000000000000003FFF000000000000, [[TANL]]
+; CHECK-NEXT: [[SINCOS:%.*]] = call { fp128, fp128 } @llvm.sincos.f128(fp128 [[A:%.*]])
+; CHECK-NEXT: [[SIN:%.*]] = extractvalue { fp128, fp128 } [[SINCOS]], 0
+; CHECK-NEXT: [[COS:%.*]] = extractvalue { fp128, fp128 } [[SINCOS]], 1
+; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc fp128 [[COS]], [[SIN]]
; CHECK-NEXT: ret fp128 [[DIV]]
;
%1 = call reassoc fp128 @llvm.cos.fp128(fp128 %a)
diff --git a/llvm/test/Transforms/InstCombine/fdiv-sin-cos.ll b/llvm/test/Transforms/InstCombine/fdiv-sin-cos.ll
index a9b8af345f96d..5e79f8a9296c3 100644
--- a/llvm/test/Transforms/InstCombine/fdiv-sin-cos.ll
+++ b/llvm/test/Transforms/InstCombine/fdiv-sin-cos.ll
@@ -3,8 +3,9 @@
define double @fdiv_sin_cos(double %a) {
; CHECK-LABEL: @fdiv_sin_cos(
-; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.sin.f64(double [[A:%.*]])
-; CHECK-NEXT: [[TMP2:%.*]] = call double @llvm.cos.f64(double [[A]])
+; CHECK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[A:%.*]])
+; CHECK-NEXT: [[TMP1:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { double, double } [[SINCOS]], 1
; CHECK-NEXT: [[DIV:%.*]] = fdiv double [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret double [[DIV]]
;
@@ -16,8 +17,9 @@ define double @fdiv_sin_cos(double %a) {
define double @fdiv_strict_sin_strict_cos_reassoc(double %a) {
; CHECK-LABEL: @fdiv_strict_sin_strict_cos_reassoc(
-; CHECK-NEXT: [[TMP1:%.*]] = call double @llvm.sin.f64(double [[A:%.*]])
-; CHECK-NEXT: [[TMP2:%.*]] = call reassoc double @llvm.cos.f64(double [[A]])
+; CHECK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[A:%.*]])
+; CHECK-NEXT: [[TMP1:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { double, double } [[SINCOS]], 1
; CHECK-NEXT: [[DIV:%.*]] = fdiv double [[TMP1]], [[TMP2]]
; CHECK-NEXT: ret double [[DIV]]
;
@@ -29,7 +31,10 @@ define double @fdiv_strict_sin_strict_cos_reassoc(double %a) {
define double @fdiv_reassoc_sin_strict_cos_strict(double %a, ptr dereferenceable(2) %dummy) {
; CHECK-LABEL: @fdiv_reassoc_sin_strict_cos_strict(
-; CHECK-NEXT: [[TAN:%.*]] = call reassoc double @tan(double [[A:%.*]]) #[[ATTR1:[0-9]+]]
+; CHECK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[A:%.*]])
+; CHECK-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
+; CHECK-NEXT: [[TAN:%.*]] = fdiv reassoc double [[SIN]], [[COS]]
; CHECK-NEXT: ret double [[TAN]]
;
%1 = call double @llvm.sin.f64(double %a)
@@ -40,7 +45,10 @@ define double @fdiv_reassoc_sin_strict_cos_strict(double %a, ptr dereferenceable
define double @fdiv_reassoc_sin_reassoc_cos_strict(double %a) {
; CHECK-LABEL: @fdiv_reassoc_sin_reassoc_cos_strict(
-; CHECK-NEXT: [[TAN:%.*]] = call reassoc double @tan(double [[A:%.*]]) #[[ATTR1]]
+; CHECK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[A:%.*]])
+; CHECK-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
+; CHECK-NEXT: [[TAN:%.*]] = fdiv reassoc double [[SIN]], [[COS]]
; CHECK-NEXT: ret double [[TAN]]
;
%1 = call reassoc double @llvm.sin.f64(double %a)
@@ -51,8 +59,9 @@ define double @fdiv_reassoc_sin_reassoc_cos_strict(double %a) {
define double @fdiv_sin_cos_reassoc_multiple_uses(double %a) {
; CHECK-LABEL: @fdiv_sin_cos_reassoc_multiple_uses(
-; CHECK-NEXT: [[TMP1:%.*]] = call reassoc double @llvm.sin.f64(double [[A:%.*]])
-; CHECK-NEXT: [[TMP2:%.*]] = call reassoc double @llvm.cos.f64(double [[A]])
+; CHECK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[A:%.*]])
+; CHECK-NEXT: [[TMP1:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-NEXT: [[TMP2:%.*]] = extractvalue { double, double } [[SINCOS]], 1
; CHECK-NEXT: [[DIV:%.*]] = fdiv reassoc double [[TMP1]], [[TMP2]]
; CHECK-NEXT: call void @use(double [[TMP2]])
; CHECK-NEXT: ret double [[DIV]]
@@ -66,7 +75,10 @@ define double @fdiv_sin_cos_reassoc_multiple_uses(double %a) {
define double @fdiv_sin_cos_reassoc(double %a) {
; CHECK-LABEL: @fdiv_sin_cos_reassoc(
-; CHECK-NEXT: [[TAN:%.*]] = call reassoc double @tan(double [[A:%.*]]) #[[ATTR1]]
+; CHECK-NEXT: [[SINCOS:%.*]] = call { double, double } @llvm.sincos.f64(double [[A:%.*]])
+; CHECK-NEXT: [[SIN:%.*]] = extractvalue { double, double } [[SINCOS]], 0
+; CHECK-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
+; CHECK-NEXT: [[TAN:%.*]] = fdiv reassoc double [[SIN]], [[COS]]
; CHECK-NEXT: ret double [[TAN]]
;
%1 = call reassoc double @llvm.sin.f64(double %a)
@@ -77,7 +89,10 @@ define double @fdiv_sin_cos_reassoc(double %a) {
define float @fdiv_sinf_cosf_reassoc(float %a) {
; CHECK-LABEL: @fdiv_sinf_cosf_reassoc(
-; CHECK-NEXT: [[TANF:%.*]] = call reassoc float @tanf(float [[A:%.*]]) #[[ATTR1]]
+; CHECK-NEXT: [[SINCOS:%.*]] = call { float, float } @llvm.sincos.f32(float [[A:%.*]])
+; CHECK-NEXT: [[SIN:%.*]] = extractvalue { float, float } [[SINCOS]], 0
+; CHECK-NEXT: [[COS:%.*]] = extractvalue { float, float } [[SINCOS]], 1
+; CHECK-NEXT: [[TANF:%.*]] = fdiv reassoc float [[SIN]], [[COS]]
; CHECK-NEXT: ret float [[TANF]]
;
%1 = call reassoc float @llvm.sin.f32(float %a)
@@ -88,7 +103,10 @@ define float @fdiv_sinf_cosf_reassoc(float %a) {
define fp128 @fdiv_sinfp128_cosfp128_reassoc(fp128 %a) {
; CHECK-LABEL: @fdiv_sinfp128_cosfp128_reassoc(
-; CHECK-NEXT: [[TANL:%.*]] = call reassoc fp128 @tanl(fp128 [[A:%.*]]) #[[ATTR1]]
+; CHECK-NEXT: [[SINCOS:%.*]] = call { fp128, fp128 } @llvm.sincos.f128(fp128 [[A:%.*]])
+; CHECK-NEXT: [[SIN:%.*]] = extractvalue { fp128, fp128 } [[SINCOS]], 0
+; CHECK-NEXT: [[COS:%.*]] = extractvalue { fp128, fp128 } [[SINCOS]], 1
+; CHECK-NEXT: [[TANL:%.*]] = fdiv reassoc fp128 [[SIN]], [[COS]]
; CHECK-NEXT: ret fp128 [[TANL]]
;
%1 = call reassoc fp128 @llvm.sin.fp128(fp128 %a)
diff --git a/llvm/test/Transforms/InstCombine/may-alias-errno.ll b/llvm/test/Transforms/InstCombine/may-alias-errno.ll
index 40fab8024b362..89f5e49cdf581 100644
--- a/llvm/test/Transforms/InstCombine/may-alias-errno.ll
+++ b/llvm/test/Transforms/InstCombine/may-alias-errno.ll
@@ -27,7 +27,7 @@ define float @does_not_alias_errno_2(float %f) {
; CHECK-NEXT: [[P:%.*]] = alloca float, align 4
; CHECK-NEXT: call void @escape(ptr nonnull [[P]])
; CHECK-NEXT: store float 0.000000e+00, ptr [[P]], align 4
-; CHECK-NEXT: [[TMP1:%.*]] = call float @sinf(float [[F]])
+; CHECK-NEXT: [[TMP0:%.*]] = call float @sinf(float [[F]])
; CHECK-NEXT: ret float 0.000000e+00
;
entry:
@@ -47,7 +47,7 @@ define double @does_not_alias_errno_3(ptr %p, float %f) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: call void @escape(ptr [[P]])
; CHECK-NEXT: store double 0.000000e+00, ptr [[P]], align 8
-; CHECK-NEXT: [[TMP1:%.*]] = call float @sinf(float [[F]])
+; CHECK-NEXT: [[TMP0:%.*]] = call float @sinf(float [[F]])
; CHECK-NEXT: ret double 0.000000e+00
;
entry:
>From ec401836c09861725e5cd543097dddcf21c21c00 Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at sifive.com>
Date: Mon, 9 Mar 2026 08:33:00 +0800
Subject: [PATCH 6/8] !fixup: Guard sin/cos to intrinsic on
doesNotAccessMemory, update tests
Only convert sin/cos libcalls to llvm.sin/llvm.cos intrinsics when the
call has memory(none) attribute, preserving errno-setting semantics.
Update affected test CHECK lines across InstCombine and CodeGenOpenCL.
---
clang/test/CodeGenOpenCL/builtins-f16.cl | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/clang/test/CodeGenOpenCL/builtins-f16.cl b/clang/test/CodeGenOpenCL/builtins-f16.cl
index f30ed0a1944ff..a0d9be6c8940b 100644
--- a/clang/test/CodeGenOpenCL/builtins-f16.cl
+++ b/clang/test/CodeGenOpenCL/builtins-f16.cl
@@ -3,6 +3,8 @@
#pragma OPENCL EXTENSION cl_khr_fp16 : enable
// CHECK-LABEL: define{{.*}} void @test_half_builtins
+// sin and cos with the same argument are combined into sincos
+// CHECK: call { half, half } @llvm.sincos.f16(half %h0)
void test_half_builtins(half h0, half h1, half h2, int i0) {
volatile half res;
@@ -27,7 +29,8 @@ void test_half_builtins(half h0, half h1, half h2, int i0) {
// CHECK: call half @llvm.ceil.f16(half %h0)
res = __builtin_ceilf16(h0);
- // CHECK: call half @llvm.cos.f16(half %h0)
+ // cos result extracted from sincos above
+ // CHECK: store volatile half %cos
res = __builtin_cosf16(h0);
// CHECK: call half @llvm.cosh.f16(half %h0)
@@ -75,7 +78,8 @@ void test_half_builtins(half h0, half h1, half h2, int i0) {
// CHECK: call half @llvm.round.f16(half %h0)
res = __builtin_roundf16(h0);
- // CHECK: call half @llvm.sin.f16(half %h0)
+ // sin result extracted from sincos above
+ // CHECK: store volatile half %sin
res = __builtin_sinf16(h0);
// CHECK: call half @llvm.sinh.f16(half %h0)
>From e838a85a8da049fb0e54bb3f2ffa8430795146ee Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at sifive.com>
Date: Mon, 9 Mar 2026 20:45:33 +0800
Subject: [PATCH 7/8] !fixup don't touch unrelated code
---
llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index 920aac644b9c5..aafdb8cb1a0c4 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -3051,8 +3051,7 @@ Value *LibCallSimplifier::optimizeSymmetric(CallInst *CI, LibFunc Func,
}
}
-Value *LibCallSimplifier::optimizeSinCosPi(CallInst *CI, bool IsSin,
- IRBuilderBase &B) {
+Value *LibCallSimplifier::optimizeSinCosPi(CallInst *CI, bool IsSin, IRBuilderBase &B) {
// Make sure the prototype is as expected, otherwise the rest of the
// function is probably invalid and likely to abort.
if (!isTrigLibCall(CI))
>From c31335bd823c20e552247312dbfb34391ce6fb81 Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng at sifive.com>
Date: Tue, 10 Mar 2026 23:07:32 +0800
Subject: [PATCH 8/8] !fixup use poison rather than undef in testcase
---
llvm/test/Transforms/InstCombine/sincos.ll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/test/Transforms/InstCombine/sincos.ll b/llvm/test/Transforms/InstCombine/sincos.ll
index ced643c349fde..021f6fbdbfe8b 100644
--- a/llvm/test/Transforms/InstCombine/sincos.ll
+++ b/llvm/test/Transforms/InstCombine/sincos.ll
@@ -268,7 +268,7 @@ define { float, float } @sincos_double_to_float_shrink(float %x) {
; CHECK-NEXT: [[COS:%.*]] = extractvalue { double, double } [[SINCOS]], 1
; CHECK-NEXT: [[ST:%.*]] = fptrunc double [[SIN]] to float
; CHECK-NEXT: [[CT:%.*]] = fptrunc double [[COS]] to float
-; CHECK-NEXT: [[R0:%.*]] = insertvalue { float, float } undef, float [[ST]], 0
+; CHECK-NEXT: [[R0:%.*]] = insertvalue { float, float } poison, float [[ST]], 0
; CHECK-NEXT: [[R1:%.*]] = insertvalue { float, float } [[R0]], float [[CT]], 1
; CHECK-NEXT: ret { float, float } [[R1]]
;
@@ -281,7 +281,7 @@ define { float, float } @sincos_double_to_float_shrink(float %x) {
%c = call double @cos(double %ext) #0
%st = fptrunc double %s to float
%ct = fptrunc double %c to float
- %r0 = insertvalue { float, float } undef, float %st, 0
+ %r0 = insertvalue { float, float } poison, float %st, 0
%r1 = insertvalue { float, float } %r0, float %ct, 1
ret { float, float } %r1
}
More information about the cfe-commits
mailing list