diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp index c7023eb79b04e..8ffbdef61b6fa 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -1073,6 +1073,18 @@ Instruction *InstCombinerImpl::visitFMul(BinaryOperator &I) { return Result; } + // tan(X) * cos(X) -> sin(X) + if (I.hasAllowContract() && + match(&I, + m_c_FMul(m_OneUse(m_Intrinsic(m_Value(X))), + m_OneUse(m_Intrinsic(m_Deferred(X)))))) { + auto *Sin = Builder.CreateUnaryIntrinsic(Intrinsic::sin, X, &I); + if (auto *Metadata = I.getMetadata(LLVMContext::MD_fpmath)) { + Sin->setMetadata(LLVMContext::MD_fpmath, Metadata); + } + return replaceInstUsesWith(I, Sin); + } + return nullptr; } diff --git a/llvm/test/Transforms/InstCombine/fmul-tan-cos.ll b/llvm/test/Transforms/InstCombine/fmul-tan-cos.ll new file mode 100644 index 0000000000000..c3446948542e8 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/fmul-tan-cos.ll @@ -0,0 +1,180 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -S -passes=instcombine < %s | FileCheck %s + +define double @fmul_tan_cos(double %a) { +; CHECK-LABEL: define double @fmul_tan_cos( +; CHECK-SAME: double [[A:%.*]]) { +; CHECK-NEXT: [[TAN:%.*]] = call double @llvm.tan.f64(double [[A]]) +; CHECK-NEXT: [[COS:%.*]] = call double @llvm.cos.f64(double [[A]]) +; CHECK-NEXT: [[RES:%.*]] = fmul double [[TAN]], [[COS]] +; CHECK-NEXT: ret double [[RES]] +; + %tan = call double @llvm.tan.f64(double %a) + %cos = call double @llvm.cos.f64(double %a) + %res = fmul double %tan, %cos + ret double %res +} + +define double @fmul_strict_tan_strict_cos_contract(double %a) { +; CHECK-LABEL: define double @fmul_strict_tan_strict_cos_contract( +; CHECK-SAME: double [[A:%.*]]) { +; CHECK-NEXT: [[TAN:%.*]] = call double @llvm.tan.f64(double [[A]]) +; CHECK-NEXT: [[COS:%.*]] = call contract double @llvm.cos.f64(double [[A]]) +; CHECK-NEXT: [[RES:%.*]] = fmul double [[TAN]], [[COS]] +; CHECK-NEXT: ret double [[RES]] +; + %tan = call double @llvm.tan.f64(double %a) + %cos = call contract double @llvm.cos.f64(double %a) + %res = fmul double %tan, %cos + ret double %res +} + +define double @fmul_contract_tan_strict_cos_strict(double %a) { +; CHECK-LABEL: define double @fmul_contract_tan_strict_cos_strict( +; CHECK-SAME: double [[A:%.*]]) { +; CHECK-NEXT: [[RES:%.*]] = call contract double @llvm.sin.f64(double [[A]]) +; CHECK-NEXT: ret double [[RES]] +; + %tan = call double @llvm.tan.f64(double %a) + %cos = call double @llvm.cos.f64(double %a) + %res = fmul contract double %tan, %cos + ret double %res +} + +define double @fmul_contract_tan_contract_cos_strict(double %a) { +; CHECK-LABEL: define double @fmul_contract_tan_contract_cos_strict( +; CHECK-SAME: double [[A:%.*]]) { +; CHECK-NEXT: [[RES:%.*]] = call contract double @llvm.sin.f64(double [[A]]) +; CHECK-NEXT: ret double [[RES]] +; + %tan = call contract double @llvm.tan.f64(double %a) + %cos = call double @llvm.cos.f64(double %a) + %res = fmul contract double %tan, %cos + ret double %res +} + +define double @fmul_tan_cos_contract_multiple_uses(double %a) { +; CHECK-LABEL: define double @fmul_tan_cos_contract_multiple_uses( +; CHECK-SAME: double [[A:%.*]]) { +; CHECK-NEXT: [[TAN:%.*]] = call contract double @llvm.tan.f64(double [[A]]) +; CHECK-NEXT: [[COS:%.*]] = call contract double @llvm.cos.f64(double [[A]]) +; CHECK-NEXT: [[RES:%.*]] = fmul contract double [[TAN]], [[COS]] +; CHECK-NEXT: call void @use(double [[COS]]) +; CHECK-NEXT: ret double [[RES]] +; + %tan = call contract double @llvm.tan.f64(double %a) + %cos = call contract double @llvm.cos.f64(double %a) + %res = fmul contract double %tan, %cos + call void @use(double %cos) + ret double %res +} + +define double @fmul_tan_cos_contract(double %a) { +; CHECK-LABEL: define double @fmul_tan_cos_contract( +; CHECK-SAME: double [[A:%.*]]) { +; CHECK-NEXT: [[RES:%.*]] = call contract double @llvm.sin.f64(double [[A]]) +; CHECK-NEXT: ret double [[RES]] +; + %tan = call contract double @llvm.tan.f64(double %a) + %cos = call contract double @llvm.cos.f64(double %a) + %res = fmul contract double %tan, %cos + ret double %res +} + +define float @fmul_tanf_cosf_contract(float %a) { +; CHECK-LABEL: define float @fmul_tanf_cosf_contract( +; CHECK-SAME: float [[A:%.*]]) { +; CHECK-NEXT: [[RES:%.*]] = call contract float @llvm.sin.f32(float [[A]]) +; CHECK-NEXT: ret float [[RES]] +; + %tan = call contract float @llvm.tan.f32(float %a) + %cos = call contract float @llvm.cos.f32(float %a) + %res = fmul contract float %tan, %cos + ret float %res +} + +define fp128 @fmul_tanfp128_cosfp128_contract(fp128 %a) { +; CHECK-LABEL: define fp128 @fmul_tanfp128_cosfp128_contract( +; CHECK-SAME: fp128 [[A:%.*]]) { +; CHECK-NEXT: [[RES:%.*]] = call contract fp128 @llvm.sin.f128(fp128 [[A]]) +; CHECK-NEXT: ret fp128 [[RES]] +; + %tan = call contract fp128 @llvm.tan.fp128(fp128 %a) + %cos = call contract fp128 @llvm.cos.fp128(fp128 %a) + %res = fmul contract fp128 %tan, %cos + ret fp128 %res +} + +; commutativity +define double @commutativity_cos_tan(double %a) { +; CHECK-LABEL: define double @commutativity_cos_tan( +; CHECK-SAME: double [[A:%.*]]) { +; CHECK-NEXT: [[RES:%.*]] = call contract double @llvm.sin.f64(double [[A]]) +; CHECK-NEXT: ret double [[RES]] +; + %cos = call contract double @llvm.cos.f64(double %a) + %tan = call contract double @llvm.tan.f64(double %a) + %res = fmul contract double %cos, %tan + ret double %res +} + +; negative test with mismatched value +define double @tan_cos_value_mismatch(double %a, double %b) { +; CHECK-LABEL: define double @tan_cos_value_mismatch( +; CHECK-SAME: double [[A:%.*]], double [[B:%.*]]) { +; CHECK-NEXT: [[TAN:%.*]] = call contract double @llvm.tan.f64(double [[A]]) +; CHECK-NEXT: [[COS:%.*]] = call contract double @llvm.cos.f64(double [[B]]) +; CHECK-NEXT: [[RES:%.*]] = fmul contract double [[TAN]], [[COS]] +; CHECK-NEXT: ret double [[RES]] +; + %tan = call contract double @llvm.tan.f64(double %a) + %cos = call contract double @llvm.cos.f64(double %b) + %res = fmul contract double %tan, %cos + ret double %res +} + +; Vector +define <2 x double> @fmul_tan_cos_vector(<2 x double> %a) { +; CHECK-LABEL: define <2 x double> @fmul_tan_cos_vector( +; CHECK-SAME: <2 x double> [[A:%.*]]) { +; CHECK-NEXT: [[RES:%.*]] = call contract <2 x double> @llvm.sin.v2f64(<2 x double> [[A]]) +; CHECK-NEXT: ret <2 x double> [[RES]] +; + %tan = call contract <2 x double> @llvm.tan.v2f64(<2 x double> %a) + %cos = call contract <2 x double> @llvm.cos.v2f64(<2 x double> %a) + %res = fmul contract <2 x double> %tan, %cos + ret <2 x double> %res +} + +; Flag preservation +define double @fmul_tan_cos_nnan_preservation(double %a) { +; CHECK-LABEL: define double @fmul_tan_cos_nnan_preservation( +; CHECK-SAME: double [[A:%.*]]) { +; CHECK-NEXT: [[RES:%.*]] = call nnan contract double @llvm.sin.f64(double [[A]]) +; CHECK-NEXT: ret double [[RES]] +; + %tan = call contract double @llvm.tan.f64(double %a) + %cos = call contract double @llvm.cos.f64(double %a) + %res = fmul contract nnan double %tan, %cos + ret double %res +} + +; !fpmath metadata preservation +define double @fmul_tan_cos_fpmath_metadata_preservation(double %a) { +; CHECK-LABEL: define double @fmul_tan_cos_fpmath_metadata_preservation( +; CHECK-SAME: double [[A:%.*]]) { +; CHECK-NEXT: [[RES:%.*]] = call contract double @llvm.sin.f64(double [[A]]), !fpmath [[META0:![0-9]+]] +; CHECK-NEXT: ret double [[RES]] +; + %tan = call contract double @llvm.tan.f64(double %a) + %cos = call contract double @llvm.cos.f64(double %a) + %res = fmul contract double %tan, %cos, !fpmath !0 + ret double %res +} + +declare void @use(double) + +!0 = !{ float 2.5 } +;. +; CHECK: [[META0]] = !{float 2.500000e+00} +;.