[llvm] r305663 - [SCEV] Teach SCEVExpander to expand BinPow

Max Kazantsev via llvm-commits llvm-commits at lists.llvm.org
Sun Jun 18 23:24:53 PDT 2017


Author: mkazantsev
Date: Mon Jun 19 01:24:53 2017
New Revision: 305663

URL: http://llvm.org/viewvc/llvm-project?rev=305663&view=rev
Log:
[SCEV] Teach SCEVExpander to expand BinPow

Current implementation of SCEVExpander demonstrates a very naive behavior when
it deals with power calculation. For example, a SCEV for x^8 looks like

  (x * x * x * x * x * x * x * x)

If we try to expand it, it generates a very straightforward sequence of muls, like:

  x2 = mul x, x
  x3 = mul x2, x
  x4 = mul x3, x
      ...
  x8 = mul x7, x

This is a non-efficient way of doing that. A better way is to generate a sequence of
binary power calculation. In this case the expanded calculation will look like:

  x2 = mul x, x
  x4 = mul x2, x2
  x8 = mul x4, x4

In some cases the code size reduction for such SCEVs is dramatic. If we had a loop:

  x = a;
  for (int i = 0; i < 3; i++)
    x = x * x;

And this loop have been fully unrolled, we have something like:

  x = a;
  x2 = x * x;
  x4 = x2 * x2;
  x8 = x4 * x4;

The SCEV for x8 is the same as in example above, and if we for some reason
want to expand it, we will generate naively 7 multiplications instead of 3.
The BinPow expansion algorithm here allows to keep code size reasonable.

This patch teaches SCEV Expander to generate a sequence of BinPow multiplications
if we have repeating arguments in SCEVMulExpressions.

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

Added:
    llvm/trunk/test/Transforms/LoopStrengthReduce/X86/bin_power.ll
Modified:
    llvm/trunk/lib/Analysis/ScalarEvolutionExpander.cpp

