Index: lib/Transforms/InstCombine/InstCombineAndOrXor.cpp =================================================================== --- lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -2226,6 +2226,89 @@ } } + // ((V< ((V&C5)<>C3 == C2>>C4, for both logical shifts + { + auto MatchAndOfShift = [](Value *V, Value *&Source, + Instruction::BinaryOps &ShiftOpcode, + ConstantInt *&ShiftBy, ConstantInt *&PreShiftMask, + ConstantInt *&PostShiftMask, + BinaryOperator *&IntermediateInstr) -> bool { + if (!match(V, m_And(m_BinOp(IntermediateInstr), + m_ConstantInt(PostShiftMask))) || + !match(IntermediateInstr, + m_LogicalShift(m_Value(Source), m_ConstantInt(ShiftBy)))) + return false; + ShiftOpcode = IntermediateInstr->getOpcode(); + Instruction::BinaryOps InverseOpcode = + IntermediateInstr->getOpcode() == Instruction::Shl ? Instruction::LShr + : Instruction::Shl; + PreShiftMask = cast( + ConstantExpr::get(InverseOpcode, PostShiftMask, ShiftBy)); + return true; + }; + Value *Source0, *Source1; + Instruction::BinaryOps ShiftOpcode0, ShiftOpcode1; + ConstantInt *ShiftBy0, *ShiftBy1, *PreShiftMask0, *PreShiftMask1, + *PostShiftMask0, *PostShiftMask1; + BinaryOperator *IntermediateInstr0, *IntermediateInstr1; + if (MatchAndOfShift(Op0, Source0, ShiftOpcode0, ShiftBy0, PreShiftMask0, + PostShiftMask0, IntermediateInstr0) && + MatchAndOfShift(Op1, Source1, ShiftOpcode1, ShiftBy1, PreShiftMask1, + PostShiftMask1, IntermediateInstr1) && + Source0 == Source1) { + if (ShiftBy0 == ShiftBy1 && ShiftOpcode0 == ShiftOpcode1) { + // ((V< (V<hasOneUse() + Op1->hasOneUse() + + IntermediateInstr0->hasOneUse() + + IntermediateInstr1->hasOneUse(); + if (SavedInstructions >= 2) { + Value *Sh = Builder.CreateBinOp(ShiftOpcode0, Source0, ShiftBy0); + Constant *Mask = ConstantExpr::get(Instruction::Or, PostShiftMask0, + PostShiftMask1); + return BinaryOperator::CreateAnd(Sh, Mask); + } + } + Value *CommonOperand; + if (PreShiftMask0 == PreShiftMask1 && + match(Op0, m_BinOp(m_Value(CommonOperand), m_Value())) && + !match(Op1, m_BinOp(m_Specific(CommonOperand), m_Value())) && + (IntermediateInstr0->hasOneUse() || + IntermediateInstr1->hasOneUse())) { + Value *MaskedSource = Builder.CreateAnd(Source0, PreShiftMask0); + Value *NewOp0 = + Builder.CreateBinOp(ShiftOpcode0, MaskedSource, ShiftBy0); + Value *NewOp1 = + Builder.CreateBinOp(ShiftOpcode1, MaskedSource, ShiftBy1); + return BinaryOperator::CreateOr(NewOp0, NewOp1); + } + } + } + + // (or (or (op1 A C1)(op2 B C2)) (or (op1 D C1)(op2 E C2))) --> + // (or (or (op1 A C1)(op1 D C1)) (or (op2 B C2)(op2 E C2))) + { + BinaryOperator *BinOp00, *BinOp01, *BinOp10, *BinOp11; + ConstantInt *C00, *C01, *C10, *C11; + if (match(Op0, m_OneUse(m_Or(m_BinOp(BinOp00), m_BinOp(BinOp01)))) && + match(BinOp00, m_BinOp(m_Value(), m_ConstantInt(C00))) && + match(BinOp01, m_BinOp(m_Value(), m_ConstantInt(C01))) && + match(Op1, m_OneUse(m_Or(m_BinOp(BinOp10), m_BinOp(BinOp11)))) && + match(BinOp10, m_BinOp(m_Value(), m_ConstantInt(C10))) && + match(BinOp11, m_BinOp(m_Value(), m_ConstantInt(C11))) && + !(BinOp00->getOpcode() == BinOp01->getOpcode() && C00 == C01) && + !(BinOp10->getOpcode() == BinOp11->getOpcode() && C10 == C11)) { + if (BinOp00->getOpcode() == BinOp10->getOpcode() && C00 == C10 && + BinOp01->getOpcode() == BinOp11->getOpcode() && C01 == C11) + return BinaryOperator::CreateOr(Builder.CreateOr(BinOp00, BinOp10), + Builder.CreateOr(BinOp01, BinOp11)); + if (BinOp00->getOpcode() == BinOp11->getOpcode() && C00 == C11 && + BinOp01->getOpcode() == BinOp10->getOpcode() && C01 == C10) + return BinaryOperator::CreateOr(Builder.CreateOr(BinOp00, BinOp11), + Builder.CreateOr(BinOp01, BinOp10)); + } + } + return Changed ? &I : nullptr; } Index: test/Transforms/InstCombine/or-or-shift.ll =================================================================== --- test/Transforms/InstCombine/or-or-shift.ll +++ test/Transforms/InstCombine/or-or-shift.ll @@ -0,0 +1,80 @@ +; RUN: opt -S -instcombine < %s | FileCheck %s + +; (((A & 2) >> 1) | ((A & 2) << 6)) | (((A & 4) >> 1) | ((A & 4) << 6)) +; --> (((A & 2) >> 1) | ((A & 4) >> 1)) | (((A & 2) << 6) | ((A & 4) << 6)) +; --> (((A & 2) >> 1) | ((A & 4) >> 1)) | (((A & 2) | (A & 4)) << 6) +; --> (((A & 2) >> 1) | ((A & 4) >> 1)) | ((A & 6) << 6) +; --> (((A & 2) >> 1) | ((A & 4) >> 1)) | ((A << 6) & 384) +; --> (((A & 2) | (A & 4)) >> 1) | ((A << 6) & 384) +; --> ((A & 6) >> 1) | ((A << 6) & 384) +; --> ((A >> 1) & 3) | ((A << 6) & 384) +; --> ((A & 6) >> 1) | ((A & 6) << 6) + +define i32 @or_or_shift(i32 %x) local_unnamed_addr #0 { +; CHECK-LABEL: @or_or_shift( +; CHECK-NEXT: [[AND:%.*]] = and i32 %x, 6 +; CHECK-NEXT: [[SHL:%.*]] = shl nuw nsw i32 [[AND]], 6 +; CHECK-NEXT: [[SHR:%.*]] = lshr exact i32 [[AND]], 1 +; CHECK-NEXT: [[OR:%.*]] = or i32 [[SHL]], [[SHR]] +; CHECK-NEXT: ret i32 [[OR]] +; + %1 = and i32 %x, 2 + %2 = and i32 %x, 4 + %3 = shl nuw nsw i32 %1, 6 + %4 = lshr exact i32 %1, 1 + %5 = or i32 %3, %4 + %6 = shl nuw nsw i32 %2, 6 + %7 = lshr exact i32 %2, 1 + %8 = or i32 %6, %7 + %9 = or i32 %5, %8 + ret i32 %9 +} + +define i32 @or_and_shifts1(i32 %x) local_unnamed_addr #0 { +; CHECK-LABEL: @or_and_shifts1( +; CHECK-NEXT: [[AND:%.*]] = and i32 %x, 1 +; CHECK-NEXT: [[SHL1:%.*]] = shl nuw nsw i32 [[AND]], 3 +; CHECK-NEXT: [[SHL2:%.*]] = shl nuw nsw i32 [[AND]], 5 +; CHECK-NEXT: [[OR:%.*]] = or i32 [[SHL1]], [[SHL2]] +; CHECK-NEXT: ret i32 [[OR]] +; + %1 = shl i32 %x, 3 + %2 = and i32 %1, 15 + %3 = shl i32 %x, 5 + %4 = and i32 %3, 60 + %5 = or i32 %2, %4 + ret i32 %5 +} + +define i32 @or_and_shifts2(i32 %x) local_unnamed_addr #0 { +; CHECK-LABEL: @or_and_shifts2( +; CHECK-NEXT: [[AND:%.*]] = and i32 %x, 112 +; CHECK-NEXT: [[SHL:%.*]] = shl nuw nsw i32 [[AND]], 3 +; CHECK-NEXT: [[SHR:%.*]] = lshr exact i32 [[AND]], 4 +; CHECK-NEXT: [[OR:%.*]] = or i32 [[SHL]], [[SHR]] +; CHECK-NEXT: ret i32 [[OR]] +; + %1 = shl i32 %x, 3 + %2 = and i32 %1, 896 + %3 = lshr i32 %x, 4 + %4 = and i32 %3, 7 + %5 = or i32 %2, %4 + ret i32 %5 +} + +define i32 @or_and_shift_shift_and(i32 %x) local_unnamed_addr #0 { +; CHECK-LABEL: @or_and_shift_shift_and( +; CHECK-NEXT: [[AND:%.*]] = and i32 %x, 7 +; CHECK-NEXT: [[SHL1:%.*]] = shl nuw nsw i32 [[AND]], 3 +; CHECK-NEXT: [[SHL2:%.*]] = shl nuw nsw i32 [[AND]], 2 +; CHECK-NEXT: [[OR:%.*]] = or i32 [[SHL1]], [[SHL2]] +; CHECK-NEXT: ret i32 [[OR]] +; + %1 = and i32 %x, 7 + %2 = shl i32 %1, 3 + %3 = shl i32 %x, 2 + %4 = and i32 %3, 28 + %5 = or i32 %2, %4 + ret i32 %5 +} + Index: test/Transforms/InstCombine/or-xor.ll =================================================================== --- test/Transforms/InstCombine/or-xor.ll +++ test/Transforms/InstCombine/or-xor.ll @@ -414,3 +414,25 @@ %xor = xor i32 %or1, %or2 ret i32 %xor } + +; (((x ^ C1) | (x & C2)) | ((y ^ C1) | (y & C2))) -> +; (((x ^ C1) | (y ^ C1)) | ((x | y) & C2)) +define i32 @or_or_xor_and(i32 %x, i32 %y) local_unnamed_addr #0 { +; CHECK-LABEL: @or_or_xor_and( +; CHECK-NEXT: [[XOR1:%.*]] = xor i32 %x, 15 +; CHECK-NEXT: [[XOR2:%.*]] = xor i32 %y, 15 +; CHECK-NEXT: [[OR1:%.*]] = or i32 %x, %y +; CHECK-NEXT: [[AND:%.*]] = and i32 [[OR1]], 38 +; CHECK-NEXT: [[OR2:%.*]] = or i32 [[XOR1]], [[XOR2]] +; CHECK-NEXT: [[OR3:%.*]] = or i32 [[OR2]], [[AND]] +; CHECK-NEXT: ret i32 [[OR3]] +; + %1 = xor i32 %x, 15 + %2 = and i32 %x, 38 + %3 = or i32 %1, %2 + %4 = xor i32 %y, 15 + %5 = and i32 %y, 38 + %6 = or i32 %4, %5 + %7 = or i32 %3, %6 + ret i32 %7 +} \ No newline at end of file