[llvm] [X86] - Prevent the wrong fold of x86_avx512_mask_cmp_ss/sd to fcmp (PR #202321)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 8 04:25:21 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-x86

Author: Rohit Aggarwal (rohitaggarwal007)

<details>
<summary>Changes</summary>

The issue is based upon the SemiAnalysisAI by @<!-- -->jlebar. [058-mask-cmp-ss-imm-immediate-not-validated](https://github.com/SemiAnalysisAI/FuzzX/blob/master/x86/bugs/058-mask-cmp-ss-imm-immediate-not-validated/NOTES.md)

It is not a real bug, just a warning for the future fold implementation of mask_cmp → fcmp.

 There is non to fix as of now in the source code. Added a few comments and test cases for the future implementation of the folds.

@<!-- -->topperc @<!-- -->phoebewang 


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


2 Files Affected:

- (modified) llvm/lib/Target/X86/X86InstCombineIntrinsic.cpp (+11) 
- (modified) llvm/test/Transforms/InstCombine/X86/x86-avx512.ll (+78) 


``````````diff
diff --git a/llvm/lib/Target/X86/X86InstCombineIntrinsic.cpp b/llvm/lib/Target/X86/X86InstCombineIntrinsic.cpp
index 932b4a416a8d3..613f614bd8756 100644
--- a/llvm/lib/Target/X86/X86InstCombineIntrinsic.cpp
+++ b/llvm/lib/Target/X86/X86InstCombineIntrinsic.cpp
@@ -2424,6 +2424,17 @@ X86TTIImpl::instCombineIntrinsic(InstCombiner &IC, IntrinsicInst &II) const {
   case Intrinsic::x86_avx512_mask_cmp_sd: {
     // These intrinsics only demand the 0th element of their input vectors. If
     // we can simplify the input based on that, do so now.
+    //
+    // NOTE: Only operands 0 and 1 (the scalar-as-vector FP inputs) may be
+    // touched here. For the mask.cmp.ss/sd intrinsics, operand 2 is the
+    // comparison predicate (0..31) and operand 4 is the SAE/embedded-rounding
+    // control; both encode semantics that a plain fcmp cannot represent
+    // (signaling-vs-quiet predicates and FP-exception suppression). Do NOT add
+    // a fold that lowers these to fcmp/select unless it first proves the
+    // predicate is an SSE-compatible *quiet* form and the SAE operand is the
+    // exception-enabled default (4 == _MM_FROUND_CUR_DIRECTION); otherwise the
+    // signaling/QNaN-trap behavior would be silently dropped. The comi/ucomi
+    // intrinsics share this case but have only the two FP operands.
     bool MadeChange = false;
     Value *Arg0 = II.getArgOperand(0);
     Value *Arg1 = II.getArgOperand(1);
diff --git a/llvm/test/Transforms/InstCombine/X86/x86-avx512.ll b/llvm/test/Transforms/InstCombine/X86/x86-avx512.ll
index d89cf6b0bb986..56cdd11d229dc 100644
--- a/llvm/test/Transforms/InstCombine/X86/x86-avx512.ll
+++ b/llvm/test/Transforms/InstCombine/X86/x86-avx512.ll
@@ -802,6 +802,84 @@ define i8 @test_cmp_sd(<2 x double> %a, <2 x double> %b, i8 %mask) {
   ret i8 %3
 }
 
+; The mask.cmp.ss/sd predicate (arg2) and SAE/rounding (arg4) immediates carry
+; signaling-vs-quiet and exception-suppression semantics that a plain fcmp cannot
+; represent. InstCombine may only simplify the demanded (lane 0) operands; it must
+; never fold these intrinsics to fcmp, and must preserve arg2/arg4 verbatim.
+
+; predicate 20 = _CMP_NEQ_US (unordered, *signaling*) - aliases fcmp une but traps.
+define i8 @test_cmp_ss_signaling_pred(<4 x float> %a, <4 x float> %b, i8 %mask) {
+; CHECK-LABEL: @test_cmp_ss_signaling_pred(
+; CHECK-NEXT:    [[TMP1:%.*]] = tail call i8 @llvm.x86.avx512.mask.cmp.ss(<4 x float> [[A:%.*]], <4 x float> [[B:%.*]], i32 20, i8 [[MASK:%.*]], i32 4)
+; CHECK-NEXT:    ret i8 [[TMP1]]
+;
+  %1 = insertelement <4 x float> %a, float 1.000000e+00, i32 1
+  %2 = insertelement <4 x float> %1, float 2.000000e+00, i32 2
+  %3 = insertelement <4 x float> %2, float 3.000000e+00, i32 3
+  %4 = insertelement <4 x float> %b, float 4.000000e+00, i32 1
+  %5 = insertelement <4 x float> %4, float 5.000000e+00, i32 2
+  %6 = insertelement <4 x float> %5, float 6.000000e+00, i32 3
+  %7 = tail call i8 @llvm.x86.avx512.mask.cmp.ss(<4 x float> %3, <4 x float> %6, i32 20, i8 %mask, i32 4)
+  ret i8 %7
+}
+
+; SAE = 8 = _MM_FROUND_NO_EXC (suppress FP exceptions) - cannot be modeled by fcmp.
+define i8 @test_cmp_ss_sae(<4 x float> %a, <4 x float> %b, i8 %mask) {
+; CHECK-LABEL: @test_cmp_ss_sae(
+; CHECK-NEXT:    [[TMP1:%.*]] = tail call i8 @llvm.x86.avx512.mask.cmp.ss(<4 x float> [[A:%.*]], <4 x float> [[B:%.*]], i32 4, i8 [[MASK:%.*]], i32 8)
+; CHECK-NEXT:    ret i8 [[TMP1]]
+;
+  %1 = insertelement <4 x float> %a, float 1.000000e+00, i32 1
+  %2 = insertelement <4 x float> %1, float 2.000000e+00, i32 2
+  %3 = insertelement <4 x float> %2, float 3.000000e+00, i32 3
+  %4 = insertelement <4 x float> %b, float 4.000000e+00, i32 1
+  %5 = insertelement <4 x float> %4, float 5.000000e+00, i32 2
+  %6 = insertelement <4 x float> %5, float 6.000000e+00, i32 3
+  %7 = tail call i8 @llvm.x86.avx512.mask.cmp.ss(<4 x float> %3, <4 x float> %6, i32 4, i8 %mask, i32 8)
+  ret i8 %7
+}
+
+; Lane 0 is a QNaN constant with predicate 4 (_CMP_NEQ_UQ) and SAE = 8. The
+; intrinsic must survive unchanged (not be folded to an fcmp that ignores the
+; QNaN class / SAE suppression).
+define i8 @test_cmp_ss_qnan_lane0(<4 x float> %b, i8 %mask) {
+; CHECK-LABEL: @test_cmp_ss_qnan_lane0(
+; CHECK-NEXT:    [[TMP1:%.*]] = tail call i8 @llvm.x86.avx512.mask.cmp.ss(<4 x float> <float +qnan, float poison, float poison, float poison>, <4 x float> [[B:%.*]], i32 4, i8 [[MASK:%.*]], i32 8)
+; CHECK-NEXT:    ret i8 [[TMP1]]
+;
+  %1 = tail call i8 @llvm.x86.avx512.mask.cmp.ss(<4 x float> <float 0x7FF8000000000000, float 1.000000e+00, float 2.000000e+00, float 3.000000e+00>, <4 x float> %b, i32 4, i8 %mask, i32 8)
+  ret i8 %1
+}
+
+; sd variant: signaling predicate 20 + SAE 8 must both be preserved.
+define i8 @test_cmp_sd_signaling_sae(<2 x double> %a, <2 x double> %b, i8 %mask) {
+; CHECK-LABEL: @test_cmp_sd_signaling_sae(
+; CHECK-NEXT:    [[TMP1:%.*]] = tail call i8 @llvm.x86.avx512.mask.cmp.sd(<2 x double> [[A:%.*]], <2 x double> [[B:%.*]], i32 20, i8 [[MASK:%.*]], i32 8)
+; CHECK-NEXT:    ret i8 [[TMP1]]
+;
+  %1 = insertelement <2 x double> %a, double 1.000000e+00, i32 1
+  %2 = insertelement <2 x double> %b, double 2.000000e+00, i32 1
+  %3 = tail call i8 @llvm.x86.avx512.mask.cmp.sd(<2 x double> %1, <2 x double> %2, i32 20, i8 %mask, i32 8)
+  ret i8 %3
+}
+
+; Explicit negative test: even the most fcmp-like form (predicate 0 = _CMP_EQ_OQ,
+; ordered + quiet; rounding 4 = _MM_FROUND_CUR_DIRECTION, i.e. no SAE) must NOT be
+; folded to an fcmp. The intrinsic must survive untouched. This function's checks
+; are maintained by hand (CHECK-NOT is not emitted by update_test_checks.py); do
+; not regenerate it.
+define i8 @test_cmp_ss_not_folded_to_fcmp(<4 x float> %a, <4 x float> %b, i8 %mask) {
+; CHECK-LABEL: @test_cmp_ss_not_folded_to_fcmp(
+; CHECK-NOT:   fcmp
+; CHECK:       tail call i8 @llvm.x86.avx512.mask.cmp.ss({{.*}}, i32 0, i8 {{.*}}, i32 4)
+; CHECK-NOT:   fcmp
+;
+  %1 = insertelement <4 x float> %a, float 1.000000e+00, i32 1
+  %2 = insertelement <4 x float> %b, float 2.000000e+00, i32 1
+  %3 = tail call i8 @llvm.x86.avx512.mask.cmp.ss(<4 x float> %1, <4 x float> %2, i32 0, i8 %mask, i32 4)
+  ret i8 %3
+}
+
 define i64 @test(float %f, double %d) {
 ;
 ; CHECK-LABEL: @test(

``````````

</details>


https://github.com/llvm/llvm-project/pull/202321


More information about the llvm-commits mailing list