Modified: llvm/trunk/lib/Analysis/ScalarEvolutionExpander.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/ScalarEvolutionExpander.cpp?rev=305663&r1=305662&r2=305663&view=diff
==============================================================================
--- llvm/trunk/lib/Analysis/ScalarEvolutionExpander.cpp (original)
+++ llvm/trunk/lib/Analysis/ScalarEvolutionExpander.cpp Mon Jun 19 01:24:53 2017
@@ -748,18 +748,56 @@ Value *SCEVExpander::visitMulExpr(const
   // Emit instructions to mul all the operands. Hoist as much as possible
   // out of loops.
   Value *Prod = nullptr;
-  for (const auto &I : OpsAndLoops) {
-    const SCEV *Op = I.second;
+  auto I = OpsAndLoops.begin();
+
+  // Expand the calculation of X pow N in the following manner:
+  // Let N = P1 + P2 + ... + PK, where all P are powers of 2. Then:
+  // X pow N = (X pow P1) * (X pow P2) * ... * (X pow PK).
+  const auto ExpandOpBinPowN = [this, &I, &OpsAndLoops, &Ty]() {
+    auto E = I;
+    // Calculate how many times the same operand from the same loop is included
+    // into this power.
+    uint64_t Exponent = 0;
+    const uint64_t MaxExponent = UINT64_MAX >> 1;
+    // No one sane will ever try to calculate such huge exponents, but if we
+    // need this, we stop on UINT64_MAX / 2 because we need to exit the loop
+    // below when the power of 2 exceeds our Exponent, and we want it to be
+    // 1u << 31 at most to not deal with unsigned overflow.
+    while (E != OpsAndLoops.end() && *I == *E && Exponent != MaxExponent) {
+      ++Exponent;
+      ++E;
+    }
+    assert(Exponent > 0 && "Trying to calculate a zeroth exponent of operand?");
+
+    // Calculate powers with exponents 1, 2, 4, 8 etc. and include those of them
+    // that are needed into the result.
+    Value *P = expandCodeFor(I->second, Ty);
+    Value *Result = nullptr;
+    if (Exponent & 1)
+      Result = P;
+    for (uint64_t BinExp = 2; BinExp <= Exponent; BinExp <<= 1) {
+      P = InsertBinop(Instruction::Mul, P, P);
+      if (Exponent & BinExp)
+        Result = Result ? InsertBinop(Instruction::Mul, Result, P) : P;
+    }
+
+    I = E;
+    assert(Result && "Nothing was expanded?");
+    return Result;
+  };
+
+  while (I != OpsAndLoops.end()) {
     if (!Prod) {
       // This is the first operand. Just expand it.
-      Prod = expand(Op);
-    } else if (Op->isAllOnesValue()) {
+      Prod = ExpandOpBinPowN();
+    } else if (I->second->isAllOnesValue()) {
       // Instead of doing a multiply by negative one, just do a negate.
       Prod = InsertNoopCastOfTo(Prod, Ty);
       Prod = InsertBinop(Instruction::Sub, Constant::getNullValue(Ty), Prod);
+      ++I;
     } else {
       // A simple mul.
-      Value *W = expandCodeFor(Op, Ty);
+      Value *W = ExpandOpBinPowN();
       Prod = InsertNoopCastOfTo(Prod, Ty);
       // Canonicalize a constant to the RHS.
       if (isa<Constant>(Prod)) std::swap(Prod, W);

Added: llvm/trunk/test/Transforms/LoopStrengthReduce/X86/bin_power.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/LoopStrengthReduce/X86/bin_power.ll?rev=305663&view=auto
==============================================================================
--- llvm/trunk/test/Transforms/LoopStrengthReduce/X86/bin_power.ll (added)
+++ llvm/trunk/test/Transforms/LoopStrengthReduce/X86/bin_power.ll Mon Jun 19 01:24:53 2017
@@ -0,0 +1,264 @@
+; RUN: opt < %s -loop-reduce -S | FileCheck %s
+
+target datalayout = "e-m:e-i32:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; Show that the b^2 is expanded correctly.
+define i32 @test_01(i32 %a) {
+; CHECK-LABEL: @test_01
+; CHECK:       entry:
+; CHECK-NEXT:  br label %loop
+; CHECK:       loop:
+; CHECK-NEXT:  [[IV:[^ ]+]] = phi i32 [ [[IV_INC:[^ ]+]], %loop ], [ 0, %entry ]
+; CHECK-NEXT:  [[IV_INC]] = add nsw i32 [[IV]], -1
+; CHECK-NEXT:  [[EXITCOND:[^ ]+]] = icmp eq i32 [[IV_INC]], -80
+; CHECK-NEXT:  br i1 [[EXITCOND]], label %exit, label %loop
+; CHECK:       exit:
+; CHECK-NEXT:  [[B:[^ ]+]] = add i32 %a, 1
+; CHECK-NEXT:  [[B2:[^ ]+]] = mul i32 [[B]], [[B]]
+; CHECK-NEXT:  [[R1:[^ ]+]] = add i32 [[B2]], -1
+; CHECK-NEXT:  [[R2:[^ ]+]] = sub i32 [[R1]], [[IV_INC]]
+; CHECK-NEXT:  ret i32 [[R2]]
+
+entry:
+  br label %loop
+
+loop:                                           ; preds = %loop, %entry
+  %indvars.iv = phi i32 [ 0, %entry ], [ %indvars.iv.next, %loop ]
+  %b = add i32 %a, 1
+  %b.pow.2 = mul i32 %b, %b
+  %result = add i32 %b.pow.2, %indvars.iv
+  %indvars.iv.next = add nuw nsw i32 %indvars.iv, 1
+  %exitcond = icmp eq i32 %indvars.iv.next, 80
+  br i1 %exitcond, label %exit, label %loop
+
+exit:                                             ; preds = %loop
+  ret i32 %result
+}
+
+; Show that b^8 is expanded correctly.
+define i32 @test_02(i32 %a) {
+; CHECK-LABEL: @test_02
+; CHECK:       entry:
+; CHECK-NEXT:  br label %loop
+; CHECK:       loop:
+; CHECK-NEXT:  [[IV:[^ ]+]] = phi i32 [ [[IV_INC:[^ ]+]], %loop ], [ 0, %entry ]
+; CHECK-NEXT:  [[IV_INC]] = add nsw i32 [[IV]], -1
+; CHECK-NEXT:  [[EXITCOND:[^ ]+]] = icmp eq i32 [[IV_INC]], -80
+; CHECK-NEXT:  br i1 [[EXITCOND]], label %exit, label %loop
+; CHECK:       exit:
+; CHECK-NEXT:  [[B:[^ ]+]] = add i32 %a, 1
+; CHECK-NEXT:  [[B2:[^ ]+]] = mul i32 [[B]], [[B]]
+; CHECK-NEXT:  [[B4:[^ ]+]] = mul i32 [[B2]], [[B2]]
+; CHECK-NEXT:  [[B8:[^ ]+]] = mul i32 [[B4]], [[B4]]
+; CHECK-NEXT:  [[R1:[^ ]+]] = add i32 [[B8]], -1
+; CHECK-NEXT:  [[R2:[^ ]+]] = sub i32 [[R1]], [[IV_INC]]
+; CHECK-NEXT:  ret i32 [[R2]]
+entry:
+  br label %loop
+
+loop:                                           ; preds = %loop, %entry
+  %indvars.iv = phi i32 [ 0, %entry ], [ %indvars.iv.next, %loop ]
+  %b = add i32 %a, 1
+  %b.pow.2 = mul i32 %b, %b
+  %b.pow.4 = mul i32 %b.pow.2, %b.pow.2
+  %b.pow.8 = mul i32 %b.pow.4, %b.pow.4
+  %result = add i32 %b.pow.8, %indvars.iv
+  %indvars.iv.next = add nuw nsw i32 %indvars.iv, 1
+  %exitcond = icmp eq i32 %indvars.iv.next, 80
+  br i1 %exitcond, label %exit, label %loop
+
+exit:                                             ; preds = %loop
+  ret i32 %result
+}
+
+; Show that b^27 (27 = 1 + 2 + 8 + 16) is expanded correctly.
+define i32 @test_03(i32 %a) {
+; CHECK-LABEL: @test_03
+; CHECK:       entry:
+; CHECK-NEXT:  br label %loop
+; CHECK:       loop:
+; CHECK-NEXT:  [[IV:[^ ]+]] = phi i32 [ [[IV_INC:[^ ]+]], %loop ], [ 0, %entry ]
+; CHECK-NEXT:  [[IV_INC]] = add nsw i32 [[IV]], -1
+; CHECK-NEXT:  [[EXITCOND:[^ ]+]] = icmp eq i32 [[IV_INC]], -80
+; CHECK-NEXT:  br i1 [[EXITCOND]], label %exit, label %loop
+; CHECK:       exit:
+; CHECK-NEXT:  [[B:[^ ]+]] = add i32 %a, 1
+; CHECK-NEXT:  [[B2:[^ ]+]] = mul i32 [[B]], [[B]]
+; CHECK-NEXT:  [[B3:[^ ]+]] = mul i32 [[B]], [[B2]]
+; CHECK-NEXT:  [[B4:[^ ]+]] = mul i32 [[B2]], [[B2]]
+; CHECK-NEXT:  [[B8:[^ ]+]] = mul i32 [[B4]], [[B4]]
+; CHECK-NEXT:  [[B11:[^ ]+]] = mul i32 [[B3]], [[B8]]
+; CHECK-NEXT:  [[B16:[^ ]+]] = mul i32 [[B8]], [[B8]]
+; CHECK-NEXT:  [[B27:[^ ]+]] = mul i32 [[B11]], [[B16]]
+; CHECK-NEXT:  [[R1:[^ ]+]] = add i32 [[B27]], -1
+; CHECK-NEXT:  [[R2:[^ ]+]] = sub i32 [[R1]], [[IV_INC]]
+; CHECK-NEXT:  ret i32 [[R2]]
+entry:
+  br label %loop
+
+loop:                                           ; preds = %loop, %entry
+  %indvars.iv = phi i32 [ 0, %entry ], [ %indvars.iv.next, %loop ]
+  %b = add i32 %a, 1
+  %b.pow.2 = mul i32 %b, %b
+  %b.pow.4 = mul i32 %b.pow.2, %b.pow.2
+  %b.pow.8 = mul i32 %b.pow.4, %b.pow.4
+  %b.pow.16 = mul i32 %b.pow.8, %b.pow.8
+  %b.pow.24 = mul i32 %b.pow.16, %b.pow.8
+  %b.pow.25 = mul i32 %b.pow.24, %b
+  %b.pow.26 = mul i32 %b.pow.25, %b
+  %b.pow.27 = mul i32 %b.pow.26, %b
+  %result = add i32 %b.pow.27, %indvars.iv
+  %indvars.iv.next = add nuw nsw i32 %indvars.iv, 1
+  %exitcond = icmp eq i32 %indvars.iv.next, 80
+  br i1 %exitcond, label %exit, label %loop
+
+exit:                                             ; preds = %loop
+  ret i32 %result
+}
+
+; Show how linear calculation of b^16 is turned into logarithmic.
+define i32 @test_04(i32 %a) {
+; CHECK-LABEL: @test_04
+; CHECK:       entry:
+; CHECK-NEXT:  br label %loop
+; CHECK:       loop:
+; CHECK-NEXT:  [[IV:[^ ]+]] = phi i32 [ [[IV_INC:[^ ]+]], %loop ], [ 0, %entry ]
+; CHECK-NEXT:  [[IV_INC]] = add nsw i32 [[IV]], -1
+; CHECK-NEXT:  [[EXITCOND:[^ ]+]] = icmp eq i32 [[IV_INC]], -80
+; CHECK-NEXT:  br i1 [[EXITCOND]], label %exit, label %loop
+; CHECK:       exit:
+; CHECK-NEXT:  [[B:[^ ]+]] = add i32 %a, 1
+; CHECK-NEXT:  [[B2:[^ ]+]] = mul i32 [[B]], [[B]]
+; CHECK-NEXT:  [[B4:[^ ]+]] = mul i32 [[B2]], [[B2]]
+; CHECK-NEXT:  [[B8:[^ ]+]] = mul i32 [[B4]], [[B4]]
+; CHECK-NEXT:  [[B16:[^ ]+]] = mul i32 [[B8]], [[B8]]
+; CHECK-NEXT:  [[R1:[^ ]+]] = add i32 [[B16]], -1
+; CHECK-NEXT:  [[R2:[^ ]+]] = sub i32 [[R1]], [[IV_INC]]
+; CHECK-NEXT:  ret i32 [[R2]]
+entry:
+  br label %loop
+
+loop:                                           ; preds = %loop, %entry
+  %indvars.iv = phi i32 [ 0, %entry ], [ %indvars.iv.next, %loop ]
+  %b = add i32 %a, 1
+  %b.pow.2 = mul i32 %b, %b
+  %b.pow.3 = mul i32 %b.pow.2, %b
+  %b.pow.4 = mul i32 %b.pow.3, %b
+  %b.pow.5 = mul i32 %b.pow.4, %b
+  %b.pow.6 = mul i32 %b.pow.5, %b
+  %b.pow.7 = mul i32 %b.pow.6, %b
+  %b.pow.8 = mul i32 %b.pow.7, %b
+  %b.pow.9 = mul i32 %b.pow.8, %b
+  %b.pow.10 = mul i32 %b.pow.9, %b
+  %b.pow.11 = mul i32 %b.pow.10, %b
+  %b.pow.12 = mul i32 %b.pow.11, %b
+  %b.pow.13 = mul i32 %b.pow.12, %b
+  %b.pow.14 = mul i32 %b.pow.13, %b
+  %b.pow.15 = mul i32 %b.pow.14, %b
+  %b.pow.16 = mul i32 %b.pow.15, %b
+  %result = add i32 %b.pow.16, %indvars.iv
+  %indvars.iv.next = add nuw nsw i32 %indvars.iv, 1
+  %exitcond = icmp eq i32 %indvars.iv.next, 80
+  br i1 %exitcond, label %exit, label %loop
+
+exit:                                             ; preds = %loop
+  ret i32 %result
+}
+
+; The output here is reasonably big, we just check that the amount of expanded
+; instructions is sane.
+define i32 @test_05(i32 %a) {
+; CHECK-LABEL: @test_05
+; CHECK:       entry:
+; CHECK-NEXT:  br label %loop
+; CHECK:       loop:
+; CHECK-NEXT:  [[IV:[^ ]+]] = phi i32 [ [[IV_INC:[^ ]+]], %loop ], [ 0, %entry ]
+; CHECK-NEXT:  [[IV_INC]] = add nsw i32 [[IV]], -1
+; CHECK-NEXT:  [[EXITCOND:[^ ]+]] = icmp eq i32 [[IV_INC]], -80
+; CHECK-NEXT:  br i1 [[EXITCOND]], label %exit, label %loop
+; CHECK:       exit:
+; CHECK:       %100
+; CHECK-NOT:   %150
+
+entry:
+  br label %loop
+
+loop:                                           ; preds = %loop, %entry
+  %indvars.iv = phi i32 [ 0, %entry ], [ %indvars.iv.next, %loop ]
+  %tmp3 = add i32 %a, 1
+  %tmp4 = mul i32 %tmp3, %tmp3
+  %tmp5 = mul i32 %tmp4, %tmp4
+  %tmp6 = mul i32 %tmp5, %tmp5
+  %tmp7 = mul i32 %tmp6, %tmp6
+  %tmp8 = mul i32 %tmp7, %tmp7
+  %tmp9 = mul i32 %tmp8, %tmp8
+  %tmp10 = mul i32 %tmp9, %tmp9
+  %tmp11 = mul i32 %tmp10, %tmp10
+  %tmp12 = mul i32 %tmp11, %tmp11
+  %tmp13 = mul i32 %tmp12, %tmp12
+  %tmp14 = mul i32 %tmp13, %tmp13
+  %tmp15 = mul i32 %tmp14, %tmp14
+  %tmp16 = mul i32 %tmp15, %tmp15
+  %tmp17 = mul i32 %tmp16, %tmp16
+  %tmp18 = mul i32 %tmp17, %tmp17
+  %tmp19 = mul i32 %tmp18, %tmp18
+  %tmp20 = mul i32 %tmp19, %tmp19
+  %tmp22 = add i32 %tmp20, %indvars.iv
+  %indvars.iv.next = add nuw nsw i32 %indvars.iv, 1
+  %exitcond = icmp eq i32 %indvars.iv.next, 80
+  br i1 %exitcond, label %exit, label %loop
+
+exit:                                             ; preds = %loop
+  ret i32 %tmp22
+}
+
+; Show that the transformation works even if the calculation involves different
+; values inside.
+define i32 @test_06(i32 %a, i32 %c) {
+; CHECK-LABEL: @test_06
+; CHECK:       entry:
+; CHECK-NEXT:  br label %loop
+; CHECK:       loop:
+; CHECK-NEXT:  [[IV:[^ ]+]] = phi i32 [ [[IV_INC:[^ ]+]], %loop ], [ 0, %entry ]
+; CHECK-NEXT:  [[IV_INC]] = add nsw i32 [[IV]], -1
+; CHECK-NEXT:  [[EXITCOND:[^ ]+]] = icmp eq i32 [[IV_INC]], -80
+; CHECK-NEXT:  br i1 [[EXITCOND]], label %exit, label %loop
+; CHECK:       exit:
+; CHECK:       [[B:[^ ]+]] = add i32 %a, 1
+; CHECK-NEXT:  [[B2:[^ ]+]] = mul i32 [[B]], [[B]]
+; CHECK-NEXT:  [[B4:[^ ]+]] = mul i32 [[B2]], [[B2]]
+; CHECK-NEXT:  [[B8:[^ ]+]] = mul i32 [[B4]], [[B4]]
+; CHECK-NEXT:  [[B16:[^ ]+]] = mul i32 [[B8]], [[B8]]
+entry:
+  br label %loop
+
+loop:                                           ; preds = %loop, %entry
+  %indvars.iv = phi i32 [ 0, %entry ], [ %indvars.iv.next, %loop ]
+  %b = add i32 %a, 1
+  %b.pow.2.tmp = mul i32 %b, %b
+  %b.pow.2 = mul i32 %b.pow.2.tmp, %c
+  %b.pow.3 = mul i32 %b.pow.2, %b
+  %b.pow.4 = mul i32 %b.pow.3, %b
+  %b.pow.5 = mul i32 %b.pow.4, %b
+  %b.pow.6.tmp = mul i32 %b.pow.5, %b
+  %b.pow.6 = mul i32 %b.pow.6.tmp, %c
+  %b.pow.7 = mul i32 %b.pow.6, %b
+  %b.pow.8 = mul i32 %b.pow.7, %b
+  %b.pow.9 = mul i32 %b.pow.8, %b
+  %b.pow.10 = mul i32 %b.pow.9, %b
+  %b.pow.11 = mul i32 %b.pow.10, %b
+  %b.pow.12.tmp = mul i32 %b.pow.11, %b
+  %b.pow.12 = mul i32 %c, %b.pow.12.tmp
+  %b.pow.13 = mul i32 %b.pow.12, %b
+  %b.pow.14 = mul i32 %b.pow.13, %b
+  %b.pow.15 = mul i32 %b.pow.14, %b
+  %b.pow.16 = mul i32 %b.pow.15, %b
+  %result = add i32 %b.pow.16, %indvars.iv
+  %indvars.iv.next = add nuw nsw i32 %indvars.iv, 1
+  %exitcond = icmp eq i32 %indvars.iv.next, 80
+  br i1 %exitcond, label %exit, label %loop
+
+exit:                                             ; preds = %loop
+  ret i32 %result
+}




More information about the llvm-commits mailing list