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 <[email protected]> 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 + +@var32 = global float 0.0 +@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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
