[llvm] 2e87333 - [InstCombine] convert mul by negative-pow2 to negate and shift

Sanjay Patel via llvm-commits llvm-commits at lists.llvm.org
Sun Oct 2 10:16:55 PDT 2022


Author: Sanjay Patel
Date: 2022-10-02T12:22:25-04:00
New Revision: 2e87333bfe7e226533fb66c84015c4523e352d3f

URL: https://github.com/llvm/llvm-project/commit/2e87333bfe7e226533fb66c84015c4523e352d3f
DIFF: https://github.com/llvm/llvm-project/commit/2e87333bfe7e226533fb66c84015c4523e352d3f.diff

LOG: [InstCombine] convert mul by negative-pow2 to negate and shift

This is an unusual canonicalization because we create an extra instruction,
but it's likely better for analysis and codegen (similar reasoning as D133399).

InstCombine::Negator may create this kind of multiply from negate and shift,
but this should not conflict because of the narrow negation.

I don't know how to create a fully general proof for this kind of transform in
Alive2, but here's an example with bitwidths similar to one of the regression
tests:
https://alive2.llvm.org/ce/z/J3jTjR

Differential Revision: https://reviews.llvm.org/D133667

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
    llvm/test/Transforms/InstCombine/add.ll
    llvm/test/Transforms/InstCombine/mul.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
index dc37f23b78244..ab1cfe99e7063 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp
@@ -158,7 +158,8 @@ Instruction *InstCombinerImpl::visitMul(BinaryOperator &I) {
     return replaceInstUsesWith(I, V);
 
   Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1);
