[llvm] [InstCombine] Fold copysign(floor(fabs(X)), X) to trunc(X) (PR #200836)
Aayush Shrivastava via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 1 07:31:59 PDT 2026
https://github.com/iamaayushrivastava created https://github.com/llvm/llvm-project/pull/200836
Fixes #200519.
Adds an InstCombine fold for the pattern `copysign(floor(fabs(X)), X) --> trunc(X)`.
>From 6980a4367bde1d1eb3899f8794b2bf3f758899c4 Mon Sep 17 00:00:00 2001
From: iamaayushrivastava <iamaayushrivastava at gmail.com>
Date: Mon, 1 Jun 2026 19:59:39 +0530
Subject: [PATCH] [InstCombine] Fold copysign(floor(fabs(X)), X) to trunc(X)
---
.../InstCombine/InstCombineCalls.cpp | 6 ++
llvm/test/Transforms/InstCombine/copysign.ll | 70 +++++++++++++++++++
2 files changed, 76 insertions(+)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index aeb40939cd10e..d60337c540e15 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -3116,6 +3116,12 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
if (match(Mag, m_FAbs(m_Value(X))) || match(Mag, m_FNeg(m_Value(X))))
return replaceOperand(*II, 0, X);
+ // copysign(floor(fabs(X)), X) --> trunc(X)
+ if (match(Mag, m_Intrinsic<Intrinsic::floor>(m_FAbs(m_Specific(Sign))))) {
+ Value *Trunc = Builder.CreateUnaryIntrinsic(Intrinsic::trunc, Sign, II);
+ return replaceInstUsesWith(*II, Trunc);
+ }
+
Type *SignEltTy = Sign->getType()->getScalarType();
Value *CastSrc;
diff --git a/llvm/test/Transforms/InstCombine/copysign.ll b/llvm/test/Transforms/InstCombine/copysign.ll
index 4c7b4861b33c9..b4f96a790996b 100644
--- a/llvm/test/Transforms/InstCombine/copysign.ll
+++ b/llvm/test/Transforms/InstCombine/copysign.ll
@@ -2,9 +2,18 @@
; RUN: opt -S -passes=instcombine < %s | FileCheck %s
declare float @llvm.fabs.f32(float)
+declare float @llvm.floor.f32(float)
+declare float @llvm.trunc.f32(float)
declare float @llvm.copysign.f32(float, float)
declare float @llvm.maxnum.f32(float, float)
+declare double @llvm.fabs.f64(double)
+declare double @llvm.floor.f64(double)
+declare double @llvm.trunc.f64(double)
+declare double @llvm.copysign.f64(double, double)
declare <3 x double> @llvm.copysign.v3f64(<3 x double>, <3 x double>)
+declare <3 x double> @llvm.fabs.v3f64(<3 x double>)
+declare <3 x double> @llvm.floor.v3f64(<3 x double>)
+declare <3 x double> @llvm.trunc.v3f64(<3 x double>)
define float @positive_sign_arg(float %x) {
; CHECK-LABEL: @positive_sign_arg(
@@ -285,3 +294,64 @@ define <2 x bfloat> @copysign_simplify_demanded_bits_sign_bitcast_not_int_vec(<2
%result = call <2 x bfloat> @llvm.copysign.v2bf16(<2 x bfloat> %mag, <2 x bfloat> %cast.sign)
ret <2 x bfloat> %result
}
+
+; copysign(floor(fabs(X)), X) --> trunc(X)
+
+define double @copysign_floor_fabs_to_trunc_f64(double %x) {
+; CHECK-LABEL: @copysign_floor_fabs_to_trunc_f64(
+; CHECK-NEXT: [[RESULT:%.*]] = call double @llvm.trunc.f64(double [[X:%.*]])
+; CHECK-NEXT: ret double [[RESULT]]
+;
+ %abs = call double @llvm.fabs.f64(double %x)
+ %fl = call double @llvm.floor.f64(double %abs)
+ %result = call double @llvm.copysign.f64(double %fl, double %x)
+ ret double %result
+}
+
+define float @copysign_floor_fabs_to_trunc_f32(float %x) {
+; CHECK-LABEL: @copysign_floor_fabs_to_trunc_f32(
+; CHECK-NEXT: [[RESULT:%.*]] = call float @llvm.trunc.f32(float [[X:%.*]])
+; CHECK-NEXT: ret float [[RESULT]]
+;
+ %abs = call float @llvm.fabs.f32(float %x)
+ %fl = call float @llvm.floor.f32(float %abs)
+ %result = call float @llvm.copysign.f32(float %fl, float %x)
+ ret float %result
+}
+
+define <3 x double> @copysign_floor_fabs_to_trunc_vec(<3 x double> %x) {
+; CHECK-LABEL: @copysign_floor_fabs_to_trunc_vec(
+; CHECK-NEXT: [[RESULT:%.*]] = call <3 x double> @llvm.trunc.v3f64(<3 x double> [[X:%.*]])
+; CHECK-NEXT: ret <3 x double> [[RESULT]]
+;
+ %abs = call <3 x double> @llvm.fabs.v3f64(<3 x double> %x)
+ %fl = call <3 x double> @llvm.floor.v3f64(<3 x double> %abs)
+ %result = call <3 x double> @llvm.copysign.v3f64(<3 x double> %fl, <3 x double> %x)
+ ret <3 x double> %result
+}
+
+; FMF flags from copysign propagate to the resulting trunc.
+define double @copysign_floor_fabs_to_trunc_fmf(double %x) {
+; CHECK-LABEL: @copysign_floor_fabs_to_trunc_fmf(
+; CHECK-NEXT: [[RESULT:%.*]] = call nnan ninf double @llvm.trunc.f64(double [[X:%.*]])
+; CHECK-NEXT: ret double [[RESULT]]
+;
+ %abs = call double @llvm.fabs.f64(double %x)
+ %fl = call double @llvm.floor.f64(double %abs)
+ %result = call nnan ninf double @llvm.copysign.f64(double %fl, double %x)
+ ret double %result
+}
+
+; Negative test: sign argument differs from fabs argument -- must not fold.
+define double @copysign_floor_fabs_no_fold_different_sign(double %x, double %y) {
+; CHECK-LABEL: @copysign_floor_fabs_no_fold_different_sign(
+; CHECK-NEXT: [[ABS:%.*]] = call double @llvm.fabs.f64(double [[X:%.*]])
+; CHECK-NEXT: [[FL:%.*]] = call double @llvm.floor.f64(double [[ABS]])
+; CHECK-NEXT: [[RESULT:%.*]] = call double @llvm.copysign.f64(double [[FL]], double [[Y:%.*]])
+; CHECK-NEXT: ret double [[RESULT]]
+;
+ %abs = call double @llvm.fabs.f64(double %x)
+ %fl = call double @llvm.floor.f64(double %abs)
+ %result = call double @llvm.copysign.f64(double %fl, double %y)
+ ret double %result
+}
More information about the llvm-commits
mailing list