Skip to content

Commit e39f33c

Browse files
phoebewangtru
authored andcommitted
[X86][CCMP] Fix invalid CCMP emission (llvm#211161)
This patch ports AArch64's negation-aware conjunction algorithm to fix invalid CCMP emission when OR nested inside an AND. Example: https://godbolt.org/z/ave7f61hK Before the change, the above case returns 5 rather the 9 when CCMP enabled. Assisted-by: Claude Opus 4.8 (cherry picked from commit 45b3065)
1 parent 060d53f commit e39f33c

2 files changed

Lines changed: 199 additions & 76 deletions

File tree

llvm/lib/Target/X86/X86ISelLowering.cpp

Lines changed: 139 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -25494,52 +25494,102 @@ static SDValue LowerSELECTWithCmpZero(SDValue CmpVal, SDValue LHS, SDValue RHS,
2549425494
return SDValue();
2549525495
}
2549625496

25497-
// Return true if Val is an integer ISD::SETCC or an AND/OR tree thereof,
25498-
// suitable for lowering to a CCMP chain.
25499-
static bool canEmitConjunctionForCCMP(SDValue Val) {
25500-
unsigned Opc = Val.getOpcode();
25501-
if (Opc == ISD::SETCC)
25502-
return Val.getOperand(0).getSimpleValueType().isInteger();
25503-
if (Opc == ISD::AND && Val.hasOneUse())
25504-
return canEmitConjunctionForCCMP(Val.getOperand(0)) &&
25505-
canEmitConjunctionForCCMP(Val.getOperand(1));
25506-
// For OR, at least one operand must be a leaf SETCC so the DCF of the right
25507-
// CCMP is unambiguous.
25508-
if (Opc == ISD::OR && Val.hasOneUse())
25509-
return (Val.getOperand(0).getOpcode() == ISD::SETCC ||
25510-
Val.getOperand(1).getOpcode() == ISD::SETCC) &&
25511-
canEmitConjunctionForCCMP(Val.getOperand(0)) &&
25512-
canEmitConjunctionForCCMP(Val.getOperand(1));
25497+
/// Returns true if \p Val is a tree of integer AND/OR/SETCC operations that
25498+
/// can be expressed as a CCMP conjunction. This mirrors AArch64's
25499+
/// canEmitConjunction and tracks the negation bookkeeping required to lower
25500+
/// arbitrarily nested AND/OR trees (e.g. AND(cc, OR(cc, cc))) correctly.
25501+
/// \param CanNegate Set to true if the whole sub-tree can be negated just by
25502+
/// inverting the conditions on the SETCC leaves.
25503+
/// \param MustBeFirst Set to true if this sub-tree needs to be negated but
25504+
/// cannot be negated naturally, so it must be emitted first.
25505+
/// \param WillNegate True when the result of this sub-expression must be
25506+
/// negated (i.e. the outer expression is an OR).
25507+
static bool canEmitConjunctionForCCMP(SDValue Val, bool &CanNegate,
25508+
bool &MustBeFirst, bool WillNegate,
25509+
unsigned Depth = 0) {
25510+
if (!Val.hasOneUse())
25511+
return false;
25512+
unsigned Opcode = Val.getOpcode();
25513+
if (Opcode == ISD::SETCC) {
25514+
if (!Val.getOperand(0).getSimpleValueType().isInteger())
25515+
return false;
25516+
CanNegate = true;
25517+
MustBeFirst = false;
25518+
return true;
25519+
}
25520+
// Protect against exponential runtime and stack overflow.
25521+
if (Depth > 6)
25522+
return false;
25523+
if (Opcode == ISD::AND || Opcode == ISD::OR) {
25524+
bool IsOR = Opcode == ISD::OR;
25525+
SDValue O0 = Val.getOperand(0);
25526+
SDValue O1 = Val.getOperand(1);
25527+
bool CanNegateL, MustBeFirstL;
25528+
if (!canEmitConjunctionForCCMP(O0, CanNegateL, MustBeFirstL, IsOR,
25529+
Depth + 1))
25530+
return false;
25531+
bool CanNegateR, MustBeFirstR;
25532+
if (!canEmitConjunctionForCCMP(O1, CanNegateR, MustBeFirstR, IsOR,
25533+
Depth + 1))
25534+
return false;
25535+
25536+
if (MustBeFirstL && MustBeFirstR)
25537+
return false;
25538+
25539+
if (IsOR) {
25540+
// For an OR we need to be able to naturally negate at least one side or
25541+
// we cannot do the transformation at all.
25542+
if (!CanNegateL && !CanNegateR)
25543+
return false;
25544+
// If the OR's result will be negated and both leaves can be negated
25545+
// naturally, then this sub-tree as a whole negates naturally.
25546+
CanNegate = WillNegate && CanNegateL && CanNegateR;
25547+
MustBeFirst = !CanNegate;
25548+
} else {
25549+
assert(Opcode == ISD::AND && "Must be OR or AND");
25550+
// We cannot naturally negate an AND operation.
25551+
CanNegate = false;
25552+
MustBeFirst = MustBeFirstL || MustBeFirstR;
25553+
}
25554+
return true;
25555+
}
2551325556
return false;
2551425557
}
2551525558

25516-
// Recursively emit a CCMP chain for an AND/OR tree of integer SETCCs.
25517-
// CCOp: incoming flags value (null for the first/root comparison)
25518-
// Predicate: condition under which CCOp was produced (COND_INVALID at root)
25519-
// OutCC: set to the condition code to test after the whole chain
25520-
// Returns the flags-producing node (SUB or CCMP).
25521-
//
25522-
// AND(cc1, cc2): emit cc1 first; CCMP(cc2) fires when cc1 is true.
25523-
// SrcCC = cc1, DCF forces cc2 false when cc1 is false.
25524-
// OR(cc1, cc2): emit cc1 first; CCMP(cc2) fires when cc1 is false.
25525-
// SrcCC = ~cc1, DCF forces cc2 true when cc1 is true.
25559+
/// Emit a conjunction or disjunction tree as a CMP followed by a chain of
25560+
/// CCMP operations. Mirrors AArch64's emitConjunctionRec.
25561+
/// CCOp: incoming flags value (null for the first/root comparison)
25562+
/// Predicate: condition under which CCOp was produced (COND_INVALID at root)
25563+
/// Negate: true if this sub-tree should be negated by inverting the
25564+
/// conditions on its SETCC leaves
25565+
/// OutCC: set to the condition code to test after the whole chain
25566+
/// Returns the flags-producing node (CMP/SUB or CCMP).
2552625567
static SDValue emitConjunctionForCCMPRec(SDValue Val, X86::CondCode &OutCC,
25527-
SDValue CCOp, X86::CondCode Predicate,
25568+
bool Negate, SDValue CCOp,
25569+
X86::CondCode Predicate,
2552825570
SelectionDAG &DAG,
2552925571
const X86Subtarget &Subtarget) {
2553025572
SDLoc DL(Val);
2553125573

25532-
if (Val.getOpcode() == ISD::SETCC) {
25574+
unsigned Opcode = Val.getOpcode();
25575+
if (Opcode == ISD::SETCC) {
2553325576
SDValue LHS = Val.getOperand(0), RHS = Val.getOperand(1);
2553425577
ISD::CondCode CC = cast<CondCodeSDNode>(Val.getOperand(2))->get();
25578+
if (Negate)
25579+
CC = ISD::getSetCCInverse(CC, LHS.getValueType());
2553525580
X86::CondCode X86CC = TranslateX86CC(CC, DL, /*IsFP=*/false, LHS, RHS, DAG);
2553625581
assert(X86CC != X86::COND_INVALID);
2553725582
OutCC = X86CC;
2553825583

2553925584
SDValue Flags = EmitCmp(LHS, RHS, X86CC, DL, DAG, Subtarget);
25585+
// Produce a normal comparison if we are first in the chain.
2554025586
if (!CCOp)
2554125587
return Flags;
2554225588

25589+
// Otherwise produce a CCMP. The CCMP executes (and updates EFLAGS) only
25590+
// when Predicate holds; when it is skipped it forces the default condition
25591+
// flags, which must make OutCC evaluate to false. That default is encoded
25592+
// from the opposite of X86CC.
2554325593
SDNode *FlagsNode = Flags.getNode();
2554425594
X86::CondCode DCFCode = X86::GetOppositeBranchCondition(X86CC);
2554525595
SDValue CFlags = DAG.getTargetConstant(
@@ -25549,47 +25599,78 @@ static SDValue emitConjunctionForCCMPRec(SDValue Val, X86::CondCode &OutCC,
2554925599
{FlagsNode->getOperand(0), FlagsNode->getOperand(1),
2555025600
CFlags, SrcCC, CCOp});
2555125601
}
25602+
assert(Val.hasOneUse() && "Valid conjunction/disjunction tree");
2555225603

25553-
bool IsOR = Val.getOpcode() == ISD::OR;
25554-
SDValue LHS = Val.getOperand(0), RHS = Val.getOperand(1);
25555-
25556-
// For OR, the right subtree must be a leaf SETCC so its DCF unambiguously
25557-
// forces the outcome true when skipped. OR is commutative, so swap if needed.
25558-
if (IsOR && RHS.getOpcode() != ISD::SETCC)
25559-
std::swap(LHS, RHS);
25560-
25561-
// Emit the left subtree first (provides CCOp for the right subtree's CCMP).
25562-
X86::CondCode LHSCC;
25563-
SDValue CmpL =
25564-
emitConjunctionForCCMPRec(LHS, LHSCC, CCOp, Predicate, DAG, Subtarget);
25604+
bool IsOR = Opcode == ISD::OR;
2556525605

25566-
// For AND: right CCMP fires when left is true, SrcCC = LHSCC.
25567-
// For OR: right CCMP fires when left is false, SrcCC = !LHSCC.
25568-
X86::CondCode NextPred =
25569-
IsOR ? X86::GetOppositeBranchCondition(LHSCC) : LHSCC;
25606+
SDValue LHS = Val.getOperand(0);
25607+
bool CanNegateL, MustBeFirstL;
25608+
bool ValidL = canEmitConjunctionForCCMP(LHS, CanNegateL, MustBeFirstL, IsOR);
25609+
assert(ValidL && "Valid conjunction/disjunction tree");
25610+
(void)ValidL;
2557025611

25571-
SDValue CmpR =
25572-
emitConjunctionForCCMPRec(RHS, OutCC, CmpL, NextPred, DAG, Subtarget);
25612+
SDValue RHS = Val.getOperand(1);
25613+
bool CanNegateR, MustBeFirstR;
25614+
bool ValidR = canEmitConjunctionForCCMP(RHS, CanNegateR, MustBeFirstR, IsOR);
25615+
assert(ValidR && "Valid conjunction/disjunction tree");
25616+
(void)ValidR;
2557325617

25574-
// For OR, patch the DCF of the right leaf's CCMP to force OutCC TRUE when
25575-
// the CCMP is skipped (i.e. when the left condition was already true).
25576-
if (IsOR && CmpR.getOpcode() == X86ISD::CCMP) {
25577-
SDValue CFlags = DAG.getTargetConstant(
25578-
X86::getCCMPCondFlagsFromCondCode(OutCC), DL, MVT::i8);
25579-
CmpR = DAG.getNode(X86ISD::CCMP, DL, MVT::i32,
25580-
{CmpR.getOperand(0), CmpR.getOperand(1), CFlags,
25581-
CmpR.getOperand(3), CmpR.getOperand(4)});
25618+
// Swap the sub-tree that must come first to the right side.
25619+
if (MustBeFirstL) {
25620+
assert(!MustBeFirstR && "Valid conjunction/disjunction tree");
25621+
std::swap(LHS, RHS);
25622+
std::swap(CanNegateL, CanNegateR);
25623+
std::swap(MustBeFirstL, MustBeFirstR);
2558225624
}
25583-
return CmpR;
25625+
25626+
bool NegateR, NegateAfterR, NegateL, NegateAfterAll;
25627+
if (IsOR) {
25628+
// Swap the sub-tree that we can negate naturally to the left.
25629+
if (!CanNegateL) {
25630+
assert(CanNegateR && "at least one side must be negatable");
25631+
assert(!MustBeFirstR && "invalid conjunction/disjunction tree");
25632+
assert(!Negate);
25633+
std::swap(LHS, RHS);
25634+
NegateR = false;
25635+
NegateAfterR = true;
25636+
} else {
25637+
// Negate the left sub-tree if possible, otherwise negate the result.
25638+
NegateR = CanNegateR;
25639+
NegateAfterR = !CanNegateR;
25640+
}
25641+
NegateL = true;
25642+
NegateAfterAll = !Negate;
25643+
} else {
25644+
assert(Opcode == ISD::AND && "Valid conjunction/disjunction tree");
25645+
assert(!Negate && "Valid conjunction/disjunction tree");
25646+
NegateL = false;
25647+
NegateR = false;
25648+
NegateAfterR = false;
25649+
NegateAfterAll = false;
25650+
}
25651+
25652+
// Emit sub-trees. The right sub-tree is emitted first so its flags feed the
25653+
// left sub-tree's CCMP chain.
25654+
X86::CondCode RHSCC;
25655+
SDValue CmpR = emitConjunctionForCCMPRec(RHS, RHSCC, NegateR, CCOp, Predicate,
25656+
DAG, Subtarget);
25657+
if (NegateAfterR)
25658+
RHSCC = X86::GetOppositeBranchCondition(RHSCC);
25659+
SDValue CmpL = emitConjunctionForCCMPRec(LHS, OutCC, NegateL, CmpR, RHSCC,
25660+
DAG, Subtarget);
25661+
if (NegateAfterAll)
25662+
OutCC = X86::GetOppositeBranchCondition(OutCC);
25663+
return CmpL;
2558425664
}
2558525665

2558625666
static SDValue emitConjunctionForCCMP(SDValue Val, X86::CondCode &OutCC,
2558725667
SelectionDAG &DAG,
2558825668
const X86Subtarget &Subtarget) {
25589-
if (!canEmitConjunctionForCCMP(Val))
25669+
bool DummyCanNegate, DummyMustBeFirst;
25670+
if (!canEmitConjunctionForCCMP(Val, DummyCanNegate, DummyMustBeFirst, false))
2559025671
return SDValue();
25591-
return emitConjunctionForCCMPRec(Val, OutCC, SDValue(), X86::COND_INVALID,
25592-
DAG, Subtarget);
25672+
return emitConjunctionForCCMPRec(Val, OutCC, /*Negate=*/false, SDValue(),
25673+
X86::COND_INVALID, DAG, Subtarget);
2559325674
}
2559425675

2559525676
SDValue X86TargetLowering::LowerSELECT(SDValue Op, SelectionDAG &DAG) const {

llvm/test/CodeGen/X86/apx/ccmp.ll

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2103,16 +2103,16 @@ define i32 @ccmp_cmov_and(i32 %a, i32 %b, i32 %c, i32 %d) {
21032103
; CHECK-LABEL: ccmp_cmov_and:
21042104
; CHECK: # %bb.0: # %entry
21052105
; CHECK-NEXT: movl %edi, %eax # encoding: [0x89,0xf8]
2106-
; CHECK-NEXT: cmpl %ecx, %esi # encoding: [0x39,0xce]
2107-
; CHECK-NEXT: ccmpnel {dfv=} %edx, %edi # encoding: [0x62,0xf4,0x04,0x05,0x39,0xd7]
2108-
; CHECK-NEXT: cmovll %esi, %eax # encoding: [0x0f,0x4c,0xc6]
2106+
; CHECK-NEXT: cmpl %edx, %edi # encoding: [0x39,0xd7]
2107+
; CHECK-NEXT: ccmpll {dfv=zf} %ecx, %esi # encoding: [0x62,0xf4,0x14,0x0c,0x39,0xce]
2108+
; CHECK-NEXT: cmovnel %esi, %eax # encoding: [0x0f,0x45,0xc6]
21092109
; CHECK-NEXT: retq # encoding: [0xc3]
21102110
;
21112111
; NDD-LABEL: ccmp_cmov_and:
21122112
; NDD: # %bb.0: # %entry
2113-
; NDD-NEXT: cmpl %ecx, %esi # encoding: [0x39,0xce]
2114-
; NDD-NEXT: ccmpnel {dfv=} %edx, %edi # encoding: [0x62,0xf4,0x04,0x05,0x39,0xd7]
2115-
; NDD-NEXT: cmovll %esi, %edi, %eax # encoding: [0x62,0xf4,0x7c,0x18,0x4c,0xfe]
2113+
; NDD-NEXT: cmpl %edx, %edi # encoding: [0x39,0xd7]
2114+
; NDD-NEXT: ccmpll {dfv=zf} %ecx, %esi # encoding: [0x62,0xf4,0x14,0x0c,0x39,0xce]
2115+
; NDD-NEXT: cmovnel %esi, %edi, %eax # encoding: [0x62,0xf4,0x7c,0x18,0x45,0xfe]
21162116
; NDD-NEXT: retq # encoding: [0xc3]
21172117
;
21182118
; ZU_COMMON-LABEL: ccmp_cmov_and:
@@ -2136,17 +2136,17 @@ define i32 @ccmp_cmov_and_or(i32 %a, i32 %b, i32 %c, i32 %d) {
21362136
; CHECK-LABEL: ccmp_cmov_and_or:
21372137
; CHECK: # %bb.0: # %entry
21382138
; CHECK-NEXT: movl %edi, %eax # encoding: [0x89,0xf8]
2139-
; CHECK-NEXT: cmpl %ecx, %esi # encoding: [0x39,0xce]
2140-
; CHECK-NEXT: ccmpnel {dfv=} %edx, %edi # encoding: [0x62,0xf4,0x04,0x05,0x39,0xd7]
2141-
; CHECK-NEXT: ccmpgel {dfv=} %ecx, %edi # encoding: [0x62,0xf4,0x04,0x0d,0x39,0xcf]
2139+
; CHECK-NEXT: cmpl %edx, %edi # encoding: [0x39,0xd7]
2140+
; CHECK-NEXT: ccmpll {dfv=zf} %ecx, %esi # encoding: [0x62,0xf4,0x14,0x0c,0x39,0xce]
2141+
; CHECK-NEXT: ccmpel {dfv=} %ecx, %edi # encoding: [0x62,0xf4,0x04,0x04,0x39,0xcf]
21422142
; CHECK-NEXT: cmovgl %esi, %eax # encoding: [0x0f,0x4f,0xc6]
21432143
; CHECK-NEXT: retq # encoding: [0xc3]
21442144
;
21452145
; NDD-LABEL: ccmp_cmov_and_or:
21462146
; NDD: # %bb.0: # %entry
2147-
; NDD-NEXT: cmpl %ecx, %esi # encoding: [0x39,0xce]
2148-
; NDD-NEXT: ccmpnel {dfv=} %edx, %edi # encoding: [0x62,0xf4,0x04,0x05,0x39,0xd7]
2149-
; NDD-NEXT: ccmpgel {dfv=} %ecx, %edi # encoding: [0x62,0xf4,0x04,0x0d,0x39,0xcf]
2147+
; NDD-NEXT: cmpl %edx, %edi # encoding: [0x39,0xd7]
2148+
; NDD-NEXT: ccmpll {dfv=zf} %ecx, %esi # encoding: [0x62,0xf4,0x14,0x0c,0x39,0xce]
2149+
; NDD-NEXT: ccmpel {dfv=} %ecx, %edi # encoding: [0x62,0xf4,0x04,0x04,0x39,0xcf]
21502150
; NDD-NEXT: cmovgl %esi, %edi, %eax # encoding: [0x62,0xf4,0x7c,0x18,0x4f,0xfe]
21512151
; NDD-NEXT: retq # encoding: [0xc3]
21522152
;
@@ -2176,17 +2176,17 @@ define i32 @ccmp_cmov_and_or_c(i32 %a, i32 %b, i32 %c, i32 %d) {
21762176
; CHECK-LABEL: ccmp_cmov_and_or_c:
21772177
; CHECK: # %bb.0: # %entry
21782178
; CHECK-NEXT: movl %edi, %eax # encoding: [0x89,0xf8]
2179-
; CHECK-NEXT: cmpl %edx, %edi # encoding: [0x39,0xd7]
2180-
; CHECK-NEXT: ccmpll {dfv=zf} %ecx, %esi # encoding: [0x62,0xf4,0x14,0x0c,0x39,0xce]
2181-
; CHECK-NEXT: ccmpel {dfv=} %ecx, %edi # encoding: [0x62,0xf4,0x04,0x04,0x39,0xcf]
2179+
; CHECK-NEXT: cmpl %ecx, %esi # encoding: [0x39,0xce]
2180+
; CHECK-NEXT: ccmpnel {dfv=} %edx, %edi # encoding: [0x62,0xf4,0x04,0x05,0x39,0xd7]
2181+
; CHECK-NEXT: ccmpgel {dfv=} %ecx, %edi # encoding: [0x62,0xf4,0x04,0x0d,0x39,0xcf]
21822182
; CHECK-NEXT: cmovgl %esi, %eax # encoding: [0x0f,0x4f,0xc6]
21832183
; CHECK-NEXT: retq # encoding: [0xc3]
21842184
;
21852185
; NDD-LABEL: ccmp_cmov_and_or_c:
21862186
; NDD: # %bb.0: # %entry
2187-
; NDD-NEXT: cmpl %edx, %edi # encoding: [0x39,0xd7]
2188-
; NDD-NEXT: ccmpll {dfv=zf} %ecx, %esi # encoding: [0x62,0xf4,0x14,0x0c,0x39,0xce]
2189-
; NDD-NEXT: ccmpel {dfv=} %ecx, %edi # encoding: [0x62,0xf4,0x04,0x04,0x39,0xcf]
2187+
; NDD-NEXT: cmpl %ecx, %esi # encoding: [0x39,0xce]
2188+
; NDD-NEXT: ccmpnel {dfv=} %edx, %edi # encoding: [0x62,0xf4,0x04,0x05,0x39,0xd7]
2189+
; NDD-NEXT: ccmpgel {dfv=} %ecx, %edi # encoding: [0x62,0xf4,0x04,0x0d,0x39,0xcf]
21902190
; NDD-NEXT: cmovgl %esi, %edi, %eax # encoding: [0x62,0xf4,0x7c,0x18,0x4f,0xfe]
21912191
; NDD-NEXT: retq # encoding: [0xc3]
21922192
;
@@ -2210,5 +2210,47 @@ entry:
22102210
ret i32 %sel
22112211
}
22122212

2213+
; (b != d) && ((a < c) || (a > d)): OR nested inside an AND. This shape needs
2214+
; the De Morgan negation bookkeeping in the CCMP conjunction emitter; a naive
2215+
; emitter miscompiles it (the inner OR wrongly forces the result true when the
2216+
; AND's left operand is false).
2217+
define i32 @ccmp_cmov_and_of_or(i32 %a, i32 %b, i32 %c, i32 %d) {
2218+
; CHECK-LABEL: ccmp_cmov_and_of_or:
2219+
; CHECK: # %bb.0: # %entry
2220+
; CHECK-NEXT: movl %edi, %eax # encoding: [0x89,0xf8]
2221+
; CHECK-NEXT: cmpl %ecx, %edi # encoding: [0x39,0xcf]
2222+
; CHECK-NEXT: ccmplel {dfv=sf} %edx, %edi # encoding: [0x62,0xf4,0x24,0x0e,0x39,0xd7]
2223+
; CHECK-NEXT: ccmpll {dfv=zf} %ecx, %esi # encoding: [0x62,0xf4,0x14,0x0c,0x39,0xce]
2224+
; CHECK-NEXT: cmovnel %esi, %eax # encoding: [0x0f,0x45,0xc6]
2225+
; CHECK-NEXT: retq # encoding: [0xc3]
2226+
;
2227+
; NDD-LABEL: ccmp_cmov_and_of_or:
2228+
; NDD: # %bb.0: # %entry
2229+
; NDD-NEXT: cmpl %ecx, %edi # encoding: [0x39,0xcf]
2230+
; NDD-NEXT: ccmplel {dfv=sf} %edx, %edi # encoding: [0x62,0xf4,0x24,0x0e,0x39,0xd7]
2231+
; NDD-NEXT: ccmpll {dfv=zf} %ecx, %esi # encoding: [0x62,0xf4,0x14,0x0c,0x39,0xce]
2232+
; NDD-NEXT: cmovnel %esi, %edi, %eax # encoding: [0x62,0xf4,0x7c,0x18,0x45,0xfe]
2233+
; NDD-NEXT: retq # encoding: [0xc3]
2234+
;
2235+
; ZU_COMMON-LABEL: ccmp_cmov_and_of_or:
2236+
; ZU_COMMON: # %bb.0: # %entry
2237+
; ZU_COMMON-NEXT: cmpl %ecx, %edi # encoding: [0x39,0xcf]
2238+
; ZU_COMMON-NEXT: movl %edi, %eax # encoding: [0x89,0xf8]
2239+
; ZU_COMMON-NEXT: cmovgl %esi, %eax # encoding: [0x0f,0x4f,0xc6]
2240+
; ZU_COMMON-NEXT: cmpl %edx, %edi # encoding: [0x39,0xd7]
2241+
; ZU_COMMON-NEXT: cmovll %esi, %eax # encoding: [0x0f,0x4c,0xc6]
2242+
; ZU_COMMON-NEXT: cmpl %ecx, %esi # encoding: [0x39,0xce]
2243+
; ZU_COMMON-NEXT: cmovel %edi, %eax # encoding: [0x0f,0x44,0xc7]
2244+
; ZU_COMMON-NEXT: retq # encoding: [0xc3]
2245+
entry:
2246+
%cmp1 = icmp ne i32 %b, %d
2247+
%cmp2 = icmp slt i32 %a, %c
2248+
%cmp3 = icmp sgt i32 %a, %d
2249+
%or = or i1 %cmp2, %cmp3
2250+
%and = and i1 %cmp1, %or
2251+
%sel = select i1 %and, i32 %b, i32 %a
2252+
ret i32 %sel
2253+
}
2254+
22132255
declare dso_local void @foo(...)
22142256
declare {i64, i1} @llvm.ssub.with.overflow.i64(i64, i64) nounwind readnone

0 commit comments

Comments
 (0)