diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -228,6 +228,58 @@ return false; } +// CanFoldToConstantUsingOneOperand returns a constant after folding "L Op R" +// after using only one of L or R, but not both. +// Formally, it returns a constant X s.t. "L Op Y --> X" or "Y Op R --> X" holds +// for any Y (E --> V stands for simplifying E to V). +// +// ex) Given "0 & R", X is 0. +// ex2) Given "L | -1", X is -1. +// ex3) Given "0 + 0", there is no such X. +// +// If L or R is undef, whenever undef in L is chosen to be a specific +// constant, it should be assumed that R's value has changed (and vice versa). +// +// ex4) Given "undef + 0", X cannot be 0: if L = undef is chosen to be 0, +// R cannot be 0 anymore. +// ex5) Given "undef + undef", X cannot be 0: If L = undef is chosen to be 0, +// R may have changed into 1, causing X to be 1. +// ex6) Given "undef & R", X is 0. If L = undef is chosen to be 0, the result +// is 0 regardless of R. +static Constant *FoldUsingOneOperand(Instruction::BinaryOps Op, Value *L, + Value *R) { + switch (Op) { + case Instruction::Mul: + case Instruction::And: + // ? * 0 = 0, ? & 0 = 0 + // ? * undef --> 0, ? & undef --> 0 + return match(L, m_CombineOr(m_Undef(), m_Zero())) || + match(R, m_CombineOr(m_Undef(), m_Zero())) + ? Constant::getNullValue(L->getType()) + : nullptr; + case Instruction::Or: + // ? | -1 = -1 + // ? | undef --> 0 + return match(L, m_CombineOr(m_Undef(), m_AllOnes())) || + match(R, m_CombineOr(m_Undef(), m_AllOnes())) + ? Constant::getAllOnesValue(L->getType()) + : nullptr; + case Instruction::Add: + case Instruction::Sub: + return nullptr; + case Instruction::Shl: + case Instruction::AShr: + case Instruction::LShr: + // If L is zero, we can always fold the result into zero. + // (If shiftamt >= LHS's bitwidth, the result is poison, but it can be + // folded into zero) + return match(L, m_Zero()) ? Constant::getNullValue(L->getType()) : nullptr; + default: + break; + } + return nullptr; +} + /// Simplify "A op (B op' C)" by distributing op over op', turning it into /// "(A op B) op' (A op C)". Here "op" is given by Opcode and "op'" is /// given by OpcodeToExpand, while "A" corresponds to LHS and "B op' C" to RHS. @@ -246,21 +298,38 @@ // It does! Try turning it into "(A op C) op' (B op C)". Value *A = Op0->getOperand(0), *B = Op0->getOperand(1), *C = RHS; // Do "A op C" and "B op C" both simplify? - if (Value *L = SimplifyBinOp(Opcode, A, C, Q, MaxRecurse)) + if (Value *L = SimplifyBinOp(Opcode, A, C, Q, MaxRecurse)) { if (Value *R = SimplifyBinOp(Opcode, B, C, Q, MaxRecurse)) { - // They do! Return "L op' R" if it simplifies or is already available. - // If "L op' R" equals "A op' B" then "L op' R" is just the LHS. - if ((L == A && R == B) || (Instruction::isCommutative(OpcodeToExpand) - && L == B && R == A)) { + // They do! + // If C contains undef in its subexpression, it should be folded + // into the same constant in L and R. + // Implementing this entanglement is non-trivial, however. So, a + // simpler approach is used instead: + // (1) If "L Op' R" can be folded into a constant by using only L + // or R, use it + // (2) If C is never undef or poison, it is okay + if (Constant *Res = FoldUsingOneOperand(OpcodeToExpand, L, R)) { ++NumExpand; - return LHS; - } - // Otherwise return "L op' R" if it simplifies. - if (Value *V = SimplifyBinOp(OpcodeToExpand, L, R, Q, MaxRecurse)) { - ++NumExpand; - return V; + return Res; + } else if (isGuaranteedNotToBeUndefOrPoison(C, Q.CxtI, Q.DT, + MaxRecurse)) { + // Return "L op' R" if it simplifies or is already + // available. If "L op' R" equals "A op' B" then "L op' R" is just + // the LHS. + if ((L == A && R == B) || + (Instruction::isCommutative(OpcodeToExpand) && L == B && + R == A)) { + ++NumExpand; + return LHS; + } + // Otherwise return "L op' R" if it simplifies. + if (Value *V = SimplifyBinOp(OpcodeToExpand, L, R, Q, MaxRecurse)) { + ++NumExpand; + return V; + } } } + } } // Check whether the expression has the form "A op (B op' C)". @@ -269,21 +338,31 @@ // It does! Try turning it into "(A op B) op' (A op C)". Value *A = LHS, *B = Op1->getOperand(0), *C = Op1->getOperand(1); // Do "A op B" and "A op C" both simplify? - if (Value *L = SimplifyBinOp(Opcode, A, B, Q, MaxRecurse)) + if (Value *L = SimplifyBinOp(Opcode, A, B, Q, MaxRecurse)) { if (Value *R = SimplifyBinOp(Opcode, A, C, Q, MaxRecurse)) { - // They do! Return "L op' R" if it simplifies or is already available. - // If "L op' R" equals "B op' C" then "L op' R" is just the RHS. - if ((L == B && R == C) || (Instruction::isCommutative(OpcodeToExpand) - && L == C && R == B)) { - ++NumExpand; - return RHS; - } - // Otherwise return "L op' R" if it simplifies. - if (Value *V = SimplifyBinOp(OpcodeToExpand, L, R, Q, MaxRecurse)) { + // They do! + if (Constant *Res = FoldUsingOneOperand(OpcodeToExpand, L, R)) { ++NumExpand; - return V; + return Res; + } else if (isGuaranteedNotToBeUndefOrPoison(A, Q.CxtI, Q.DT, + MaxRecurse)) { + // Return "L op' R" if it simplifies or is already + // available. If "L op' R" equals "B op' C" then "L op' R" is just + // the RHS. + if ((L == B && R == C) || + (Instruction::isCommutative(OpcodeToExpand) && L == C && + R == B)) { + ++NumExpand; + return RHS; + } + // Otherwise return "L op' R" if it simplifies. + if (Value *V = SimplifyBinOp(OpcodeToExpand, L, R, Q, MaxRecurse)) { + ++NumExpand; + return V; + } } } + } } return nullptr; diff --git a/llvm/test/Transforms/InstCombine/and-or-icmps.ll b/llvm/test/Transforms/InstCombine/and-or-icmps.ll --- a/llvm/test/Transforms/InstCombine/and-or-icmps.ll +++ b/llvm/test/Transforms/InstCombine/and-or-icmps.ll @@ -218,12 +218,14 @@ ; CHECK-NEXT: [[C7:%.*]] = icmp slt i16 [[L7]], 0 ; CHECK-NEXT: [[B15:%.*]] = xor i1 [[C7]], [[C10]] ; CHECK-NEXT: [[B19:%.*]] = xor i1 [[C11]], [[B15]] -; CHECK-NEXT: [[TMP2:%.*]] = and i1 [[C10]], [[C5]] -; CHECK-NEXT: [[C3:%.*]] = and i1 [[TMP2]], [[B19]] -; CHECK-NEXT: [[TMP3:%.*]] = xor i1 [[C10]], true -; CHECK-NEXT: [[C18:%.*]] = or i1 [[C7]], [[TMP3]] -; CHECK-NEXT: [[TMP4:%.*]] = sext i1 [[C3]] to i64 -; CHECK-NEXT: [[G26:%.*]] = getelementptr i1, i1* null, i64 [[TMP4]] +; CHECK-NEXT: [[TMP2:%.*]] = xor i1 [[C11]], true +; CHECK-NEXT: [[C6:%.*]] = or i1 [[B19]], [[TMP2]] +; CHECK-NEXT: [[TMP3:%.*]] = and i1 [[C10]], [[C5]] +; CHECK-NEXT: [[C3:%.*]] = and i1 [[TMP3]], [[C6]] +; CHECK-NEXT: [[TMP4:%.*]] = xor i1 [[C10]], true +; CHECK-NEXT: [[C18:%.*]] = or i1 [[C7]], [[TMP4]] +; CHECK-NEXT: [[TMP5:%.*]] = sext i1 [[C3]] to i64 +; CHECK-NEXT: [[G26:%.*]] = getelementptr i1, i1* null, i64 [[TMP5]] ; CHECK-NEXT: store i16 [[L7]], i16* undef, align 2 ; CHECK-NEXT: store i1 [[C18]], i1* undef, align 1 ; CHECK-NEXT: store i1* [[G26]], i1** undef, align 8