[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