return TruncInst::CreateTruncOrBitCast(NewAShr, I.getType());
}
+/// This is a specialization of a more general transform from
+/// SimplifyUsingDistributiveLaws. If that code can be made to work optimally
+/// for multi-use cases or propagating nsw/nuw, then we would not need this.
+static Instruction *factorizeMathWithShlOps(BinaryOperator &I,
+ InstCombiner::BuilderTy &Builder) {
+ // TODO: Also handle mul by doubling the shift amount?
+ assert(I.getOpcode() == Instruction::Add ||
+ I.getOpcode() == Instruction::Sub && "Expected add/sub");
+ auto *Op0 = dyn_cast<BinaryOperator>(I.getOperand(0));
+ auto *Op1 = dyn_cast<BinaryOperator>(I.getOperand(1));
+ if (!Op0 || !Op1 || !(Op0->hasOneUse() || Op1->hasOneUse()))
+ return nullptr;
+
+ Value *X, *Y, *ShAmt;
+ if (!match(Op0, m_Shl(m_Value(X), m_Value(ShAmt))) ||
+ !match(Op1, m_Shl(m_Value(Y), m_Specific(ShAmt))))
+ return nullptr;
+
+ // No-wrap propagates only when all ops have no-wrap.
+ bool HasNSW = I.hasNoSignedWrap() && Op0->hasNoSignedWrap() &&
+ Op1->hasNoSignedWrap();
+ bool HasNUW = I.hasNoUnsignedWrap() && Op0->hasNoUnsignedWrap() &&
+ Op1->hasNoUnsignedWrap();
+
+ // add/sub (X << ShAmt), (Y << ShAmt) --> (add/sub X, Y) << ShAmt
+ Value *NewMath = Builder.CreateBinOp(I.getOpcode(), X, Y);
+ if (auto *NewI = dyn_cast<BinaryOperator>(NewMath)) {
+ NewI->setHasNoSignedWrap(HasNSW);
+ NewI->setHasNoUnsignedWrap(HasNUW);
+ }
+ auto *NewShl = BinaryOperator::CreateShl(NewMath, ShAmt);
+ NewShl->setHasNoSignedWrap(HasNSW);
+ NewShl->setHasNoUnsignedWrap(HasNUW);
+ return NewShl;
+}
+
Instruction *InstCombinerImpl::visitAdd(BinaryOperator &I) {
if (Value *V = SimplifyAddInst(I.getOperand(0), I.getOperand(1),
I.hasNoSignedWrap(), I.hasNoUnsignedWrap(),
if (Value *V = SimplifyUsingDistributiveLaws(I))
return replaceInstUsesWith(I, V);
+ if (Instruction *R = factorizeMathWithShlOps(I, Builder))
+ return R;
+
if (Instruction *X = foldAddWithConstant(I))
return X;
if (Value *V = SimplifyUsingDistributiveLaws(I))
return replaceInstUsesWith(I, V);
+ if (Instruction *R = factorizeMathWithShlOps(I, Builder))
+ return R;
+
if (I.getType()->isIntOrIntVectorTy(1))
return BinaryOperator::CreateXor(Op0, Op1);
define i6 @add_shl_same_amount(i6 %x, i6 %y, i6 %z) {
; CHECK-LABEL: @add_shl_same_amount(
-; CHECK-NEXT: [[XS:%.*]] = shl i6 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl i6 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = add i6 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = add i6 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret i6 [[DIFF]]
;
%xs = shl i6 %x, %z
define <2 x i4> @add_shl_same_amount_nsw(<2 x i4> %x, <2 x i4> %y, <2 x i4> %z) {
; CHECK-LABEL: @add_shl_same_amount_nsw(
-; CHECK-NEXT: [[XS:%.*]] = shl nsw <2 x i4> [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl nsw <2 x i4> [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = add nsw <2 x i4> [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = add nsw <2 x i4> [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl nsw <2 x i4> [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret <2 x i4> [[DIFF]]
;
%xs = shl nsw <2 x i4> %x, %z
define i64 @add_shl_same_amount_nuw(i64 %x, i64 %y, i64 %z) {
; CHECK-LABEL: @add_shl_same_amount_nuw(
-; CHECK-NEXT: [[XS:%.*]] = shl nuw i64 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl nuw i64 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = add nuw i64 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = add nuw i64 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl nuw i64 [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret i64 [[DIFF]]
;
%xs = shl nuw i64 %x, %z
; CHECK-LABEL: @add_shl_same_amount_nsw_extra_use1(
; CHECK-NEXT: [[XS:%.*]] = shl nuw nsw i8 [[X:%.*]], [[Z:%.*]]
; CHECK-NEXT: call void @use8(i8 [[XS]])
-; CHECK-NEXT: [[YS:%.*]] = shl nuw nsw i8 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = add nsw i8 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = add nsw i8 [[X]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl nsw i8 [[TMP1]], [[Z]]
; CHECK-NEXT: ret i8 [[DIFF]]
;
%xs = shl nsw nuw i8 %x, %z
define i8 @add_shl_same_amount_nuw_extra_use2(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @add_shl_same_amount_nuw_extra_use2(
-; CHECK-NEXT: [[XS:%.*]] = shl nuw i8 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl nuw nsw i8 [[Y:%.*]], [[Z]]
+; CHECK-NEXT: [[YS:%.*]] = shl nuw nsw i8 [[Y:%.*]], [[Z:%.*]]
; CHECK-NEXT: call void @use8(i8 [[YS]])
-; CHECK-NEXT: [[DIFF:%.*]] = add nuw nsw i8 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = add nuw i8 [[X:%.*]], [[Y]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl nuw i8 [[TMP1]], [[Z]]
; CHECK-NEXT: ret i8 [[DIFF]]
;
%xs = shl nuw i8 %x, %z
define i6 @add_shl_same_amount_partial_nsw1(i6 %x, i6 %y, i6 %z) {
; CHECK-LABEL: @add_shl_same_amount_partial_nsw1(
-; CHECK-NEXT: [[XS:%.*]] = shl nsw i6 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl nsw i6 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = add i6 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = add i6 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret i6 [[DIFF]]
;
%xs = shl nsw i6 %x, %z
define i6 @add_shl_same_amount_partial_nsw2(i6 %x, i6 %y, i6 %z) {
; CHECK-LABEL: @add_shl_same_amount_partial_nsw2(
-; CHECK-NEXT: [[XS:%.*]] = shl i6 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl nsw i6 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = add nsw i6 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = add i6 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret i6 [[DIFF]]
;
%xs = shl i6 %x, %z
define i6 @add_shl_same_amount_partial_nuw1(i6 %x, i6 %y, i6 %z) {
; CHECK-LABEL: @add_shl_same_amount_partial_nuw1(
-; CHECK-NEXT: [[XS:%.*]] = shl nuw i6 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl nuw i6 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = add i6 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = add i6 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret i6 [[DIFF]]
;
%xs = shl nuw i6 %x, %z
define i6 @add_shl_same_amount_partial_nuw2(i6 %x, i6 %y, i6 %z) {
; CHECK-LABEL: @add_shl_same_amount_partial_nuw2(
-; CHECK-NEXT: [[XS:%.*]] = shl nuw i6 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl i6 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = add nuw i6 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = add i6 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret i6 [[DIFF]]
;
%xs = shl nuw i6 %x, %z
define i6 @sub_shl_same_amount(i6 %x, i6 %y, i6 %z) {
; CHECK-LABEL: @sub_shl_same_amount(
-; CHECK-NEXT: [[XS:%.*]] = shl i6 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl i6 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = sub i6 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = sub i6 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret i6 [[DIFF]]
;
%xs = shl i6 %x, %z
define <2 x i4> @sub_shl_same_amount_nsw(<2 x i4> %x, <2 x i4> %y, <2 x i4> %z) {
; CHECK-LABEL: @sub_shl_same_amount_nsw(
-; CHECK-NEXT: [[XS:%.*]] = shl nsw <2 x i4> [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl nsw <2 x i4> [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = sub nsw <2 x i4> [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = sub nsw <2 x i4> [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl nsw <2 x i4> [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret <2 x i4> [[DIFF]]
;
%xs = shl nsw <2 x i4> %x, %z
define i64 @sub_shl_same_amount_nuw(i64 %x, i64 %y, i64 %z) {
; CHECK-LABEL: @sub_shl_same_amount_nuw(
-; CHECK-NEXT: [[XS:%.*]] = shl nuw i64 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl nuw i64 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = sub nuw i64 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = sub nuw i64 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl nuw i64 [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret i64 [[DIFF]]
;
%xs = shl nuw i64 %x, %z
; CHECK-LABEL: @sub_shl_same_amount_nsw_extra_use1(
; CHECK-NEXT: [[XS:%.*]] = shl nuw nsw i8 [[X:%.*]], [[Z:%.*]]
; CHECK-NEXT: call void @use8(i8 [[XS]])
-; CHECK-NEXT: [[YS:%.*]] = shl nuw nsw i8 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = sub nsw i8 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = sub nsw i8 [[X]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl nsw i8 [[TMP1]], [[Z]]
; CHECK-NEXT: ret i8 [[DIFF]]
;
%xs = shl nsw nuw i8 %x, %z
define i8 @sub_shl_same_amount_nuw_extra_use2(i8 %x, i8 %y, i8 %z) {
; CHECK-LABEL: @sub_shl_same_amount_nuw_extra_use2(
-; CHECK-NEXT: [[XS:%.*]] = shl nuw i8 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl nuw nsw i8 [[Y:%.*]], [[Z]]
+; CHECK-NEXT: [[YS:%.*]] = shl nuw nsw i8 [[Y:%.*]], [[Z:%.*]]
; CHECK-NEXT: call void @use8(i8 [[YS]])
-; CHECK-NEXT: [[DIFF:%.*]] = sub nuw nsw i8 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = sub nuw i8 [[X:%.*]], [[Y]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl nuw i8 [[TMP1]], [[Z]]
; CHECK-NEXT: ret i8 [[DIFF]]
;
%xs = shl nuw i8 %x, %z
define i6 @sub_shl_same_amount_partial_nsw1(i6 %x, i6 %y, i6 %z) {
; CHECK-LABEL: @sub_shl_same_amount_partial_nsw1(
-; CHECK-NEXT: [[XS:%.*]] = shl nsw i6 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl nsw i6 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = sub i6 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = sub i6 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret i6 [[DIFF]]
;
%xs = shl nsw i6 %x, %z
define i6 @sub_shl_same_amount_partial_nsw2(i6 %x, i6 %y, i6 %z) {
; CHECK-LABEL: @sub_shl_same_amount_partial_nsw2(
-; CHECK-NEXT: [[XS:%.*]] = shl i6 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl nsw i6 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = sub nsw i6 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = sub i6 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret i6 [[DIFF]]
;
%xs = shl i6 %x, %z
define i6 @sub_shl_same_amount_partial_nuw1(i6 %x, i6 %y, i6 %z) {
; CHECK-LABEL: @sub_shl_same_amount_partial_nuw1(
-; CHECK-NEXT: [[XS:%.*]] = shl nuw i6 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl nuw i6 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = sub i6 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = sub i6 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret i6 [[DIFF]]
;
%xs = shl nuw i6 %x, %z
define i6 @sub_shl_same_amount_partial_nuw2(i6 %x, i6 %y, i6 %z) {
; CHECK-LABEL: @sub_shl_same_amount_partial_nuw2(
-; CHECK-NEXT: [[XS:%.*]] = shl nuw i6 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT: [[YS:%.*]] = shl i6 [[Y:%.*]], [[Z]]
-; CHECK-NEXT: [[DIFF:%.*]] = sub nuw i6 [[XS]], [[YS]]
+; CHECK-NEXT: [[TMP1:%.*]] = sub i6 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[DIFF:%.*]] = shl i6 [[TMP1]], [[Z:%.*]]
; CHECK-NEXT: ret i6 [[DIFF]]
;
%xs = shl nuw i6 %x, %z