-  unsigned BitWidth = I.getType()->getScalarSizeInBits();
+  Type *Ty = I.getType();
+  unsigned BitWidth = Ty->getScalarSizeInBits();
 
   // X * -1 == 0 - X
   if (match(Op1, m_AllOnes())) {
@@ -212,6 +213,25 @@ Instruction *InstCombinerImpl::visitMul(BinaryOperator &I) {
     if (Value *NegOp0 = Negator::Negate(/*IsNegation*/ true, Op0, *this))
       return BinaryOperator::CreateMul(
           NegOp0, ConstantExpr::getNeg(cast<Constant>(Op1)), I.getName());
+
+    // Try to convert multiply of extended operand to narrow negate and shift
+    // for better analysis.
+    // This is valid if the shift amount (trailing zeros in the multiplier
+    // constant) clears more high bits than the bitwidth 
diff erence between
+    // source and destination types:
+    // ({z/s}ext X) * (-1<<C) --> (zext (-X)) << C
+    const APInt *NegPow2C;
+    Value *X;
+    if (match(Op0, m_ZExtOrSExt(m_Value(X))) &&
+        match(Op1, m_APIntAllowUndef(NegPow2C))) {
+      unsigned SrcWidth = X->getType()->getScalarSizeInBits();
+      unsigned ShiftAmt = NegPow2C->countTrailingZeros();
+      if (ShiftAmt >= BitWidth - SrcWidth) {
+        Value *N = Builder.CreateNeg(X, X->getName() + ".neg");
+        Value *Z = Builder.CreateZExt(N, Ty, N->getName() + ".z");
+        return BinaryOperator::CreateShl(Z, ConstantInt::get(Ty, ShiftAmt));
+      }
+    }
   }
 
   if (Instruction *FoldedMul = foldBinOpIntoSelectOrPhi(I))
@@ -320,7 +340,6 @@ Instruction *InstCombinerImpl::visitMul(BinaryOperator &I) {
   //   2) X * Y --> X & Y, iff X, Y can be only {0,1}.
   // Note: We could use known bits to generalize this and related patterns with
   // shifts/truncs
-  Type *Ty = I.getType();
   if (Ty->isIntOrIntVectorTy(1) ||
       (match(Op0, m_And(m_Value(), m_One())) &&
        match(Op1, m_And(m_Value(), m_One()))))

diff  --git a/llvm/test/Transforms/InstCombine/add.ll b/llvm/test/Transforms/InstCombine/add.ll
index c8bb8cc3ab365..eb855f515340d 100644
--- a/llvm/test/Transforms/InstCombine/add.ll
+++ b/llvm/test/Transforms/InstCombine/add.ll
@@ -2254,18 +2254,16 @@ define { i64, i64 } @PR57576(i64 noundef %x, i64 noundef %y, i64 noundef %z, i64
 ; CHECK-LABEL: @PR57576(
 ; CHECK-NEXT:    [[ZX:%.*]] = zext i64 [[X:%.*]] to i128
 ; CHECK-NEXT:    [[ZY:%.*]] = zext i64 [[Y:%.*]] to i128
-; CHECK-NEXT:    [[ZW:%.*]] = zext i64 [[W:%.*]] to i128
 ; CHECK-NEXT:    [[ZZ:%.*]] = zext i64 [[Z:%.*]] to i128
 ; CHECK-NEXT:    [[SHY:%.*]] = shl nuw i128 [[ZY]], 64
 ; CHECK-NEXT:    [[XY:%.*]] = or i128 [[SHY]], [[ZX]]
-; CHECK-NEXT:    [[TMP1:%.*]] = shl nuw i128 [[ZW]], 64
-; CHECK-NEXT:    [[TMP2:%.*]] = or i128 [[TMP1]], [[ZZ]]
-; CHECK-NEXT:    [[ADD:%.*]] = sub i128 [[XY]], [[TMP2]]
-; CHECK-NEXT:    [[T:%.*]] = trunc i128 [[ADD]] to i64
-; CHECK-NEXT:    [[H:%.*]] = lshr i128 [[ADD]], 64
-; CHECK-NEXT:    [[T2:%.*]] = trunc i128 [[H]] to i64
+; CHECK-NEXT:    [[SUB:%.*]] = sub i128 [[XY]], [[ZZ]]
+; CHECK-NEXT:    [[T:%.*]] = trunc i128 [[SUB]] to i64
+; CHECK-NEXT:    [[TMP1:%.*]] = lshr i128 [[SUB]], 64
+; CHECK-NEXT:    [[DOTTR:%.*]] = trunc i128 [[TMP1]] to i64
+; CHECK-NEXT:    [[DOTNARROW:%.*]] = sub i64 [[DOTTR]], [[W:%.*]]
 ; CHECK-NEXT:    [[R1:%.*]] = insertvalue { i64, i64 } poison, i64 [[T]], 0
-; CHECK-NEXT:    [[R2:%.*]] = insertvalue { i64, i64 } [[R1]], i64 [[T2]], 1
+; CHECK-NEXT:    [[R2:%.*]] = insertvalue { i64, i64 } [[R1]], i64 [[DOTNARROW]], 1
 ; CHECK-NEXT:    ret { i64, i64 } [[R2]]
 ;
   %zx = zext i64 %x to i128

diff  --git a/llvm/test/Transforms/InstCombine/mul.ll b/llvm/test/Transforms/InstCombine/mul.ll
index c074ff84182e0..a86d94a0efc1d 100644
--- a/llvm/test/Transforms/InstCombine/mul.ll
+++ b/llvm/test/Transforms/InstCombine/mul.ll
@@ -1484,8 +1484,9 @@ define i32 @mulnot_extrause(i32 %a0) {
 
 define i32 @zext_negpow2(i8 %x) {
 ; CHECK-LABEL: @zext_negpow2(
-; CHECK-NEXT:    [[ZX:%.*]] = zext i8 [[X:%.*]] to i32
-; CHECK-NEXT:    [[R:%.*]] = mul i32 [[ZX]], -16777216
+; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]
+; CHECK-NEXT:    [[X_NEG_Z:%.*]] = zext i8 [[X_NEG]] to i32
+; CHECK-NEXT:    [[R:%.*]] = shl nuw i32 [[X_NEG_Z]], 24
 ; CHECK-NEXT:    ret i32 [[R]]
 ;
   %zx = zext i8 %x to i32
@@ -1493,10 +1494,13 @@ define i32 @zext_negpow2(i8 %x) {
   ret i32 %r
 }
 
+; splat constant
+
 define <2 x i14> @zext_negpow2_vec(<2 x i5> %x) {
 ; CHECK-LABEL: @zext_negpow2_vec(
-; CHECK-NEXT:    [[ZX:%.*]] = zext <2 x i5> [[X:%.*]] to <2 x i14>
-; CHECK-NEXT:    [[R:%.*]] = mul <2 x i14> [[ZX]], <i14 -2048, i14 -2048>
+; CHECK-NEXT:    [[X_NEG:%.*]] = sub <2 x i5> zeroinitializer, [[X:%.*]]
+; CHECK-NEXT:    [[X_NEG_Z:%.*]] = zext <2 x i5> [[X_NEG]] to <2 x i14>
+; CHECK-NEXT:    [[R:%.*]] = shl <2 x i14> [[X_NEG_Z]], <i14 11, i14 11>
 ; CHECK-NEXT:    ret <2 x i14> [[R]]
 ;
   %zx = zext <2 x i5> %x to <2 x i14>
@@ -1504,6 +1508,8 @@ define <2 x i14> @zext_negpow2_vec(<2 x i5> %x) {
   ret <2 x i14> %r
 }
 
+; negative test - mul must be big enough to cover bitwidth 
diff 
+
 define i32 @zext_negpow2_too_small(i8 %x) {
 ; CHECK-LABEL: @zext_negpow2_too_small(
 ; CHECK-NEXT:    [[ZX:%.*]] = zext i8 [[X:%.*]] to i32
@@ -1517,8 +1523,9 @@ define i32 @zext_negpow2_too_small(i8 %x) {
 
 define i16 @sext_negpow2(i9 %x) {
 ; CHECK-LABEL: @sext_negpow2(
-; CHECK-NEXT:    [[SX:%.*]] = sext i9 [[X:%.*]] to i16
-; CHECK-NEXT:    [[R:%.*]] = mul i16 [[SX]], -1024
+; CHECK-NEXT:    [[X_NEG:%.*]] = sub i9 0, [[X:%.*]]
+; CHECK-NEXT:    [[X_NEG_Z:%.*]] = zext i9 [[X_NEG]] to i16
+; CHECK-NEXT:    [[R:%.*]] = shl i16 [[X_NEG_Z]], 10
 ; CHECK-NEXT:    ret i16 [[R]]
 ;
   %sx = sext i9 %x to i16
@@ -1526,10 +1533,13 @@ define i16 @sext_negpow2(i9 %x) {
   ret i16 %r
 }
 
+; splat constant with poison element(s)
+
 define <2 x i16> @sext_negpow2_vec(<2 x i8> %x) {
 ; CHECK-LABEL: @sext_negpow2_vec(
-; CHECK-NEXT:    [[SX:%.*]] = sext <2 x i8> [[X:%.*]] to <2 x i16>
-; CHECK-NEXT:    [[R:%.*]] = mul <2 x i16> [[SX]], <i16 -256, i16 poison>
+; CHECK-NEXT:    [[X_NEG:%.*]] = sub <2 x i8> zeroinitializer, [[X:%.*]]
+; CHECK-NEXT:    [[X_NEG_Z:%.*]] = zext <2 x i8> [[X_NEG]] to <2 x i16>
+; CHECK-NEXT:    [[R:%.*]] = shl nuw <2 x i16> [[X_NEG_Z]], <i16 8, i16 8>
 ; CHECK-NEXT:    ret <2 x i16> [[R]]
 ;
   %sx = sext <2 x i8> %x to <2 x i16>
@@ -1537,6 +1547,8 @@ define <2 x i16> @sext_negpow2_vec(<2 x i8> %x) {
   ret <2 x i16> %r
 }
 
+; negative test - mul must be big enough to cover bitwidth 
diff 
+
 define <2 x i16> @sext_negpow2_too_small_vec(<2 x i8> %x) {
 ; CHECK-LABEL: @sext_negpow2_too_small_vec(
 ; CHECK-NEXT:    [[SX:%.*]] = sext <2 x i8> [[X:%.*]] to <2 x i16>
@@ -1548,6 +1560,8 @@ define <2 x i16> @sext_negpow2_too_small_vec(<2 x i8> %x) {
   ret <2 x i16> %r
 }
 
+; negative test - too many uses
+
 define i32 @zext_negpow2_use(i8 %x) {
 ; CHECK-LABEL: @zext_negpow2_use(
 ; CHECK-NEXT:    [[ZX:%.*]] = zext i8 [[X:%.*]] to i32


        


More information about the llvm-commits mailing list