Skip to content

Conversation

@arsenm
Copy link
Contributor

@arsenm arsenm commented Dec 10, 2025

No description provided.

@arsenm arsenm added floating-point Floating-point math llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:ir llvm:transforms labels Dec 10, 2025 — with Graphite App
@llvmbot
Copy link
Member

llvmbot commented Dec 10, 2025

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-llvm-ir

Author: Matt Arsenault (arsenm)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/171729.diff

1 Files Affected:

  • (added) llvm/test/Transforms/Attributor/nofpclass-fmul.ll (+288)
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]], <float 0x4160000000000000, float poison>
+; CHECK-NEXT:    ret <2 x float> [[CALL]]
+;
+  %call = fmul <2 x float> %arg0, <float 0x4160000000000000, float poison>
+  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]], <float 0x4160000000000000, float 0x4170000000000000>
+; CHECK-NEXT:    ret <2 x float> [[CALL]]
+;
+  %call = fmul <2 x float> %arg0, <float 0x4160000000000000, float 0x4170000000000000>
+  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]], <float 0x4160000000000000, float 0x4150000000000000>
+; CHECK-NEXT:    ret <2 x float> [[CALL]]
+;
+  %call = fmul <2 x float> %arg0, <float 0x4160000000000000, float 0x4150000000000000>
+  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
+}
+

Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

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
@arsenm arsenm force-pushed the users/arsenm/valuetracking/add-baseline-test-fmul-exponent-handling branch from 89db664 to ef8a54e Compare December 11, 2025 10:41
@arsenm arsenm force-pushed the users/arsenm/issue64697/allow-reassoc-fmul-no-nsz branch from a7f8f32 to 966cb03 Compare December 11, 2025 10:41
Base automatically changed from users/arsenm/issue64697/allow-reassoc-fmul-no-nsz to main December 11, 2025 11:11
@arsenm arsenm merged commit 4be3df8 into main Dec 11, 2025
13 of 17 checks passed
@arsenm arsenm deleted the users/arsenm/valuetracking/add-baseline-test-fmul-exponent-handling branch December 11, 2025 12:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

floating-point Floating-point math llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:ir llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants