; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt < %s -passes=instcombine -S | FileCheck %s declare void @use_double(double) ;; ============================================================ ;; Core pattern: fcmp une (select C1, K, K'), (select C2, K, K') → xor ;; Truth table: {(0,0):false, (0,1):true, (1,0):true, (1,1):false} = 0b0110 ;; ============================================================ define i1 @fcmp_une_select_same_consts(double %a, double %b) { ; CHECK-LABEL: @fcmp_une_select_same_consts( ; CHECK-NEXT: [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V2:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V4:%.*]] = xor i1 [[V0]], [[V2]] ; CHECK-NEXT: ret i1 [[V4]] ; %v0 = fcmp ult double %b, 0.000000e+00 %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00 %v2 = fcmp ult double %a, 0.000000e+00 %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00 %v4 = fcmp une double %v1, %v3 ret i1 %v4 } ;; ============================================================ ;; fcmp oeq → xnor ;; Truth table: 0b1001 ;; ============================================================ define i1 @fcmp_oeq_select_same_consts(double %a, double %b) { ; CHECK-LABEL: @fcmp_oeq_select_same_consts( ; CHECK-NEXT: [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00 ; CHECK-NEXT: [[TMP1:%.*]] = fcmp oge double [[A:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V4:%.*]] = xor i1 [[V0]], [[TMP1]] ; CHECK-NEXT: ret i1 [[V4]] ; %v0 = fcmp ult double %b, 0.000000e+00 %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00 %v2 = fcmp ult double %a, 0.000000e+00 %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00 %v4 = fcmp oeq double %v1, %v3 ret i1 %v4 } ;; ============================================================ ;; fcmp one (ordered not-equal) → xor (same as une for non-NaN) ;; ============================================================ define i1 @fcmp_one_select_same_consts(double %a, double %b) { ; CHECK-LABEL: @fcmp_one_select_same_consts( ; CHECK-NEXT: [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V2:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V4:%.*]] = xor i1 [[V0]], [[V2]] ; CHECK-NEXT: ret i1 [[V4]] ; %v0 = fcmp ult double %b, 0.000000e+00 %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00 %v2 = fcmp ult double %a, 0.000000e+00 %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00 %v4 = fcmp one double %v1, %v3 ret i1 %v4 } ;; ============================================================ ;; fcmp ueq → xnor (same as oeq for non-NaN) ;; ============================================================ define i1 @fcmp_ueq_select_same_consts(double %a, double %b) { ; CHECK-LABEL: @fcmp_ueq_select_same_consts( ; CHECK-NEXT: [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00 ; CHECK-NEXT: [[TMP1:%.*]] = fcmp oge double [[A:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V4:%.*]] = xor i1 [[V0]], [[TMP1]] ; CHECK-NEXT: ret i1 [[V4]] ; %v0 = fcmp ult double %b, 0.000000e+00 %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00 %v2 = fcmp ult double %a, 0.000000e+00 %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00 %v4 = fcmp ueq double %v1, %v3 ret i1 %v4 } ;; ============================================================ ;; Swapped constants: (select C1, K1, K2) vs (select C2, K2, K1) ;; une with swapped → truth table 0b1001 → xnor ;; ============================================================ define i1 @fcmp_une_select_swapped_consts(double %a, double %b) { ; CHECK-LABEL: @fcmp_une_select_swapped_consts( ; CHECK-NEXT: [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00 ; CHECK-NEXT: [[TMP1:%.*]] = fcmp oge double [[A:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V4:%.*]] = xor i1 [[V0]], [[TMP1]] ; CHECK-NEXT: ret i1 [[V4]] ; %v0 = fcmp ult double %b, 0.000000e+00 %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00 %v2 = fcmp ult double %a, 0.000000e+00 %v3 = select i1 %v2, double 1.000000e+00, double -1.000000e+00 %v4 = fcmp une double %v1, %v3 ret i1 %v4 } ;; ============================================================ ;; Different constant pairs (not just -1/+1) ;; ============================================================ define i1 @fcmp_une_select_other_consts(double %a, double %b) { ; CHECK-LABEL: @fcmp_une_select_other_consts( ; CHECK-NEXT: [[V0:%.*]] = fcmp olt double [[B:%.*]], 5.000000e+00 ; CHECK-NEXT: [[V2:%.*]] = fcmp olt double [[A:%.*]], 5.000000e+00 ; CHECK-NEXT: [[V4:%.*]] = xor i1 [[V0]], [[V2]] ; CHECK-NEXT: ret i1 [[V4]] ; %v0 = fcmp olt double %b, 5.000000e+00 %v1 = select i1 %v0, double 4.200000e+01, double 1.337000e+02 %v2 = fcmp olt double %a, 5.000000e+00 %v3 = select i1 %v2, double 4.200000e+01, double 1.337000e+02 %v4 = fcmp une double %v1, %v3 ret i1 %v4 } ;; ============================================================ ;; Float type (not just double) ;; ============================================================ define i1 @fcmp_une_select_float(float %a, float %b) { ; CHECK-LABEL: @fcmp_une_select_float( ; CHECK-NEXT: [[V0:%.*]] = fcmp ult float [[B:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V2:%.*]] = fcmp ult float [[A:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V4:%.*]] = xor i1 [[V0]], [[V2]] ; CHECK-NEXT: ret i1 [[V4]] ; %v0 = fcmp ult float %b, 0.000000e+00 %v1 = select i1 %v0, float -1.000000e+00, float 1.000000e+00 %v2 = fcmp ult float %a, 0.000000e+00 %v3 = select i1 %v2, float -1.000000e+00, float 1.000000e+00 %v4 = fcmp une float %v1, %v3 ret i1 %v4 } ;; ============================================================ ;; Relational predicate: olt → now folds via truth table ;; Truth table: 0b0100 → and(C1, not(C2)) ;; ============================================================ define i1 @fcmp_olt_select(double %a, double %b) { ; CHECK-LABEL: @fcmp_olt_select( ; CHECK-NEXT: [[V2:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V3:%.*]] = fcmp oge double [[A1:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V4:%.*]] = and i1 [[V2]], [[V3]] ; CHECK-NEXT: ret i1 [[V4]] ; %v0 = fcmp ult double %b, 0.000000e+00 %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00 %v2 = fcmp ult double %a, 0.000000e+00 %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00 %v4 = fcmp olt double %v1, %v3 ret i1 %v4 } ;; ============================================================ ;; Relational predicate: ogt → truth table 0b0010 ;; → and(not(C1), C2) ;; ============================================================ define i1 @fcmp_ogt_select(double %a, double %b) { ; CHECK-LABEL: @fcmp_ogt_select( ; CHECK-NEXT: [[V0:%.*]] = fcmp oge double [[B:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V2:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V4:%.*]] = and i1 [[V0]], [[V2]] ; CHECK-NEXT: ret i1 [[V4]] ; %v0 = fcmp ult double %b, 0.000000e+00 %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00 %v2 = fcmp ult double %a, 0.000000e+00 %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00 %v4 = fcmp ogt double %v1, %v3 ret i1 %v4 } ;; ============================================================ ;; Vector type ;; ============================================================ define <2 x i1> @fcmp_une_select_vec(<2 x double> %a, <2 x double> %b) { ; CHECK-LABEL: @fcmp_une_select_vec( ; CHECK-NEXT: [[V0:%.*]] = fcmp ult <2 x double> [[B:%.*]], zeroinitializer ; CHECK-NEXT: [[V2:%.*]] = fcmp ult <2 x double> [[A:%.*]], zeroinitializer ; CHECK-NEXT: [[V4:%.*]] = xor <2 x i1> [[V0]], [[V2]] ; CHECK-NEXT: ret <2 x i1> [[V4]] ; %v0 = fcmp ult <2 x double> %b, zeroinitializer %v1 = select <2 x i1> %v0, <2 x double> , <2 x double> %v2 = fcmp ult <2 x double> %a, zeroinitializer %v3 = select <2 x i1> %v2, <2 x double> , <2 x double> %v4 = fcmp une <2 x double> %v1, %v3 ret <2 x i1> %v4 } ;; ============================================================ ;; Negative: vector constant folding can yield a mixed mask like ;; , so the truth-table fold must bail out. ;; ============================================================ define <2 x i1> @fcmp_une_select_vec_mixed_mask_no_fold(<2 x i1> %c0, <2 x i1> %c1) { ; CHECK-LABEL: @fcmp_une_select_vec_mixed_mask_no_fold( ; CHECK-NEXT: [[S0:%.*]] = select <2 x i1> [[C0:%.*]], <2 x double> , <2 x double> ; CHECK-NEXT: [[S1:%.*]] = select <2 x i1> [[C1:%.*]], <2 x double> , <2 x double> ; CHECK-NEXT: [[R:%.*]] = fcmp une <2 x double> [[S0]], [[S1]] ; CHECK-NEXT: ret <2 x i1> [[R]] %s0 = select <2 x i1> %c0, <2 x double> , <2 x double> %s1 = select <2 x i1> %c1, <2 x double> , <2 x double> %r = fcmp une <2 x double> %s0, %s1 ret <2 x i1> %r } ;; ============================================================ ;; Multi-use: une (xor) still folds — xor is single instruction ;; ============================================================ define i1 @fcmp_une_select_multi_use(double %a, double %b) { ; CHECK-LABEL: @fcmp_une_select_multi_use( ; CHECK-NEXT: [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V1:%.*]] = select i1 [[V0]], double -1.000000e+00, double 1.000000e+00 ; CHECK-NEXT: [[V2:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V3:%.*]] = select i1 [[V2]], double -1.000000e+00, double 1.000000e+00 ; CHECK-NEXT: call void @use_double(double [[V1]]) ; CHECK-NEXT: call void @use_double(double [[V3]]) ; CHECK-NEXT: [[V4:%.*]] = xor i1 [[V0]], [[V2]] ; CHECK-NEXT: ret i1 [[V4]] ; %v0 = fcmp ult double %b, 0.000000e+00 %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00 %v2 = fcmp ult double %a, 0.000000e+00 %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00 call void @use_double(double %v1) call void @use_double(double %v3) %v4 = fcmp une double %v1, %v3 ret i1 %v4 } ;; ============================================================ ;; Negative: multi-use + olt (needs 2 instructions, HasOneUse=false) ;; → createLogicFromTable returns nullptr ;; ============================================================ define i1 @fcmp_olt_select_multi_use_no_fold(double %a, double %b) { ; CHECK-LABEL: @fcmp_olt_select_multi_use_no_fold( ; CHECK-NEXT: [[V0:%.*]] = fcmp ult double [[B:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V1:%.*]] = select i1 [[V0]], double -1.000000e+00, double 1.000000e+00 ; CHECK-NEXT: [[V2:%.*]] = fcmp ult double [[A:%.*]], 0.000000e+00 ; CHECK-NEXT: [[V3:%.*]] = select i1 [[V2]], double -1.000000e+00, double 1.000000e+00 ; CHECK-NEXT: call void @use_double(double [[V1]]) ; CHECK-NEXT: call void @use_double(double [[V3]]) ; CHECK-NEXT: [[V4:%.*]] = fcmp olt double [[V1]], [[V3]] ; CHECK-NEXT: ret i1 [[V4]] ; %v0 = fcmp ult double %b, 0.000000e+00 %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00 %v2 = fcmp ult double %a, 0.000000e+00 %v3 = select i1 %v2, double -1.000000e+00, double 1.000000e+00 call void @use_double(double %v1) call void @use_double(double %v3) %v4 = fcmp olt double %v1, %v3 ret i1 %v4 } ;; ============================================================ ;; Negative: constants are the same (K1 == K2) ;; Selects simplify to constant, fcmp une → false ;; ============================================================ define i1 @fcmp_une_select_same_val(double %a, double %b) { ; CHECK-LABEL: @fcmp_une_select_same_val( ; CHECK-NEXT: ret i1 false ; %v0 = fcmp ult double %b, 0.000000e+00 %v1 = select i1 %v0, double 1.000000e+00, double 1.000000e+00 %v2 = fcmp ult double %a, 0.000000e+00 %v3 = select i1 %v2, double 1.000000e+00, double 1.000000e+00 %v4 = fcmp une double %v1, %v3 ret i1 %v4 } ;; ============================================================ ;; Different constant pairs across selects ;; All 4 combinations yield une=true → constant true ;; ============================================================ define i1 @fcmp_une_select_diff_pairs(double %a, double %b) { ; CHECK-LABEL: @fcmp_une_select_diff_pairs( ; CHECK-NEXT: ret i1 true ; %v0 = fcmp ult double %b, 0.000000e+00 %v1 = select i1 %v0, double -1.000000e+00, double 1.000000e+00 %v2 = fcmp ult double %a, 0.000000e+00 %v3 = select i1 %v2, double -2.000000e+00, double 3.000000e+00 %v4 = fcmp une double %v1, %v3 ret i1 %v4 } ;; ============================================================ ;; icmp ne with select of integer constants → xor ;; Truth table: 0b0110 ;; ============================================================ define i1 @icmp_ne_select_consts(i32 %a, i32 %b) { ; CHECK-LABEL: @icmp_ne_select_consts( ; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[B:%.*]], [[A:%.*]] ; CHECK-NEXT: [[R:%.*]] = icmp slt i32 [[TMP1]], 0 ; CHECK-NEXT: ret i1 [[R]] ; %s1 = icmp slt i32 %a, 0 %v1 = select i1 %s1, i32 -1, i32 1 %s2 = icmp slt i32 %b, 0 %v2 = select i1 %s2, i32 -1, i32 1 %r = icmp ne i32 %v1, %v2 ret i1 %r } ;; ============================================================ ;; icmp eq with select of integer constants → xnor ;; Truth table: 0b1001 ;; ============================================================ define i1 @icmp_eq_select_consts(i32 %a, i32 %b) { ; CHECK-LABEL: @icmp_eq_select_consts( ; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[B:%.*]], [[A:%.*]] ; CHECK-NEXT: [[R:%.*]] = icmp sgt i32 [[TMP1]], -1 ; CHECK-NEXT: ret i1 [[R]] ; %s1 = icmp slt i32 %a, 0 %v1 = select i1 %s1, i32 -1, i32 1 %s2 = icmp slt i32 %b, 0 %v2 = select i1 %s2, i32 -1, i32 1 %r = icmp eq i32 %v1, %v2 ret i1 %r } ;; ============================================================ ;; icmp slt with select of integer constants ;; Truth table: 0b0100 → and(C1, not(C2)) ;; ============================================================ define i1 @icmp_slt_select_consts(i32 %a, i32 %b) { ; CHECK-LABEL: @icmp_slt_select_consts( ; CHECK-NEXT: [[S1_INV:%.*]] = icmp slt i32 [[A:%.*]], 0 ; CHECK-NEXT: [[S2_INV:%.*]] = icmp sgt i32 [[B:%.*]], -1 ; CHECK-NEXT: [[R:%.*]] = select i1 [[S1_INV]], i1 [[S2_INV]], i1 false ; CHECK-NEXT: ret i1 [[R]] ; %s1 = icmp slt i32 %a, 0 %v1 = select i1 %s1, i32 -1, i32 1 %s2 = icmp slt i32 %b, 0 %v2 = select i1 %s2, i32 -1, i32 1 %r = icmp slt i32 %v1, %v2 ret i1 %r } ;; ============================================================ ;; icmp with swapped integer constants ;; icmp ne (select C1, -1, 1), (select C2, 1, -1) ;; Truth table: 0b1001 → xnor ;; ============================================================ define i1 @icmp_ne_select_swapped_consts(i32 %a, i32 %b) { ; CHECK-LABEL: @icmp_ne_select_swapped_consts( ; CHECK-NEXT: [[TMP1:%.*]] = xor i32 [[B:%.*]], [[A:%.*]] ; CHECK-NEXT: [[R:%.*]] = icmp sgt i32 [[TMP1]], -1 ; CHECK-NEXT: ret i1 [[R]] ; %s1 = icmp slt i32 %a, 0 %v1 = select i1 %s1, i32 -1, i32 1 %s2 = icmp slt i32 %b, 0 %v2 = select i1 %s2, i32 1, i32 -1 %r = icmp ne i32 %v1, %v2 ret i1 %r } ;; ============================================================ ;; Negative: vector condition should not fold via truth table. ;; ConstantFoldCompareInstOperands may yield a mixed mask like ;; , so we must bail out. ;; ============================================================ define <2 x i1> @icmp_eq_select_vec_cond_no_fold(<2 x i1> %c1, <2 x i1> %c2) { ; CHECK-LABEL: @icmp_eq_select_vec_cond_no_fold( ; CHECK-NEXT: [[S1:%.*]] = select <2 x i1> [[C1:%.*]], <2 x i32> , <2 x i32> ; CHECK-NEXT: [[S2:%.*]] = select <2 x i1> [[C2:%.*]], <2 x i32> , <2 x i32> ; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i32> [[S1]], [[S2]] ; CHECK-NEXT: ret <2 x i1> [[R]] ; %s1 = select <2 x i1> %c1, <2 x i32> , <2 x i32> %s2 = select <2 x i1> %c2, <2 x i32> , <2 x i32> %r = icmp eq <2 x i32> %s1, %s2 ret <2 x i1> %r } ;; ============================================================ ;; Negative: mixed vector/scalar select conditions should not ;; fold via truth table. ;; ============================================================ define <2 x i1> @icmp_eq_mixed_cond_no_fold(<2 x i1> %c1, i1 %c2) { ; CHECK-LABEL: @icmp_eq_mixed_cond_no_fold( ; CHECK-NEXT: [[S1:%.*]] = select <2 x i1> [[C1:%.*]], <2 x i32> , <2 x i32> ; CHECK-NEXT: [[S2:%.*]] = select i1 [[C2:%.*]], <2 x i32> , <2 x i32> ; CHECK-NEXT: [[R:%.*]] = icmp eq <2 x i32> [[S1]], [[S2]] ; CHECK-NEXT: ret <2 x i1> [[R]] ; %s1 = select <2 x i1> %c1, <2 x i32> , <2 x i32> %s2 = select i1 %c2, <2 x i32> , <2 x i32> %r = icmp eq <2 x i32> %s1, %s2 ret <2 x i1> %r }