From 966cb03095ea1f0839394a8af1fc6d83ebe55e09 Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Wed, 10 Dec 2025 18:10:57 +0100 Subject: [PATCH 1/2] IR: Stop requiring nsz to reassociate fmul nsz can only change the behavior of the sign bit. The sign bit for fmul can be implemented as xor, which is associative. DAGCombiner already reassociates the multiply by 2 constants without nsz. Fixes #64967 --- llvm/lib/IR/Instruction.cpp | 1 + .../InstCombine/2006-10-26-VectorReassoc.ll | 6 ++---- llvm/test/Transforms/InstCombine/fdiv.ll | 3 +-- .../InstCombine/issue64967-reassoc-fmul.ll | 18 ++++++------------ 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp index 33ca46ca1c2c6..b95c1466871bc 100644 --- a/llvm/lib/IR/Instruction.cpp +++ b/llvm/lib/IR/Instruction.cpp @@ -1271,6 +1271,7 @@ bool Instruction::isAssociative() const { switch (Opcode) { case FMul: + return cast(this)->hasAllowReassoc(); case FAdd: return cast(this)->hasAllowReassoc() && cast(this)->hasNoSignedZeros(); diff --git a/llvm/test/Transforms/InstCombine/2006-10-26-VectorReassoc.ll b/llvm/test/Transforms/InstCombine/2006-10-26-VectorReassoc.ll index fb860a5e7bdf3..6509797e0d3dc 100644 --- a/llvm/test/Transforms/InstCombine/2006-10-26-VectorReassoc.ll +++ b/llvm/test/Transforms/InstCombine/2006-10-26-VectorReassoc.ll @@ -35,12 +35,10 @@ define <4 x float> @test_fmul_reassoc_nsz(<4 x float> %V) { } ; (V * C1) * C2 => V * (C1 * C2) -; TODO: This doesn't require 'nsz'. It should fold to V * { 1.0, 4.0e+05, -9.0, 16.0 } define <4 x float> @test_fmul_reassoc(<4 x float> %V) { ; CHECK-LABEL: @test_fmul_reassoc( -; CHECK-NEXT: [[TMP1:%.*]] = fmul reassoc <4 x float> [[V:%.*]], -; CHECK-NEXT: [[TMP2:%.*]] = fmul reassoc <4 x float> [[TMP1]], -; CHECK-NEXT: ret <4 x float> [[TMP2]] +; CHECK: [[TMP1:%.*]] = fmul reassoc <4 x float> %V, +; CHECK-NEXT: ret <4 x float> [[TMP1]] %Y = fmul reassoc <4 x float> %V, < float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00 > %Z = fmul reassoc <4 x float> %Y, < float 1.000000e+00, float 2.000000e+05, float -3.000000e+00, float 4.000000e+00 > ret <4 x float> %Z diff --git a/llvm/test/Transforms/InstCombine/fdiv.ll b/llvm/test/Transforms/InstCombine/fdiv.ll index 54b0bf8c50ac7..3465781e3af9d 100644 --- a/llvm/test/Transforms/InstCombine/fdiv.ll +++ b/llvm/test/Transforms/InstCombine/fdiv.ll @@ -525,8 +525,7 @@ define <2 x float> @div_constant_dividend2_reassoc_only(<2 x float> %x) { define <2 x float> @div_constant_dividend3(<2 x float> %x) { ; CHECK-LABEL: @div_constant_dividend3( -; CHECK-NEXT: [[TMP1:%.*]] = fmul reassoc arcp <2 x float> [[X:%.*]], -; CHECK-NEXT: [[T2:%.*]] = fmul reassoc arcp <2 x float> [[TMP1]], +; CHECK-NEXT: [[T2:%.*]] = fmul reassoc arcp <2 x float> [[X:%.*]], ; CHECK-NEXT: ret <2 x float> [[T2]] ; %t1 = fdiv <2 x float> , %x diff --git a/llvm/test/Transforms/InstCombine/issue64967-reassoc-fmul.ll b/llvm/test/Transforms/InstCombine/issue64967-reassoc-fmul.ll index 16f9cf2dd64c5..5d064234bf609 100644 --- a/llvm/test/Transforms/InstCombine/issue64967-reassoc-fmul.ll +++ b/llvm/test/Transforms/InstCombine/issue64967-reassoc-fmul.ll @@ -25,8 +25,7 @@ define float @fmul(float %x) { define float @fmul_reassoc(float %x) { ; CHECK-LABEL: define float @fmul_reassoc( ; CHECK-SAME: float [[X:%.*]]) { -; CHECK-NEXT: [[FMUL0:%.*]] = fmul reassoc float [[X]], 2.000000e+00 -; CHECK-NEXT: [[FMUL1:%.*]] = fmul reassoc float [[FMUL0]], 4.000000e+00 +; CHECK-NEXT: [[FMUL1:%.*]] = fmul reassoc float [[X]], 8.000000e+00 ; CHECK-NEXT: ret float [[FMUL1]] ; %fmul0 = fmul reassoc float %x, 2.0 @@ -37,8 +36,7 @@ define float @fmul_reassoc(float %x) { define <2 x float> @fmul_reassoc_v2(<2 x float> %x) { ; CHECK-LABEL: define <2 x float> @fmul_reassoc_v2( ; CHECK-SAME: <2 x float> [[X:%.*]]) { -; CHECK-NEXT: [[FMUL0:%.*]] = fmul reassoc <2 x float> [[X]], splat (float 2.000000e+00) -; CHECK-NEXT: [[FMUL1:%.*]] = fmul reassoc <2 x float> [[FMUL0]], splat (float 4.000000e+00) +; CHECK-NEXT: [[FMUL1:%.*]] = fmul reassoc <2 x float> [[X]], splat (float 8.000000e+00) ; CHECK-NEXT: ret <2 x float> [[FMUL1]] ; %fmul0 = fmul reassoc <2 x float> %x, splat (float 2.0) @@ -54,8 +52,7 @@ define <2 x float> @fmul_reassoc_v2(<2 x float> %x) { define float @fmul_reassoc_negative_0(float %x) { ; CHECK-LABEL: define float @fmul_reassoc_negative_0( ; CHECK-SAME: float [[X:%.*]]) { -; CHECK-NEXT: [[FMUL0:%.*]] = fmul reassoc float [[X]], 2.000000e+00 -; CHECK-NEXT: [[FMUL1:%.*]] = fmul reassoc float [[FMUL0]], -4.000000e+00 +; CHECK-NEXT: [[FMUL1:%.*]] = fmul reassoc float [[X]], -8.000000e+00 ; CHECK-NEXT: ret float [[FMUL1]] ; %fmul0 = fmul reassoc float %x, 2.0 @@ -71,8 +68,7 @@ define float @fmul_reassoc_negative_0(float %x) { define float @fmul_reassoc_negative_1(float %x) { ; CHECK-LABEL: define float @fmul_reassoc_negative_1( ; CHECK-SAME: float [[X:%.*]]) { -; CHECK-NEXT: [[FMUL0:%.*]] = fmul reassoc float [[X]], -2.000000e+00 -; CHECK-NEXT: [[FMUL1:%.*]] = fmul reassoc float [[FMUL0]], 4.000000e+00 +; CHECK-NEXT: [[FMUL1:%.*]] = fmul reassoc float [[X]], -8.000000e+00 ; CHECK-NEXT: ret float [[FMUL1]] ; %fmul0 = fmul reassoc float %x, -2.0 @@ -95,8 +91,7 @@ define float @fmul_reassoc_nsz(float %x) { define float @fmul_reassoc_posk_neg0(float %x) { ; CHECK-LABEL: define float @fmul_reassoc_posk_neg0( ; CHECK-SAME: float [[X:%.*]]) { -; CHECK-NEXT: [[FMUL0:%.*]] = fmul reassoc float [[X]], 4.000000e+00 -; CHECK-NEXT: [[FMUL1:%.*]] = fmul reassoc float [[FMUL0]], -0.000000e+00 +; CHECK-NEXT: [[FMUL1:%.*]] = fmul reassoc float [[X]], -0.000000e+00 ; CHECK-NEXT: ret float [[FMUL1]] ; %fmul0 = fmul reassoc float %x, 4.0 @@ -108,8 +103,7 @@ define float @fmul_reassoc_neg0_posk(float %x) { ; CHECK-LABEL: define float @fmul_reassoc_neg0_posk( ; CHECK-SAME: float [[X:%.*]]) { ; CHECK-NEXT: [[FMUL0:%.*]] = fmul reassoc float [[X]], -0.000000e+00 -; CHECK-NEXT: [[FMUL1:%.*]] = fmul reassoc float [[FMUL0]], 4.000000e+00 -; CHECK-NEXT: ret float [[FMUL1]] +; CHECK-NEXT: ret float [[FMUL0]] ; %fmul0 = fmul reassoc float %x, -0.0 %fmul1 = fmul reassoc float %fmul0, 4.0 From ef8a54eaa39f56886fed4a98c8d0f40b47192917 Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Wed, 10 Dec 2025 20:07:18 +0100 Subject: [PATCH 2/2] ValueTracking: Add baseline test for fmul denormal scaling handling --- .../Transforms/Attributor/nofpclass-fmul.ll | 288 ++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 llvm/test/Transforms/Attributor/nofpclass-fmul.ll diff --git a/llvm/test/Transforms/Attributor/nofpclass-fmul.ll b/llvm/test/Transforms/Attributor/nofpclass-fmul.ll new file mode 100644 index 0000000000000..6d0edf0681ed4 --- /dev/null +++ b/llvm/test/Transforms/Attributor/nofpclass-fmul.ll @@ -0,0 +1,288 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt -S -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal < %s | FileCheck %s + +; Test that multiply by a constant with a sufficiently large exponent +; proves the result is not subnormal. + +define float @ret_fmul_f32(float %arg0, float %arg1) { +; CHECK-LABEL: define float @ret_fmul_f32( +; CHECK-SAME: float [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG0]], [[ARG1]] +; CHECK-NEXT: ret float [[FMUL]] +; + %fmul = fmul float %arg0, %arg1 + ret float %fmul +} + +define float @ret_fmul_square_f32(float %arg) { +; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_fmul_square_f32( +; CHECK-SAME: float [[ARG:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[FMUL:%.*]] = fmul float [[ARG]], [[ARG]] +; CHECK-NEXT: ret float [[FMUL]] +; + %fmul = fmul float %arg, %arg + ret float %fmul +} + +define float @ret_mul_exponent_f32_22(float %arg0) { +; CHECK-LABEL: define float @ret_mul_exponent_f32_22( +; CHECK-SAME: float [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul float [[ARG0]], 0x4150000000000000 +; CHECK-NEXT: ret float [[CALL]] +; + %call = fmul float %arg0, 0x4150000000000000 + ret float %call +} + +define float @ret_mul_exponent_f32_23(float %arg0) { +; CHECK-LABEL: define float @ret_mul_exponent_f32_23( +; CHECK-SAME: float [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul float [[ARG0]], 0x4160000000000000 +; CHECK-NEXT: ret float [[CALL]] +; + %call = fmul float %arg0, 0x4160000000000000 + ret float %call +} + +define float @ret_mul_exponent_f32_24(float %arg0) { +; CHECK-LABEL: define float @ret_mul_exponent_f32_24( +; CHECK-SAME: float [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul float [[ARG0]], 0x4170000000000000 +; CHECK-NEXT: ret float [[CALL]] +; + %call = fmul float %arg0, 0x4170000000000000 + ret float %call +} + +define float @ret_mul_exponent_f32_23_nnan(float nofpclass(nan) %arg0) { +; CHECK-LABEL: define nofpclass(nan) float @ret_mul_exponent_f32_23_nnan( +; CHECK-SAME: float nofpclass(nan) [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul float [[ARG0]], 0x4160000000000000 +; CHECK-NEXT: ret float [[CALL]] +; + %call = fmul float %arg0, 0x4160000000000000 + ret float %call +} + +define double @ret_mul_exponent_f64_24(double %arg0) { +; CHECK-LABEL: define double @ret_mul_exponent_f64_24( +; CHECK-SAME: double [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul double [[ARG0]], 0x4170000000000000 +; CHECK-NEXT: ret double [[CALL]] +; + %call = fmul double %arg0, 0x4170000000000000 + ret double %call +} + +define double @ret_mul_exponent_f64_51(double %arg0) { +; CHECK-LABEL: define double @ret_mul_exponent_f64_51( +; CHECK-SAME: double [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul double [[ARG0]], 0x4320000000000000 +; CHECK-NEXT: ret double [[CALL]] +; + %call = fmul double %arg0, 0x4320000000000000 + ret double %call +} + +define double @ret_mul_exponent_f64_52(double %arg0) { +; CHECK-LABEL: define double @ret_mul_exponent_f64_52( +; CHECK-SAME: double [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul double [[ARG0]], 0x4330000000000000 +; CHECK-NEXT: ret double [[CALL]] +; + %call = fmul double %arg0, 0x4330000000000000 + ret double %call +} + +define double @ret_mul_exponent_f64_53(double %arg0) { +; CHECK-LABEL: define double @ret_mul_exponent_f64_53( +; CHECK-SAME: double [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul double [[ARG0]], 0x4340000000000000 +; CHECK-NEXT: ret double [[CALL]] +; + %call = fmul double %arg0, 0x4340000000000000 + ret double %call +} + +define half @ret_mul_exponent_f16_8(half %arg0) { +; CHECK-LABEL: define half @ret_mul_exponent_f16_8( +; CHECK-SAME: half [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul half [[ARG0]], 0xH5C00 +; CHECK-NEXT: ret half [[CALL]] +; + %call = fmul half %arg0, 0xH5C00 + ret half %call +} + +define half @ret_mul_exponent_f16_9(half %arg0) { +; CHECK-LABEL: define half @ret_mul_exponent_f16_9( +; CHECK-SAME: half [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul half [[ARG0]], 0xH6000 +; CHECK-NEXT: ret half [[CALL]] +; + %call = fmul half %arg0, 0xH6000 + ret half %call +} + +define half @ret_mul_exponent_f16_10(half %arg0) { +; CHECK-LABEL: define half @ret_mul_exponent_f16_10( +; CHECK-SAME: half [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul half [[ARG0]], 0xH6400 +; CHECK-NEXT: ret half [[CALL]] +; + %call = fmul half %arg0, 0xH6400 + ret half %call +} + +define bfloat @ret_mul_exponent_bf16_6(bfloat %arg0) { +; CHECK-LABEL: define bfloat @ret_mul_exponent_bf16_6( +; CHECK-SAME: bfloat [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul bfloat [[ARG0]], 0xR4280 +; CHECK-NEXT: ret bfloat [[CALL]] +; + %call = fmul bfloat %arg0, 0xR4280 + ret bfloat %call +} + +define bfloat @ret_mul_exponent_bf16_7(bfloat %arg0) { +; CHECK-LABEL: define bfloat @ret_mul_exponent_bf16_7( +; CHECK-SAME: bfloat [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul bfloat [[ARG0]], 0xR4300 +; CHECK-NEXT: ret bfloat [[CALL]] +; + %call = fmul bfloat %arg0, 0xR4300 + ret bfloat %call +} + +define bfloat @ret_mul_exponent_bf16_8(bfloat %arg0) { +; CHECK-LABEL: define bfloat @ret_mul_exponent_bf16_8( +; CHECK-SAME: bfloat [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul bfloat [[ARG0]], 0xR4380 +; CHECK-NEXT: ret bfloat [[CALL]] +; + %call = fmul bfloat %arg0, 0xR4380 + ret bfloat %call +} + +define float @ret_mul_exponent_f32_neg22(float %arg0) { +; CHECK-LABEL: define float @ret_mul_exponent_f32_neg22( +; CHECK-SAME: float [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul float [[ARG0]], 0x3E90000000000000 +; CHECK-NEXT: ret float [[CALL]] +; + %call = fmul float %arg0, 0x3E90000000000000 + ret float %call +} + +define float @ret_mul_exponent_f32_neg23(float %arg0) { +; CHECK-LABEL: define float @ret_mul_exponent_f32_neg23( +; CHECK-SAME: float [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul float [[ARG0]], 0x3E80000000000000 +; CHECK-NEXT: ret float [[CALL]] +; + %call = fmul float %arg0, 0x3E80000000000000 + ret float %call +} + +define float @ret_mul_exponent_f32_neg24(float %arg0) { +; CHECK-LABEL: define float @ret_mul_exponent_f32_neg24( +; CHECK-SAME: float [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul float [[ARG0]], 0x3E70000000000000 +; CHECK-NEXT: ret float [[CALL]] +; + %call = fmul float %arg0, 0x3E70000000000000 + ret float %call +} + +define float @ret_mul_exponent_f32_neg126(float %arg0) { +; CHECK-LABEL: define float @ret_mul_exponent_f32_neg126( +; CHECK-SAME: float [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul float [[ARG0]], 0x3810000000000000 +; CHECK-NEXT: ret float [[CALL]] +; + %call = fmul float %arg0, 0x3810000000000000 + ret float %call +} + +define float @ret_mul_exponent_f32_neg127(float %arg0) { +; CHECK-LABEL: define float @ret_mul_exponent_f32_neg127( +; CHECK-SAME: float [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul float [[ARG0]], 0x3800000000000000 +; CHECK-NEXT: ret float [[CALL]] +; + %call = fmul float %arg0, 0x3800000000000000 + ret float %call +} + +define <2 x float> @ret_mul_exponent_v2f32_splat_23(<2 x float> %arg0) { +; CHECK-LABEL: define <2 x float> @ret_mul_exponent_v2f32_splat_23( +; CHECK-SAME: <2 x float> [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul <2 x float> [[ARG0]], splat (float 0x4160000000000000) +; CHECK-NEXT: ret <2 x float> [[CALL]] +; + %call = fmul <2 x float> %arg0, splat (float 0x4160000000000000) + ret <2 x float> %call +} + +define <2 x float> @ret_mul_exponent_v2f32_splat_poison_23(<2 x float> %arg0) { +; CHECK-LABEL: define <2 x float> @ret_mul_exponent_v2f32_splat_poison_23( +; CHECK-SAME: <2 x float> [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul <2 x float> [[ARG0]], +; CHECK-NEXT: ret <2 x float> [[CALL]] +; + %call = fmul <2 x float> %arg0, + ret <2 x float> %call +} + +; TODO: Should be able to prove nofpclass(sub) +define <2 x float> @ret_mul_exponent_v2f32_nonsplat(<2 x float> %arg0) { +; CHECK-LABEL: define <2 x float> @ret_mul_exponent_v2f32_nonsplat( +; CHECK-SAME: <2 x float> [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul <2 x float> [[ARG0]], +; CHECK-NEXT: ret <2 x float> [[CALL]] +; + %call = fmul <2 x float> %arg0, + ret <2 x float> %call +} + +; Cannot prove this is not-subnormal +define <2 x float> @ret_mul_partially_foldable_exponent_v2f32(<2 x float> %arg0) { +; CHECK-LABEL: define <2 x float> @ret_mul_partially_foldable_exponent_v2f32( +; CHECK-SAME: <2 x float> [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul <2 x float> [[ARG0]], +; CHECK-NEXT: ret <2 x float> [[CALL]] +; + %call = fmul <2 x float> %arg0, + ret <2 x float> %call +} + +define float @ret_mul_f32_0(float %arg0) { +; CHECK-LABEL: define float @ret_mul_f32_0( +; CHECK-SAME: float [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul float [[ARG0]], 0.000000e+00 +; CHECK-NEXT: ret float [[CALL]] +; + %call = fmul float %arg0, 0.0 + ret float %call +} + +define float @ret_mul_f32_inf(float %arg0) { +; CHECK-LABEL: define float @ret_mul_f32_inf( +; CHECK-SAME: float [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul float [[ARG0]], 0x7FF0000000000000 +; CHECK-NEXT: ret float [[CALL]] +; + %call = fmul float %arg0, 0x7FF0000000000000 + ret float %call +} + +define float @ret_mul_f32_nan(float %arg0) { +; CHECK-LABEL: define float @ret_mul_f32_nan( +; CHECK-SAME: float [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CALL:%.*]] = fmul float [[ARG0]], 0x7FF8000000000000 +; CHECK-NEXT: ret float [[CALL]] +; + %call = fmul float %arg0, 0x7FF8000000000000 + ret float %call +} +