Skip to content

Conversation

@arsenm
Copy link
Contributor

@arsenm arsenm commented Dec 10, 2025

Currently fmul is not reassociated unless it has nsz, although
this should be unnecessary.

Currently fmul is not reassociated unless it has nsz, although
this should be unnecessary.
@arsenm arsenm added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes floating-point Floating-point math labels Dec 10, 2025 — with Graphite App
@arsenm arsenm marked this pull request as ready for review December 10, 2025 23:00
@llvmbot
Copy link
Member

llvmbot commented Dec 10, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Matt Arsenault (arsenm)

Changes

Currently fmul is not reassociated unless it has nsz, although
this should be unnecessary.


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

1 Files Affected:

  • (added) llvm/test/Transforms/InstCombine/issue64967-reassoc-fmul.ll (+117)
diff --git a/llvm/test/Transforms/InstCombine/issue64967-reassoc-fmul.ll b/llvm/test/Transforms/InstCombine/issue64967-reassoc-fmul.ll
new file mode 100644
index 0000000000000..16f9cf2dd64c5
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/issue64967-reassoc-fmul.ll
@@ -0,0 +1,117 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+; Show that unlike fadd, fmul does not require nsz to be reassociated.
+
+; Can't reassociate anyway
+define float @fmul(float %x) {
+; CHECK-LABEL: define float @fmul(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[FMUL0:%.*]] = fmul float [[X]], 2.000000e+00
+; CHECK-NEXT:    [[FMUL1:%.*]] = fmul float [[FMUL0]], 4.000000e+00
+; CHECK-NEXT:    ret float [[FMUL1]]
+;
+  %fmul0 = fmul float %x, 2.0
+  %fmul1 = fmul float %fmul0, 4.0
+  ret float %fmul1
+}
+
+; Should be able to reassociate without nsz
+; (+0 * 2) * 4 = +0
+; (-0 * 2) * 4 = -0
+
+; (+0 * 8) = +0
+; (-0 * 8) = -0
+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:    ret float [[FMUL1]]
+;
+  %fmul0 = fmul reassoc float %x, 2.0
+  %fmul1 = fmul reassoc float %fmul0, 4.0
+  ret float %fmul1
+}
+
+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:    ret <2 x float> [[FMUL1]]
+;
+  %fmul0 = fmul reassoc <2 x float> %x, splat (float 2.0)
+  %fmul1 = fmul reassoc <2 x float> %fmul0, splat (float 4.0)
+  ret <2 x float> %fmul1
+}
+
+; (+0 * 2) * -4 = -0
+; (-0 * 2) * -4 = +0
+
+; (+0 * -8) = -0
+; (-0 * -8) = +0
+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:    ret float [[FMUL1]]
+;
+  %fmul0 = fmul reassoc float %x, 2.0
+  %fmul1 = fmul reassoc float %fmul0, -4.0
+  ret float %fmul1
+}
+
+; (+0 * -2) * 4 = -0
+; (-0 * -2) * 4 = +0
+
+; (+0 * -8) = -0
+; (-0 * -8) = +0
+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:    ret float [[FMUL1]]
+;
+  %fmul0 = fmul reassoc float %x, -2.0
+  %fmul1 = fmul reassoc float %fmul0, 4.0
+  ret float %fmul1
+}
+
+; Does reassociate already, unnecessarily requires nsz on both multiplies.
+define float @fmul_reassoc_nsz(float %x) {
+; CHECK-LABEL: define float @fmul_reassoc_nsz(
+; CHECK-SAME: float [[X:%.*]]) {
+; CHECK-NEXT:    [[FMUL1:%.*]] = fmul reassoc nsz float [[X]], 8.000000e+00
+; CHECK-NEXT:    ret float [[FMUL1]]
+;
+  %fmul0 = fmul nsz reassoc float %x, 2.0
+  %fmul1 = fmul nsz reassoc float %fmul0, 4.0
+  ret float %fmul1
+}
+
+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:    ret float [[FMUL1]]
+;
+  %fmul0 = fmul reassoc float %x, 4.0
+  %fmul1 = fmul reassoc float %fmul0, -0.0
+  ret float %fmul1
+}
+
+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]]
+;
+  %fmul0 = fmul reassoc float %x, -0.0
+  %fmul1 = fmul reassoc float %fmul0, 4.0
+  ret float %fmul1
+}

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.

This looks good as a starting point for the fix.

@arsenm arsenm merged commit 4f5071f into main Dec 11, 2025
16 checks passed
@arsenm arsenm deleted the users/arsenm/issue64697/instcombine-add-baseline-test-reassoc-fmul branch December 11, 2025 10:37
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:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants