[llvm] [InstCombine] Freeze X in fabs(X) op fabs(X) -> X op X to fix undef unsoundness (PR #202217)

via llvm-commits llvm-commits at lists.llvm.org
Sun Jun 7 11:17:32 PDT 2026


https://github.com/lijinpei-amd updated https://github.com/llvm/llvm-project/pull/202217

>From 0b49c04c2337276755f240e2604c09635b0d8e4d Mon Sep 17 00:00:00 2001
From: Li Jinpei <jinpli at amd.com>
Date: Mon, 8 Jun 2026 01:31:37 +0800
Subject: [PATCH 1/2] [InstCombine] Add tests for fabs(X) op fabs(X) folds
 (NFC)

Pre-commit tests for the fabs(X) * fabs(X) -> X * X and fabs(X) / fabs(X) ->
X / X folds across the nnan/ninf/nsz/fast flag combinations, plus a noundef
argument. These currently reuse X without a freeze; a follow-up fixes the undef
unsoundness and updates the CHECK lines.
---
 llvm/test/Transforms/InstCombine/fdiv.ll | 26 ++++++++++++--
 llvm/test/Transforms/InstCombine/fmul.ll | 44 ++++++++++++++++++++++--
 2 files changed, 65 insertions(+), 5 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/fdiv.ll b/llvm/test/Transforms/InstCombine/fdiv.ll
index e992ba59b8574..8bac5fd37a6ad 100644
--- a/llvm/test/Transforms/InstCombine/fdiv.ll
+++ b/llvm/test/Transforms/InstCombine/fdiv.ll
@@ -591,7 +591,7 @@ define float @fdiv_fneg1_extra_use(float %x, float %y) {
 
 define float @fabs_same_op(float %x) {
 ; CHECK-LABEL: @fabs_same_op(
-; CHECK-NEXT:    [[R:%.*]] = fdiv float [[X:%.*]], [[X]]
+; CHECK-NEXT:    [[R:%.*]] = fdiv float [[X_FR:%.*]], [[X_FR]]
 ; CHECK-NEXT:    ret float [[R]]
 ;
   %a = call float @llvm.fabs.f32(float %x)
@@ -601,9 +601,9 @@ define float @fabs_same_op(float %x) {
 
 define float @fabs_same_op_extra_use(float %x) {
 ; CHECK-LABEL: @fabs_same_op_extra_use(
-; CHECK-NEXT:    [[A:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])
+; CHECK-NEXT:    [[A:%.*]] = call float @llvm.fabs.f32(float [[X_FR:%.*]])
 ; CHECK-NEXT:    call void @use_f32(float [[A]])
-; CHECK-NEXT:    [[R:%.*]] = fdiv reassoc ninf float [[X]], [[X]]
+; CHECK-NEXT:    [[R:%.*]] = fdiv reassoc ninf float [[X_FR]], [[X_FR]]
 ; CHECK-NEXT:    ret float [[R]]
 ;
   %a = call float @llvm.fabs.f32(float %x)
@@ -612,6 +612,26 @@ define float @fabs_same_op_extra_use(float %x) {
   ret float %r
 }
 
+define float @fabs_same_op_ninf(float %x) {
+; CHECK-LABEL: @fabs_same_op_ninf(
+; CHECK-NEXT:    [[R:%.*]] = fdiv ninf float [[X:%.*]], [[X]]
+; CHECK-NEXT:    ret float [[R]]
+;
+  %a = call float @llvm.fabs.f32(float %x)
+  %r = fdiv ninf float %a, %a
+  ret float %r
+}
+
+define float @fabs_same_op_nsz(float %x) {
+; CHECK-LABEL: @fabs_same_op_nsz(
+; CHECK-NEXT:    [[R:%.*]] = fdiv nsz float [[X_FR:%.*]], [[X_FR]]
+; CHECK-NEXT:    ret float [[R]]
+;
+  %a = call float @llvm.fabs.f32(float %x)
+  %r = fdiv nsz float %a, %a
+  ret float %r
+}
+
 define float @fabs_fabs(float %x, float %y) {
 ; CHECK-LABEL: @fabs_fabs(
 ; CHECK-NEXT:    [[TMP1:%.*]] = fdiv float [[X:%.*]], [[Y:%.*]]
diff --git a/llvm/test/Transforms/InstCombine/fmul.ll b/llvm/test/Transforms/InstCombine/fmul.ll
index c93982f814e43..936f8ec6de5fc 100644
--- a/llvm/test/Transforms/InstCombine/fmul.ll
+++ b/llvm/test/Transforms/InstCombine/fmul.ll
@@ -462,7 +462,7 @@ declare float @llvm.fabs.f32(float) nounwind readnone
 
 define float @fabs_squared(float %x) {
 ; CHECK-LABEL: @fabs_squared(
-; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X:%.*]], [[X]]
+; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X_FR:%.*]], [[X_FR]]
 ; CHECK-NEXT:    ret float [[MUL]]
 ;
   %x.fabs = call float @llvm.fabs.f32(float %x)
@@ -472,7 +472,7 @@ define float @fabs_squared(float %x) {
 
 define float @fabs_squared_fast(float %x) {
 ; CHECK-LABEL: @fabs_squared_fast(
-; CHECK-NEXT:    [[MUL:%.*]] = fmul fast float [[X:%.*]], [[X]]
+; CHECK-NEXT:    [[MUL:%.*]] = fmul fast float [[X_FR:%.*]], [[X_FR]]
 ; CHECK-NEXT:    ret float [[MUL]]
 ;
   %x.fabs = call float @llvm.fabs.f32(float %x)
@@ -480,6 +480,46 @@ define float @fabs_squared_fast(float %x) {
   ret float %mul
 }
 
+define float @fabs_squared_nnan(float %x) {
+; CHECK-LABEL: @fabs_squared_nnan(
+; CHECK-NEXT:    [[MUL:%.*]] = fmul nnan float [[X:%.*]], [[X]]
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %x.fabs = call float @llvm.fabs.f32(float %x)
+  %mul = fmul nnan float %x.fabs, %x.fabs
+  ret float %mul
+}
+
+define float @fabs_squared_ninf(float %x) {
+; CHECK-LABEL: @fabs_squared_ninf(
+; CHECK-NEXT:    [[MUL:%.*]] = fmul ninf float [[X:%.*]], [[X]]
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %x.fabs = call float @llvm.fabs.f32(float %x)
+  %mul = fmul ninf float %x.fabs, %x.fabs
+  ret float %mul
+}
+
+define float @fabs_squared_nsz(float %x) {
+; CHECK-LABEL: @fabs_squared_nsz(
+; CHECK-NEXT:    [[MUL:%.*]] = fmul nsz float [[X_FR:%.*]], [[X_FR]]
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %x.fabs = call float @llvm.fabs.f32(float %x)
+  %mul = fmul nsz float %x.fabs, %x.fabs
+  ret float %mul
+}
+
+define float @fabs_squared_noundef(float noundef %x) {
+; CHECK-LABEL: @fabs_squared_noundef(
+; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X:%.*]], [[X]]
+; CHECK-NEXT:    ret float [[MUL]]
+;
+  %x.fabs = call float @llvm.fabs.f32(float %x)
+  %mul = fmul float %x.fabs, %x.fabs
+  ret float %mul
+}
+
 define float @fabs_fabs(float %x, float %y) {
 ; CHECK-LABEL: @fabs_fabs(
 ; CHECK-NEXT:    [[TMP1:%.*]] = fmul float [[X:%.*]], [[Y:%.*]]

>From 87b6b4f0b18351b5df05f1d6b5175264887e5971 Mon Sep 17 00:00:00 2001
From: Li Jinpei <jinpli at amd.com>
Date: Mon, 8 Jun 2026 01:32:47 +0800
Subject: [PATCH 2/2] [InstCombine] Freeze X in fabs(X) op fabs(X) -> X op X to
 fix undef unsoundness

Folding
```
Y = fabs(X);
Z = Y * Y;
```
into
```
Z = X * X;
```
is unsound when X is undef, because each use of X may take a different value.
The same applies to fdiv fabs(X), fabs(X) -> X / X (which may then fold to 1.0).

Fix this by freezing X when needed. The freeze is unnecessary when the fmul or
fdiv carries a fast-math flag that can produce poison (nnan or ninf): undef can
be refined to a value that makes the result poison, and poison can be refined to
any value, so reusing X is sound without the freeze.
---
 llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp | 5 ++++-
 llvm/test/Transforms/InstCombine/fdiv.ll                 | 6 ++++--
 llvm/test/Transforms/InstCombine/fmul.ll                 | 6 ++++--
 3 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index 17ac09c10f41c..5f41151f178af 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -616,8 +616,11 @@ Instruction *InstCombinerImpl::foldFPSignBitOps(BinaryOperator &I) {
 
   // fabs(X) * fabs(X) -> X * X
   // fabs(X) / fabs(X) -> X / X
-  if (Op0 == Op1 && match(Op0, m_FAbs(m_Value(X))))
+  if (Op0 == Op1 && match(Op0, m_FAbs(m_Value(X)))) {
+    if (!I.hasNoNaNs() && !I.hasNoInfs() && !isGuaranteedNotToBeUndef(X))
+      X = Builder.CreateFreeze(X, X->getName() + ".fr");
     return BinaryOperator::CreateWithCopiedFlags(Opcode, X, X, &I);
+  }
 
   // fabs(X) * fabs(Y) --> fabs(X * Y)
   // fabs(X) / fabs(Y) --> fabs(X / Y)
diff --git a/llvm/test/Transforms/InstCombine/fdiv.ll b/llvm/test/Transforms/InstCombine/fdiv.ll
index 8bac5fd37a6ad..38dd8a951c7e2 100644
--- a/llvm/test/Transforms/InstCombine/fdiv.ll
+++ b/llvm/test/Transforms/InstCombine/fdiv.ll
@@ -591,7 +591,8 @@ define float @fdiv_fneg1_extra_use(float %x, float %y) {
 
 define float @fabs_same_op(float %x) {
 ; CHECK-LABEL: @fabs_same_op(
-; CHECK-NEXT:    [[R:%.*]] = fdiv float [[X_FR:%.*]], [[X_FR]]
+; CHECK-NEXT:    [[X_FR:%.*]] = freeze float [[X:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = fdiv float [[X_FR]], [[X_FR]]
 ; CHECK-NEXT:    ret float [[R]]
 ;
   %a = call float @llvm.fabs.f32(float %x)
@@ -624,7 +625,8 @@ define float @fabs_same_op_ninf(float %x) {
 
 define float @fabs_same_op_nsz(float %x) {
 ; CHECK-LABEL: @fabs_same_op_nsz(
-; CHECK-NEXT:    [[R:%.*]] = fdiv nsz float [[X_FR:%.*]], [[X_FR]]
+; CHECK-NEXT:    [[X_FR:%.*]] = freeze float [[X:%.*]]
+; CHECK-NEXT:    [[R:%.*]] = fdiv nsz float [[X_FR]], [[X_FR]]
 ; CHECK-NEXT:    ret float [[R]]
 ;
   %a = call float @llvm.fabs.f32(float %x)
diff --git a/llvm/test/Transforms/InstCombine/fmul.ll b/llvm/test/Transforms/InstCombine/fmul.ll
index 936f8ec6de5fc..66a8839e16516 100644
--- a/llvm/test/Transforms/InstCombine/fmul.ll
+++ b/llvm/test/Transforms/InstCombine/fmul.ll
@@ -462,7 +462,8 @@ declare float @llvm.fabs.f32(float) nounwind readnone
 
 define float @fabs_squared(float %x) {
 ; CHECK-LABEL: @fabs_squared(
-; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X_FR:%.*]], [[X_FR]]
+; CHECK-NEXT:    [[X_FR:%.*]] = freeze float [[X:%.*]]
+; CHECK-NEXT:    [[MUL:%.*]] = fmul float [[X_FR]], [[X_FR]]
 ; CHECK-NEXT:    ret float [[MUL]]
 ;
   %x.fabs = call float @llvm.fabs.f32(float %x)
@@ -502,7 +503,8 @@ define float @fabs_squared_ninf(float %x) {
 
 define float @fabs_squared_nsz(float %x) {
 ; CHECK-LABEL: @fabs_squared_nsz(
-; CHECK-NEXT:    [[MUL:%.*]] = fmul nsz float [[X_FR:%.*]], [[X_FR]]
+; CHECK-NEXT:    [[X_FR:%.*]] = freeze float [[X:%.*]]
+; CHECK-NEXT:    [[MUL:%.*]] = fmul nsz float [[X_FR]], [[X_FR]]
 ; CHECK-NEXT:    ret float [[MUL]]
 ;
   %x.fabs = call float @llvm.fabs.f32(float %x)



More information about the llvm-commits mailing list