[llvm] r317510 - [InstCombine] Pull shifts through a select plus binop with constant

Topper, Craig via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 6 14:32:21 PST 2017


Thanks. I'll take a look.

-----Original Message-----
From: hwennborg at google.com [mailto:hwennborg at google.com] On Behalf Of Hans Wennborg
Sent: Monday, November 06, 2017 2:29 PM
To: Topper, Craig <craig.topper at intel.com>
Cc: llvm-commits <llvm-commits at lists.llvm.org>
Subject: Re: [llvm] r317510 - [InstCombine] Pull shifts through a select plus binop with constant

This broke the buildbots, e.g.
http://lab.llvm.org:8011/builders/clang-x86_64-debian-fast/builds/7386
It seems the CodeGen/Hexagon/loop-idiom/pmpy-mod.ll test hit some assert.

I've reverted in r317518

On Mon, Nov 6, 2017 at 1:07 PM, Craig Topper via llvm-commits
<llvm-commits at lists.llvm.org> wrote:
> Author: ctopper
> Date: Mon Nov  6 13:07:22 2017
> New Revision: 317510
>
> URL: http://llvm.org/viewvc/llvm-project?rev=317510&view=rev
> Log:
> [InstCombine] Pull shifts through a select plus binop with constant
>
> This pulls shifts through a select+binop with a constant where the select conditionally executes the binop. We already do this for just the binop, but not with the select.
>
> This can allow us to get the select closer to other selects to enable removing one.
>
> Differential Revision: https://reviews.llvm.org/D39222
>
> Modified:
>     llvm/trunk/lib/Transforms/InstCombine/InstCombineShifts.cpp
>     llvm/trunk/test/Transforms/InstCombine/shift.ll
>
> Modified: llvm/trunk/lib/Transforms/InstCombine/InstCombineShifts.cpp
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/InstCombine/InstCombineShifts.cpp?rev=317510&r1=317509&r2=317510&view=diff
> ==============================================================================
> --- llvm/trunk/lib/Transforms/InstCombine/InstCombineShifts.cpp (original)
> +++ llvm/trunk/lib/Transforms/InstCombine/InstCombineShifts.cpp Mon Nov  6 13:07:22 2017
> @@ -310,6 +310,40 @@ static Value *getShiftedValue(Value *V,
>    }
>  }
>
> +// If this is a bitwise operator or add with a constant RHS we might be able
> +// to pull it through a shift.
> +static bool canShiftBinOpWithConstantRHS(BinaryOperator &Shift,
> +                                         BinaryOperator *BO,
> +                                         const APInt &C) {
> +  bool IsValid = true;     // Valid only for And, Or Xor,
> +  bool HighBitSet = false; // Transform ifhigh bit of constant set?
> +
> +  switch (BO->getOpcode()) {
> +  default: IsValid = false; break;   // Do not perform transform!
> +  case Instruction::Add:
> +    IsValid = Shift.getOpcode() == Instruction::Shl;
> +    break;
> +  case Instruction::Or:
> +  case Instruction::Xor:
> +    HighBitSet = false;
> +    break;
> +  case Instruction::And:
> +    HighBitSet = true;
> +    break;
> +  }
> +
> +  // If this is a signed shift right, and the high bit is modified
> +  // by the logical operation, do not perform the transformation.
> +  // The HighBitSet boolean indicates the value of the high bit of
> +  // the constant which would cause it to be modified for this
> +  // operation.
> +  //
> +  if (IsValid && Shift.getOpcode() == Instruction::AShr)
> +    IsValid = C.isNegative() == HighBitSet;
> +
> +  return IsValid;
> +}
> +
>  Instruction *InstCombiner::FoldShiftByConstant(Value *Op0, Constant *Op1,
>                                                 BinaryOperator &I) {
>    bool isLeftShift = I.getOpcode() == Instruction::Shl;
> @@ -472,33 +506,7 @@ Instruction *InstCombiner::FoldShiftByCo
>        // shift is the only use, we can pull it out of the shift.
>        const APInt *Op0C;
>        if (match(Op0BO->getOperand(1), m_APInt(Op0C))) {
> -        bool isValid = true;     // Valid only for And, Or, Xor
> -        bool highBitSet = false; // Transform if high bit of constant set?
> -
> -        switch (Op0BO->getOpcode()) {
> -        default: isValid = false; break;   // Do not perform transform!
> -        case Instruction::Add:
> -          isValid = isLeftShift;
> -          break;
> -        case Instruction::Or:
> -        case Instruction::Xor:
> -          highBitSet = false;
> -          break;
> -        case Instruction::And:
> -          highBitSet = true;
> -          break;
> -        }
> -
> -        // If this is a signed shift right, and the high bit is modified
> -        // by the logical operation, do not perform the transformation.
> -        // The highBitSet boolean indicates the value of the high bit of
> -        // the constant which would cause it to be modified for this
> -        // operation.
> -        //
> -        if (isValid && I.getOpcode() == Instruction::AShr)
> -          isValid = Op0C->isNegative() == highBitSet;
> -
> -        if (isValid) {
> +        if (canShiftBinOpWithConstantRHS(I, Op0BO, *Op0C)) {
>            Constant *NewRHS = ConstantExpr::get(I.getOpcode(),
>                                       cast<Constant>(Op0BO->getOperand(1)), Op1);
>
> @@ -525,6 +533,53 @@ Instruction *InstCombiner::FoldShiftByCo
>          return BinaryOperator::CreateSub(NewRHS, NewShift);
>        }
>      }
> +
> +    // If we have a select that conditionally executes some binary operator,
> +    // see if we can pull it the select and operator through the shift.
> +    //
> +    // For example, turning:
> +    //   shl (select C, (add X, C1), X), C2
> +    // Into:
> +    //   Y = shl X, C2
> +    //   select C, (add Y, C1 << C2), Y
> +    Value *Cond;
> +    BinaryOperator *TBO;
> +    Value *FalseVal;
> +    if (match(Op0, m_Select(m_Value(Cond), m_OneUse(m_BinOp(TBO)),
> +                            m_Value(FalseVal)))) {
> +      const APInt *C;
> +      if (!isa<Constant>(FalseVal) && TBO->getOperand(0) == FalseVal &&
> +          match(TBO->getOperand(1), m_APInt(C)) &&
> +          canShiftBinOpWithConstantRHS(I, TBO, *C)) {
> +        Constant *NewRHS = ConstantExpr::get(I.getOpcode(),
> +                                       cast<Constant>(TBO->getOperand(1)), Op1);
> +
> +        Value *NewShift =
> +          Builder.CreateBinOp(I.getOpcode(), FalseVal, Op1);
> +        Value *NewOp = Builder.CreateBinOp(TBO->getOpcode(), NewShift,
> +                                           NewRHS);
> +        return SelectInst::Create(Cond, NewOp, NewShift);
> +      }
> +    }
> +
> +    BinaryOperator *FBO;
> +    Value *TrueVal;
> +    if (match(Op0, m_Select(m_Value(Cond), m_Value(TrueVal),
> +                            m_OneUse(m_BinOp(FBO))))) {
> +      const APInt *C;
> +      if (!isa<Constant>(TrueVal) && FBO->getOperand(0) == TrueVal &&
> +          match(FBO->getOperand(1), m_APInt(C)) &&
> +          canShiftBinOpWithConstantRHS(I, FBO, *C)) {
> +        Constant *NewRHS = ConstantExpr::get(I.getOpcode(),
> +                                       cast<Constant>(FBO->getOperand(1)), Op1);
> +
> +        Value *NewShift =
> +          Builder.CreateBinOp(I.getOpcode(), TrueVal, Op1);
> +        Value *NewOp = Builder.CreateBinOp(FBO->getOpcode(), NewShift,
> +                                           NewRHS);
> +        return SelectInst::Create(Cond, NewShift, NewOp);
> +      }
> +    }
>    }
>
>    return nullptr;
>
> Modified: llvm/trunk/test/Transforms/InstCombine/shift.ll
> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/InstCombine/shift.ll?rev=317510&r1=317509&r2=317510&view=diff
> ==============================================================================
> --- llvm/trunk/test/Transforms/InstCombine/shift.ll (original)
> +++ llvm/trunk/test/Transforms/InstCombine/shift.ll Mon Nov  6 13:07:22 2017
> @@ -1332,3 +1332,263 @@ define i7 @test65(i7 %a, i7 %b) {
>    %y = and i7 %x, 1 ; this extracts the lsb which should be 0 because we shifted an even number of bits and all even bits of the shift input are 0.
>    ret i7 %y
>  }
> +
> +define i32 @shl_select_add_true(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @shl_select_add_true(
> +; CHECK-NEXT:    [[TMP1:%.*]] = shl i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = add i32 [[TMP1]], 14
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP2]], i32 [[TMP1]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = add i32 %x, 7
> +  %2 = select i1 %cond, i32 %1, i32 %x
> +  %3 = shl i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @shl_select_add_false(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @shl_select_add_false(
> +; CHECK-NEXT:    [[TMP1:%.*]] = shl i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = add i32 [[TMP1]], 14
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP1]], i32 [[TMP2]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = add i32 %x, 7
> +  %2 = select i1 %cond, i32 %x, i32 %1
> +  %3 = shl i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @shl_select_and_true(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @shl_select_and_true(
> +; CHECK-NEXT:    [[TMP1:%.*]] = shl i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], 14
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP2]], i32 [[TMP1]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = and i32 %x, 7
> +  %2 = select i1 %cond, i32 %1, i32 %x
> +  %3 = shl i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @shl_select_and_false(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @shl_select_and_false(
> +; CHECK-NEXT:    [[TMP1:%.*]] = shl i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], 14
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP1]], i32 [[TMP2]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = and i32 %x, 7
> +  %2 = select i1 %cond, i32 %x, i32 %1
> +  %3 = shl i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @lshr_select_and_true(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @lshr_select_and_true(
> +; CHECK-NEXT:    [[TMP1:%.*]] = lshr i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], 3
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP2]], i32 [[TMP1]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = and i32 %x, 7
> +  %2 = select i1 %cond, i32 %1, i32 %x
> +  %3 = lshr i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @lshr_select_and_false(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @lshr_select_and_false(
> +; CHECK-NEXT:    [[TMP1:%.*]] = lshr i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], 3
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP1]], i32 [[TMP2]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = and i32 %x, 7
> +  %2 = select i1 %cond, i32 %x, i32 %1
> +  %3 = lshr i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @ashr_select_and_true(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @ashr_select_and_true(
> +; CHECK-NEXT:    [[TMP1:%.*]] = ashr i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], -1073741821
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP2]], i32 [[TMP1]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = and i32 %x, 2147483655
> +  %2 = select i1 %cond, i32 %1, i32 %x
> +  %3 = ashr i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @ashr_select_and_false(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @ashr_select_and_false(
> +; CHECK-NEXT:    [[TMP1:%.*]] = ashr i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], -1073741821
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP1]], i32 [[TMP2]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = and i32 %x, 2147483655
> +  %2 = select i1 %cond, i32 %x, i32 %1
> +  %3 = ashr i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @shl_select_or_true(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @shl_select_or_true(
> +; CHECK-NEXT:    [[TMP1:%.*]] = shl i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = or i32 [[TMP1]], 14
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP2]], i32 [[TMP1]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = or i32 %x, 7
> +  %2 = select i1 %cond, i32 %1, i32 %x
> +  %3 = shl i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @shl_select_or_false(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @shl_select_or_false(
> +; CHECK-NEXT:    [[TMP1:%.*]] = shl i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = or i32 [[TMP1]], 14
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP1]], i32 [[TMP2]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = or i32 %x, 7
> +  %2 = select i1 %cond, i32 %x, i32 %1
> +  %3 = shl i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @lshr_select_or_true(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @lshr_select_or_true(
> +; CHECK-NEXT:    [[TMP1:%.*]] = lshr i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = or i32 [[TMP1]], 3
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP2]], i32 [[TMP1]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = or i32 %x, 7
> +  %2 = select i1 %cond, i32 %1, i32 %x
> +  %3 = lshr i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @lshr_select_or_false(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @lshr_select_or_false(
> +; CHECK-NEXT:    [[TMP1:%.*]] = lshr i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = or i32 [[TMP1]], 3
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP1]], i32 [[TMP2]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = or i32 %x, 7
> +  %2 = select i1 %cond, i32 %x, i32 %1
> +  %3 = lshr i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @ashr_select_or_true(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @ashr_select_or_true(
> +; CHECK-NEXT:    [[TMP1:%.*]] = ashr i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = or i32 [[TMP1]], 3
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP2]], i32 [[TMP1]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = or i32 %x, 7
> +  %2 = select i1 %cond, i32 %1, i32 %x
> +  %3 = ashr i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @ashr_select_or_false(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @ashr_select_or_false(
> +; CHECK-NEXT:    [[TMP1:%.*]] = ashr i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = or i32 [[TMP1]], 3
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP1]], i32 [[TMP2]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = or i32 %x, 7
> +  %2 = select i1 %cond, i32 %x, i32 %1
> +  %3 = ashr i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @shl_select_xor_true(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @shl_select_xor_true(
> +; CHECK-NEXT:    [[TMP1:%.*]] = shl i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = xor i32 [[TMP1]], 14
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP2]], i32 [[TMP1]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = xor i32 %x, 7
> +  %2 = select i1 %cond, i32 %1, i32 %x
> +  %3 = shl i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @shl_select_xor_false(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @shl_select_xor_false(
> +; CHECK-NEXT:    [[TMP1:%.*]] = shl i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = xor i32 [[TMP1]], 14
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP1]], i32 [[TMP2]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = xor i32 %x, 7
> +  %2 = select i1 %cond, i32 %x, i32 %1
> +  %3 = shl i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @lshr_select_xor_true(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @lshr_select_xor_true(
> +; CHECK-NEXT:    [[TMP1:%.*]] = lshr i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = xor i32 [[TMP1]], 3
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP2]], i32 [[TMP1]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = xor i32 %x, 7
> +  %2 = select i1 %cond, i32 %1, i32 %x
> +  %3 = lshr i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @lshr_select_xor_false(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @lshr_select_xor_false(
> +; CHECK-NEXT:    [[TMP1:%.*]] = lshr i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = xor i32 [[TMP1]], 3
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP1]], i32 [[TMP2]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = xor i32 %x, 7
> +  %2 = select i1 %cond, i32 %x, i32 %1
> +  %3 = lshr i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @ashr_select_xor_true(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @ashr_select_xor_true(
> +; CHECK-NEXT:    [[TMP1:%.*]] = ashr i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = xor i32 [[TMP1]], 3
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP2]], i32 [[TMP1]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = xor i32 %x, 7
> +  %2 = select i1 %cond, i32 %1, i32 %x
> +  %3 = ashr i32 %2, 1
> +  ret i32 %3
> +}
> +
> +define i32 @ashr_select_xor_false(i32 %x, i1 %cond) {
> +; CHECK-LABEL: @ashr_select_xor_false(
> +; CHECK-NEXT:    [[TMP1:%.*]] = ashr i32 [[X:%.*]], 1
> +; CHECK-NEXT:    [[TMP2:%.*]] = xor i32 [[TMP1]], 3
> +; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[COND:%.*]], i32 [[TMP1]], i32 [[TMP2]]
> +; CHECK-NEXT:    ret i32 [[TMP3]]
> +;
> +  %1 = xor i32 %x, 7
> +  %2 = select i1 %cond, i32 %x, i32 %1
> +  %3 = ashr i32 %2, 1
> +  ret i32 %3
> +}
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits


More information about the llvm-commits mailing list